を返す C++ 関数がありますstd::vector<float>
。
私はいくつかのCコードとインターフェースしています。
float 配列へのポインターを返すようにこの C++ 関数を変更するにはどうすればよいですか? また、この返された配列を C コードで使用できるように保存するにはどうすればよいでしょうか?
std::vector::data
C++11 を使用する場合、またはC++11 を使用できない場合は、「生の」配列へのポインターを取得&my_vector[0]
できます。ただし、ベクトル操作によってベクトルのサイズが強制的に変更されると、未加工のストレージがメモリ内で移動し、このポインターを安全に使用できなくなります。これが発生する可能性がある場合は、別のストレージを割り当て (たとえば、 のコピーを作成することによりvector
)、代わりにそのポインタを提供する必要があります。
更新: Woodrow のコメントにより、実際には function からポインターを返した後であることに気付きました。ヒープ割り当てメモリへのポインターのみを返すことができるため、これを行うためにローカルvector
(または他の種類のスタック割り当てメモリ) を使用することはできません。
C の観点から、次のvector<float>
2 つのことを行います。
2 は C にとって異質な概念であるため (自動的に何も起こらず、メモリを解放することはありません)、単純な置換はありません。基本的に、3 つのオプションがあります。これらは、関数が C で「文字列を返す」ようにする場合に使用できる 3 つのオプションと同じですが、ここでは呼び出し元にポインターと長さの両方を伝える必要があります。
私の意見では、3 番目の選択肢は「正しい答え」です。つまり、デザインで最初に試して、デザインが良さそうならそのまま使い続けるという意味です。1 番目と 2 番目の関数は、呼び出し元のコードが実際に恩恵を受ける場合に便利な関数として提供できます。
size_t c_wrapper(float **pResult) {
try {
std::vector<float> vec(cpp_function());
*pResult= (float*) std::malloc(vec.size() * sizeof(float));
if (!*pResult) { /* handle the error somehow */ }
std::copy(vec.begin(), vec.end(), *pResult);
return vec.size();
} catch (std::bad_alloc) { /* handle the error somehow */ }
}
利点: シンプルな呼び出しコード。
free
欠点:サイズが事前にわかっていて、データがローカル配列変数に問題なく収まる場合でも、呼び出し元はメモリを使用する必要があります。メモリ割り当てにより遅くなる場合があります。
モデル: strdup
(Posix)
jrokの答えを見てください:
size_t c_wrapper(float **pResult) {
try {
static std::vector<float> vec;
vec = cpp_function(); // or cpp_function().swap(vec) in C++03
*pResult = &vec[0];
return vec.size();
} catch (std::bad_alloc) { /* handle the error somehow */ }
}
利点: 途方もなく単純な呼び出しコード。
欠点: プログラムには のインスタンスが 1 つしかないsave
ため、返されるポインターは、次回c_wrapper
呼び出されるまで正しいデータのみを指します。特に、これは非常にスレッドセーフではありません。結果が非常に大きい場合、呼び出し元がメモリを必要としなくなってから関数が次に呼び出されるまでの間、そのメモリは浪費されます。
モデル: strtok
, gethostbyname
.
size_t c_wrapper(float *buf, size_t len) {
try {
std::vector<float> vec(cpp_function());
if (vec.size() <= len) {
std::copy(vec.begin(), vec.end(), buf);
}
return vec.size()
} catch (std::bad_alloc) { /* handle the error somehow */ }
}
利点: 最も柔軟です。
欠点: 呼び出し元は、十分な大きさのバッファーを渡す必要があります (動作が一貫していると仮定cpp_function
すると、呼び出し元は、サイズ 0 と null ポインターを使用して関数を呼び出し、どこかから十分な大きさのバッファーを取得してから、関数を再度呼び出すことでサイズを確認できます)。
モデル: strcpy
、snprintf
、getaddrinfo
。
#include <vector>
#include <iostream>
int main()
{
std::vector<float> vec;
vec.push_back(1.23);
vec.push_back(3.123);
int len = vec.size();
float *arr = new float[len];
std::copy(vec.begin(),vec.end(),arr);
for(int i = 0; i < sizeof(arr)/sizeof(arr[0]); ++i){
std::cout << arr[i] << "\n";
}
delete [] arr;
return 0;
}
返された一時的なベクターを、静的な保存期間を持つベクター オブジェクトに保存できます。
std::vector<float> foo()
{
return std::vector<float>();
}
float* call_foo_and_get_pointer()
{
static std::vector<float> save; // this line is executed only at the first
// call to enclosing function
save = foo();
return save.data(); // or &data[0]
}
から返されたポインタはcall_foo_and_get_pointer
、次の呼び出しまで有効であることが保証されています。