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

回答編集履歴

1

addd

2018/02/02 08:20

投稿

yumetodo
yumetodo

スコア5852

answer CHANGED
@@ -19,4 +19,74 @@
19
19
  }
20
20
  ```
21
21
 
22
- これ、無効なポインタを返しています。
22
+ これ、無効なポインタを返しています。
23
+
24
+
25
+ ---
26
+
27
+ 追記
28
+
29
+ C#の場合はもういっそC++/CLIでDLLをつくるほうが楽ですが(GC的に)
30
+ ref: [c# - C#でC++のDLLから文字列を受け取る - スタック・オーバーフロー](https://ja.stackoverflow.com/questions/30434/c%E3%81%A7c%E3%81%AEdll%E3%81%8B%E3%82%89%E6%96%87%E5%AD%97%E5%88%97%E3%82%92%E5%8F%97%E3%81%91%E5%8F%96%E3%82%8B)
31
+
32
+ C++の場合もDLLと呼び出し側のコンパイラを揃えないといけないですが、いっそC++で作って最初から`std::string`を渡すほうが楽です。
33
+
34
+ それでもなおC APIにこだわるということでしたら以下を読んで下さい。
35
+
36
+ 基本的にC APIで文字列をかえすには、文字列を格納するメモリを動的に確保する必要があります(`malloc`とかで)。
37
+ ということはそのメモリーは開放しないといけないですね。
38
+
39
+ ここで2つの戦略が考えられます。
40
+
41
+ # 戦略1: 先に文字列の長さを通知して、呼び出し側でメモリーを確保してもらい、そこに書き込む
42
+
43
+ つまり
44
+
45
+ ```c
46
+ size_t c_api_return_string(char* out, size_t out_size);
47
+ ```
48
+
49
+ のようなAPIがあったとき、
50
+
51
+ ```cpp
52
+ std::string c_api_return_string_wrap()
53
+ {
54
+ const auto len = c_api_return_string(nullptr, 0);
55
+ std::string str;
56
+ str.resize(len);
57
+ c_api_return_string(&str[0], str.size());
58
+ str.resize(std::char_traits<char>::length(str.c_str());
59
+ return str;
60
+ }
61
+ ```
62
+
63
+ のようなラップ関数がC++側でかけるわけです。
64
+
65
+ 利点としては、予め長さを求めるコストが無視できる場合、メモリー確保回数やコピーを最小限にでき、またDLL側で開放関数を用意せずに済むという点です。
66
+
67
+ 例では文字列の長さ通知と文字列返却を`c_api_return_string`で両方共やっていますが、別に関数を分けても構いません。(C#を考えるなら分けたほうがいい・・・?)
68
+
69
+ 欠点は、予め長さを求めるコストが無視できない場合には使えないことです。
70
+
71
+ # 戦略2: DLL側でメモリー確保して返却
72
+
73
+ ```c
74
+ char* c_api_return_string();
75
+ void free_c_api_return_string(char*);
76
+ ```
77
+
78
+ のようなAPIに対して
79
+
80
+ ```cpp
81
+ std::string c_api_return_string_wrap()
82
+ {
83
+ char* tmp = c_api_return_string();
84
+ std::string re = tmp;
85
+ free_c_api_return_string(tmp);
86
+ return tmp;
87
+ }
88
+ ```
89
+
90
+ のようなラップ関数がC++側でかけるわけです。
91
+
92
+ 利点・欠点は先の逆ですので割愛します。