質問するログイン新規登録

回答編集履歴

1

質問の前提条件が変わったので検証して追記

2017/10/03 08:10

投稿

dodox86
dodox86

スコア9418

answer CHANGED
@@ -30,4 +30,100 @@
30
30
 
31
31
  尚、上記とは反対にF#からC++の関数を呼ぶのは正しくP/Invoke 用の設定さえすれば、はるかに簡単です。
32
32
 
33
- 参考になれば幸いです。
33
+ 参考になれば幸いです。
34
+
35
+ ### [追記] C++/CLIのコードから F#のものを呼び出す
36
+ **Visual Studio 2012でできました**
37
+
38
+ 前提がC++/CLI と言うことでしたので、改めて確認しました。
39
+ 遅くなりましたが、試して確証を得た範囲でお答えします。
40
+
41
+ 私の方で先に提示させていただいた代案
42
+ 1. F#の関数を C++ではなく、C++/CLIから利用する。
43
+
44
+ ですが、Visual Studio のバージョンによってはビルドできず、確認できませんでした。
45
+ OKだったのは Visual Studio 2012 ですので、そちらで紹介します。
46
+ VS2015ではNGでしたが、いろいろいじって環境が変わったせいがあるかもしれません。
47
+ (原因は追究できていません)
48
+ VS2013はインストールしていないので未確認です。
49
+
50
+ VS2012で試した限り、基本的には以下のプロジェクト作成の流れで
51
+ C++/CLIからF#のメソッドを呼べました。
52
+
53
+ 0. F#のプロジェクトを「ライブラリ」で作成する。(.NETアセンブリ DLL)
54
+ 1. 同じソリューションの管理下で、C+++/CLIのプロジェクトを作る。
55
+  (「コンソールアプリ」で試しました)
56
+ 2. C++/CLIのプロジェクトの「参照」で、F#のプロジェクト自体を参照させる。
57
+ ※Visual StudioS2015 ではエラーが発生し、できませんでした。
58
+
59
+ 3. F# のコードを書く。
60
+ 例を示します。デフォルトコンストラクタと、intとstring を返すメソッドを持つ簡単なクラスです。
61
+
62
+ ```F#
63
+ namespace LibrarySpace
64
+
65
+ type FSharpLibrary = class
66
+ val intA: int
67
+ val stringA: string
68
+
69
+ (* default constructor *)
70
+ new () = {
71
+ intA = 10
72
+ stringA = "String#10"
73
+ }
74
+
75
+ (* return in *)
76
+ member this.getIntA = this.intA
77
+
78
+ (* return string *)
79
+ member this.getStringA = this.stringA
80
+
81
+ end;;
82
+ ```
83
+
84
+ 5.C++/CLIのコードを書く。コンソールアプリです。このコードでF#の
85
+ "LibrarySpace"名前空間のFSharpLibraryクラスのインスタンスへアクセスします。
86
+ ```
87
+ // C++/CLI
88
+ #include "stdafx.h"
89
+ #include <vcclr.h> // PtrToStringChars()
90
+ #include <stdio.h> // wprintf()
91
+
92
+ using namespace System;
93
+ using namespace LibrarySpace;
94
+
95
+ int main(array<System::String ^> ^args)
96
+ {
97
+ Console::WriteLine(L"Hello");
98
+
99
+ FSharpLibrary^ lib = gcnew FSharpLibrary();
100
+
101
+ // getIntAメソッド呼び出し
102
+ int num = lib->getIntA;
103
+
104
+ // getStringA メソッド呼び出し
105
+ System::String^ s = lib->getStringA;
106
+ Console::WriteLine(s);
107
+
108
+ // Cランタイムで文字列を表示
109
+ wchar_t* ps = (wchar_t*)System::Runtime::InteropServices::Marshal::StringToHGlobalUni(s).ToPointer();
110
+ wprintf(L"printf: %s\n", ps);
111
+ System::Runtime::InteropServices::Marshal::FreeHGlobal(IntPtr(ps));
112
+
113
+ Console::WriteLine(L"End");
114
+ return 0;
115
+ }
116
+ ```
117
+
118
+ これで、F#のLibrarySpace::FSharpLibraryクラスのgetIntA, getStringAメソッドを
119
+ 呼び出せることを確認できました。名前空間(例では"LibrarySpace")に注意してください。
120
+ 正しく参照できていれば、Visual Studioのインテリセンスで自動的にシンボルが
121
+ 入力できるようになります。
122
+
123
+ 例のコードで分かるように、C++/CLI側のコードでは、例えばchar* やwchar_t 等の
124
+ ネイティブの文字列を扱うときには .NET Framework のSystem::Stringとchar* 等を
125
+ 相互変換する必要があります。面倒くさいので、適切なラッパー関数を更に書く必要が
126
+ あるかもしれません。
127
+
128
+ また、上記のテストプロ含め、F#<-->C++/CLI間での簡単なやり取りしか確認していません。
129
+ F#でのタプル等の複雑な入出力が扱えるかは未確認です。