回答編集履歴

1

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

2017/10/03 08:10

投稿

dodox86
dodox86

スコア9183

test CHANGED
@@ -63,3 +63,195 @@
63
63
 
64
64
 
65
65
  参考になれば幸いです。
66
+
67
+
68
+
69
+ ### [追記] C++/CLIのコードから F#のものを呼び出す
70
+
71
+ **Visual Studio 2012でできました**
72
+
73
+
74
+
75
+ 前提がC++/CLI と言うことでしたので、改めて確認しました。
76
+
77
+ 遅くなりましたが、試して確証を得た範囲でお答えします。
78
+
79
+
80
+
81
+ 私の方で先に提示させていただいた代案
82
+
83
+ 1. F#の関数を C++ではなく、C++/CLIから利用する。
84
+
85
+
86
+
87
+ ですが、Visual Studio のバージョンによってはビルドできず、確認できませんでした。
88
+
89
+ OKだったのは Visual Studio 2012 ですので、そちらで紹介します。
90
+
91
+ VS2015ではNGでしたが、いろいろいじって環境が変わったせいがあるかもしれません。
92
+
93
+ (原因は追究できていません)
94
+
95
+ VS2013はインストールしていないので未確認です。
96
+
97
+
98
+
99
+ VS2012で試した限り、基本的には以下のプロジェクト作成の流れで
100
+
101
+ C++/CLIからF#のメソッドを呼べました。
102
+
103
+
104
+
105
+ 0. F#のプロジェクトを「ライブラリ」で作成する。(.NETアセンブリ DLL)
106
+
107
+ 1. 同じソリューションの管理下で、C+++/CLIのプロジェクトを作る。
108
+
109
+  (「コンソールアプリ」で試しました)
110
+
111
+ 2. C++/CLIのプロジェクトの「参照」で、F#のプロジェクト自体を参照させる。
112
+
113
+ ※Visual StudioS2015 ではエラーが発生し、できませんでした。
114
+
115
+
116
+
117
+ 3. F# のコードを書く。
118
+
119
+ 例を示します。デフォルトコンストラクタと、intとstring を返すメソッドを持つ簡単なクラスです。
120
+
121
+
122
+
123
+ ```F#
124
+
125
+ namespace LibrarySpace
126
+
127
+
128
+
129
+ type FSharpLibrary = class
130
+
131
+ val intA: int
132
+
133
+ val stringA: string
134
+
135
+
136
+
137
+ (* default constructor *)
138
+
139
+ new () = {
140
+
141
+ intA = 10
142
+
143
+ stringA = "String#10"
144
+
145
+ }
146
+
147
+
148
+
149
+ (* return in *)
150
+
151
+ member this.getIntA = this.intA
152
+
153
+
154
+
155
+ (* return string *)
156
+
157
+ member this.getStringA = this.stringA
158
+
159
+
160
+
161
+ end;;
162
+
163
+ ```
164
+
165
+
166
+
167
+ 5.C++/CLIのコードを書く。コンソールアプリです。このコードでF#の
168
+
169
+ "LibrarySpace"名前空間のFSharpLibraryクラスのインスタンスへアクセスします。
170
+
171
+ ```
172
+
173
+ // C++/CLI
174
+
175
+ #include "stdafx.h"
176
+
177
+ #include <vcclr.h> // PtrToStringChars()
178
+
179
+ #include <stdio.h> // wprintf()
180
+
181
+
182
+
183
+ using namespace System;
184
+
185
+ using namespace LibrarySpace;
186
+
187
+
188
+
189
+ int main(array<System::String ^> ^args)
190
+
191
+ {
192
+
193
+ Console::WriteLine(L"Hello");
194
+
195
+
196
+
197
+ FSharpLibrary^ lib = gcnew FSharpLibrary();
198
+
199
+
200
+
201
+ // getIntAメソッド呼び出し
202
+
203
+ int num = lib->getIntA;
204
+
205
+
206
+
207
+ // getStringA メソッド呼び出し
208
+
209
+ System::String^ s = lib->getStringA;
210
+
211
+ Console::WriteLine(s);
212
+
213
+
214
+
215
+ // Cランタイムで文字列を表示
216
+
217
+ wchar_t* ps = (wchar_t*)System::Runtime::InteropServices::Marshal::StringToHGlobalUni(s).ToPointer();
218
+
219
+ wprintf(L"printf: %s\n", ps);
220
+
221
+ System::Runtime::InteropServices::Marshal::FreeHGlobal(IntPtr(ps));
222
+
223
+
224
+
225
+ Console::WriteLine(L"End");
226
+
227
+ return 0;
228
+
229
+ }
230
+
231
+ ```
232
+
233
+
234
+
235
+ これで、F#のLibrarySpace::FSharpLibraryクラスのgetIntA, getStringAメソッドを
236
+
237
+ 呼び出せることを確認できました。名前空間(例では"LibrarySpace")に注意してください。
238
+
239
+ 正しく参照できていれば、Visual Studioのインテリセンスで自動的にシンボルが
240
+
241
+ 入力できるようになります。
242
+
243
+
244
+
245
+ 例のコードで分かるように、C++/CLI側のコードでは、例えばchar* やwchar_t 等の
246
+
247
+ ネイティブの文字列を扱うときには .NET Framework のSystem::Stringとchar* 等を
248
+
249
+ 相互変換する必要があります。面倒くさいので、適切なラッパー関数を更に書く必要が
250
+
251
+ あるかもしれません。
252
+
253
+
254
+
255
+ また、上記のテストプロ含め、F#<-->C++/CLI間での簡単なやり取りしか確認していません。
256
+
257
+ F#でのタプル等の複雑な入出力が扱えるかは未確認です。