在试图向同事证明可以使用F#的C++类时,我提出了以下概念证明.第一个片段是他为挑战提供的代码,下面的代码片段是我在F#中的实现.
namespace testapp {
struct trivial_foo {
int bar;
__declspec(dllexport) void set(int n) { bar = n; }
__declspec(dllexport) int get() { return bar; }
}
}
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
在Visual Studio中调试或作为独立程序运行时,这可以预测:TrivialFoo.Get
返回值bar
并TrivialFoo.Set
赋值给它.但是,从F#Interactive运行时,TrivialFoo.Set
不会设置该字段.我怀疑它可能与从非托管代码访问托管内存有关,但这并不能解释为什么它只在使用F#Interactive时才会发生.有谁知道这里发生了什么?
我不认为这个概念证明是互操作性的良好证明。您最好从 C++ 项目创建 DLL 导出定义并使用经过修饰的名称。
作为 PoC:F# 创建适合 CLI 的 MSIL,因此它可以与任何其他 CLI 语言进行互操作。如果这还不够,并且您想要本机到网络互操作,请考虑使用 COM,或者如上所述,在 C++ 上使用 DLL 导出定义。我个人不会尝试按照您在这里建议的方式与 C++ 类定义进行互操作,有更简单的方法可以做到这一点。
或者,只需将您的 C++ 项目更改为 .NET C++ 项目,您就可以直接从 F# 访问这些类,同时仍然拥有 C++ 的强大功能。
当然,您可能仍然想知道为什么该示例不能在 FSI 中运行。您可以通过运行以下命令来查看答案的提示:
> System.IO.Directory.GetCurrentDirectory();;
val it : string = "R:\TMP"
Run Code Online (Sandbox Code Playgroud)
要解决此问题,您有多种选择:
Win32Project2.dll
到该目录PATH
复制可能是这些解决方案中最简单的。
由于 FSI 旨在成为REPL,因此它可能不是最适合此类需要多个项目、库或其他复杂配置的任务。您可以考虑对此 FSI 请求进行投票,以支持#package
导入 NuGet 包,这可用于简化此类任务。