C++からF#の関数を呼ぶのは不可能ではないはずですが、現状、かなり難しいようです。
お試しになったやり方は F#からC++/C言語の関数を呼ぶP/Invokeの方法です。
(ただし、それでも不完全です)目的の再確認になりますが、呼び出す側の言語としては
.NET Framework用のC++/CLIではなく、ネイティブのC++ からF#の関数を呼ぶ方法、
で正しいでしょうか。
F# はご存知のように、.NET Framework上で稼動する実行ファイルを作成する言語です。
C++で作成したネイティブの実行コードからは、.NET Framework上で動作するコードに
アクセスする為、.NET Frameworkとは別の、WindowsのCOMインターフェースを
経由する必要があります。
以下は、「C++からC#のDLLを呼ぶ方法」を紹介された、大変参考になる情報へのリンクです。
C++からC#のDLLを呼ぶ方法
大筋においてこちらで紹介されている方法でC#のコード、ひいては.NET Framework用の
言語で提供される関数に、全部とはいかず、制限付きながらもアクセスできるはずですが、
Visual Studio 2015(※1)でのF#のプロジェクトの設定画面をみると、ビルドで造られる
アセンブリDLLを「COMの相互参照を可能」とし、Windowsに登録する方法が見当たりませんでした。
別途コマンドラインからセットする方法もありそうなのですが、正しく動作する例は未確認です。
※1 質問者様の使われているVisual Studioは2013ですが、ほぼ同じと思われます。
現状、F#の関数を別のC++な言語から利用する手としてより簡単なのは
0. F#の関数を C++ではなく、C++/CLIから利用する。
- F#の関数を C#から利用する。C#とC++はCOMを経由して使う。
かと考えられます。ソフトの規模にもよりますが、そうまで苦労してネイティブ用C++
からF#の関数を呼べるようにするべきか、再考の余地があるかと思います。
尚、上記とは反対にF#からC++の関数を呼ぶのは正しくP/Invoke 用の設定さえすれば、はるかに簡単です。
参考になれば幸いです。
[追記] C++/CLIのコードから F#のものを呼び出す
Visual Studio 2012でできました
前提がC++/CLI と言うことでしたので、改めて確認しました。
遅くなりましたが、試して確証を得た範囲でお答えします。
私の方で先に提示させていただいた代案
- F#の関数を C++ではなく、C++/CLIから利用する。
ですが、Visual Studio のバージョンによってはビルドできず、確認できませんでした。
OKだったのは Visual Studio 2012 ですので、そちらで紹介します。
VS2015ではNGでしたが、いろいろいじって環境が変わったせいがあるかもしれません。
(原因は追究できていません)
VS2013はインストールしていないので未確認です。
VS2012で試した限り、基本的には以下のプロジェクト作成の流れで
C++/CLIからF#のメソッドを呼べました。
-
F#のプロジェクトを「ライブラリ」で作成する。(.NETアセンブリ DLL)
-
同じソリューションの管理下で、C+++/CLIのプロジェクトを作る。
(「コンソールアプリ」で試しました)
-
C++/CLIのプロジェクトの「参照」で、F#のプロジェクト自体を参照させる。
※Visual StudioS2015 ではエラーが発生し、できませんでした。
-
F# のコードを書く。
例を示します。デフォルトコンストラクタと、intとstring を返すメソッドを持つ簡単なクラスです。
F#
1namespace LibrarySpace
2
3type FSharpLibrary = class
4 val intA: int
5 val stringA: string
6
7 (* default constructor *)
8 new () = {
9 intA = 10
10 stringA = "String#10"
11 }
12
13 (* return in *)
14 member this.getIntA = this.intA
15
16 (* return string *)
17 member this.getStringA = this.stringA
18
19 end;;
5.C++/CLIのコードを書く。コンソールアプリです。このコードでF#の
"LibrarySpace"名前空間のFSharpLibraryクラスのインスタンスへアクセスします。
// C++/CLI
#include "stdafx.h"
#include <vcclr.h> // PtrToStringChars()
#include <stdio.h> // wprintf()
using namespace System;
using namespace LibrarySpace;
int main(array<System::String ^> ^args)
{
Console::WriteLine(L"Hello");
FSharpLibrary^ lib = gcnew FSharpLibrary();
// getIntAメソッド呼び出し
int num = lib->getIntA;
// getStringA メソッド呼び出し
System::String^ s = lib->getStringA;
Console::WriteLine(s);
// Cランタイムで文字列を表示
wchar_t* ps = (wchar_t*)System::Runtime::InteropServices::Marshal::StringToHGlobalUni(s).ToPointer();
wprintf(L"printf: %s\n", ps);
System::Runtime::InteropServices::Marshal::FreeHGlobal(IntPtr(ps));
Console::WriteLine(L"End");
return 0;
}
これで、F#のLibrarySpace::FSharpLibraryクラスのgetIntA, getStringAメソッドを
呼び出せることを確認できました。名前空間(例では"LibrarySpace")に注意してください。
正しく参照できていれば、Visual Studioのインテリセンスで自動的にシンボルが
入力できるようになります。
例のコードで分かるように、C++/CLI側のコードでは、例えばchar* やwchar_t 等の
ネイティブの文字列を扱うときには .NET Framework のSystem::Stringとchar* 等を
相互変換する必要があります。面倒くさいので、適切なラッパー関数を更に書く必要が
あるかもしれません。
また、上記のテストプロ含め、F#<-->C++/CLI間での簡単なやり取りしか確認していません。
F#でのタプル等の複雑な入出力が扱えるかは未確認です。