Writing a native library in F# which can be called from C++

Recently I was asked if it’s possible to call a library written in C# from a native C++ library on Windows. I remembered that I’ve read something about native libraries in C# and CoreRT. I’ve used CoreRT a lot and already blogged about it: Compile a .NET Core app to a single native binary

If an executable binary is possible in C#, a library should be possible too, right? As it turns out it is! I’ve followed the tutorial Writing Native Libraries in C# and using them in other languages and it worked on the first try.

As I am a huge fan of the clean and simple syntax and structure of the F# programming language, the logical next step was to port the C# library to F#, which can be called by C++ (or any other language that can call standard C ABI).

Create a native F# library

First, create a new F# library project.

mkdir src
dotnet new classlib -lang=F# -o .\src\NativeLib

Now add a nuget.config file to the project, as we need an additional NuGet source for the native compiler. Add the following content to the nuget.config file.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <!--To inherit the global NuGet package sources remove the <clear/> line below -->
    <clear />
    <add key="dotnet-core" value="https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json" />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
  </packageSources>
</configuration>

To be able to export a function with an interface callable from C++, we need to annotate the function with an attribute. Create the file NativeCallable.fs with the following content.

namespace System.Runtime.InteropServices
    open System
    open System.Runtime.InteropServices

    [<AttributeUsage(AttributeTargets.Method)>]
    type NativeCallableAttribute() 
        = inherit System.Attribute()
            [<DefaultValue>] val mutable public EntryPoint : string
            [<DefaultValue>] val mutable public CallingConvention : CallingConvention

The next step is the actual library with the functions we want to export. Create a file NativeLibrary.fs with the functions we want to export, annotated with the custom attribute we just created.

module NativeLibrary
open System.Runtime.InteropServices
open System
    
[<NativeCallable(EntryPoint = "add", CallingConvention = CallingConvention.StdCall)>]
let Add (a: int) (b: int) : int =
    a + b

[<NativeCallable(EntryPoint = "write_line", CallingConvention = CallingConvention.StdCall)>]
let WriteLine (pString: IntPtr) : int =
    try
        let str = Marshal.PtrToStringAnsi(pString)
        Console.WriteLine(str)
        0
    with
    | _ -> -1

This exports two functions. The first add will add two integers and the second one write_line writes a line to stdout.

To be able to compile to a native library we need to modify the NativeLib.fsproj file to contain a reference to the CoreRT compiler and we add a few optimizations to reduce the size of the binary. To learn more about optimizations see: Optimizing CoreRT

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <Platforms>AnyCPU;x64</Platforms>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="NativeCallable.fs" />
    <Compile Include="NativeLibrary.fs" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="1.0.0-alpha-*" />
  </ItemGroup>

  <PropertyGroup>
    <IlcInvariantGlobalization>true</IlcInvariantGlobalization>
    <RootAllApplicationAssemblies>false</RootAllApplicationAssemblies>
    <IlcGenerateCompleteTypeMetadata>false</IlcGenerateCompleteTypeMetadata>
    <IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
    <IlcOptimizationPreference>Size</IlcOptimizationPreference>
    <IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>
  </PropertyGroup>

</Project>

Our library is now complete, so its time to compile it to a native Windows DLL.

dotnet publish /p:NativeLib=Shared -r win-x64 -c Release

This creates a DLL and a Lib in the folder $(SolutionDir)\src\NativeLib\bin\Release\netstandard2.0\win-x64\native.

You can check if everything worked by having a look at the exports of the DLL on penet.io. Just upload the created DLL and click on Exports.

We can clearly see our two functions are in the export table of the DLL.

Create a native C++ app and use our F# DLL

The F# part was the easy one. As usually C++ is a big pain, as we need to configure the linker to use our library.

First, we create a new empty C++ console application. Then we create a header file called NativeLib.h with the declarations for our functions.

#pragma once
extern "C" int __stdcall add(int a, int b);
extern "C" void __stdcall write_line(const char* pString);

After that, we create our Main.cpp file with the following content:

#include <iostream>
#include "NativeLib.h"
using namespace std;

int main()
{
    int result = add(1, 2);
    cout << result << endl;
    write_line("Hello World!");

    return 0;
}

As we built our F# library for x64, switch the configuration for the build to x64 in the Configuration Manager.

Next, we need to edit the properties of the project. Open the settings and change the following values. Make sure to select All Configurations to change the values for Debug and Release.

  • General -> Output Directory = $(ProjectDir)bin\$(Platform)\$(Configuration)\
  • Linker -> General -> Additional Library Directories = $(SolutionDir)src\NativeLib\bin\Release\netstandard2.0\win-x64\native;%(AdditionalLibraryDirectories)
  • Linker -> Input -> Additional Dependencies = NativeLib.lib;%(AdditionalDependencies)
  • Build Events -> Post-Build Events = xcopy /y /d “$(SolutionDir)src\NativeLib\bin\Release\netstandard2.0\win-x64\native\NativeLib.dll” “$(OutDir)”

The build event guarantees that our previously built F# library will be copied to the output folder of the C++ console application.

Now build and run the C++ console application and the following output appears:

3
Hello World!

You can find the complete code on Github: Native F# Library.

2 thoughts on “Writing a native library in F# which can be called from C++

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s