While trying to prove to a colleague that it's possible to use C++ classes from F#, I came up with the following proof of concept. The first snippet is the code he provided for the challenge, and the code snippet below is my implementation in F#.
namespace testapp {
struct trivial_foo {
int bar;
__declspec(dllexport) void set(int n) { bar = n; }
__declspec(dllexport) int get() { return bar; }
}
}
open System.Runtime.InteropServices
type TrivialFoo =
struct
val bar: int
new(_bar: int) = { bar = _bar }
end
[<DllImport("Win32Project2.dll", EntryPoint="?get@trivial_foo@testapp@@QAEHXZ", CallingConvention = CallingConvention.ThisCall)>]
extern int trivial_foo_get(TrivialFoo& trivial_foo)
[<DllImport("Win32Project2.dll", EntryPoint="?set@trivial_foo@testapp@@QAEXH@Z", CallingConvention = CallingConvention.ThisCall)>]
extern void trivial_foo_set(TrivialFoo& trivial_foo, int bar)
type TrivialFoo with
member this.Get() = trivial_foo_get(&this)
member this.Set(bar) = trivial_foo_set(&this, bar)
When debugged in Visual Studio or run as a standalone program, this works predictably: TrivialFoo.Get
returns the value of bar
and TrivialFoo.Set
assigns to it. When run from F# Interactive however, TrivialFoo.Set
will not set the field. I suspect it might have something to do with accessing managed memory from unmanaged code, but that doesn't explain why it only happens when using F# Interactive. Does anyone know what's going on here?
I don't think this proof of concept is a good proof of interoperability. You may be better off creating DLL export definitions from your C++ project and use the de-decorated names instead.
As a PoC: F# creates MSIL that fits in the CLI, so it can interoperate with any other CLI language out there. If that is not enough and you want native-to-net interop, consider using COM, or as mentioned above, DLL export definitions on your C++. I personally wouldn't try to interop with C++ class definitions the way you suggest here, there are way easier ways to do that.
Alternatively, just change your C++ project into a .NET C++ project and you can access the classes directly from F#, while still having the power of C++.
Naturally, you may still be wondering why the example doesn't run in FSI. You can see a hint of an answer by running the following:
> System.IO.Directory.GetCurrentDirectory();;
val it : string = "R:\TMP"
To fix this, you have a myriad of options:
Win32Project2.dll
to that directoryPATH
Copying is probably the easiest of these solutions.
Since FSI is meant to be a REPL, it may not be best tailored for this kind of tasks that require multiple projects, libraries or otherwise complex configurations. You may consider voting on this FSI request for support for #package
to import NuGet packages, which could be used to ease such tasks.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With