直接写入std :: string内部缓冲区
我正在寻找一种方法来将一些数据填充到DLL边界的string中。 因为我们使用不同的编译器,所以我们所有的dll接口都是简单的char *。
有没有正确的方法来传递一个指针到dll函数,以便它能够直接填充string缓冲区?
string stringToFillIn(100, '\0'); FunctionInDLL( stringToFillIn.c_str(), stringToFillIn.size() ); // definitely WRONG! FunctionInDLL( const_cast<char*>(stringToFillIn.data()), stringToFillIn.size() ); // WRONG? FunctionInDLL( &stringToFillIn[0], stringToFillIn.size() ); // WRONG? stringToFillIn.resize( strlen( stringToFillIn.c_str() ) );
看起来最有希望的是&stringToFillIn [0],但是这是一个正确的方法来做到这一点,因为你会认为string :: data()==&string [0]? 这似乎不一致。
或者吞下额外的分配并避免这个问题是更好的:
vector<char> vectorToFillIn(100); FunctionInDLL( &vectorToFillIn[0], vectorToFillIn.size() ); string dllGaveUs( &vectorToFillIn[0] );
我不确定标准保证std::string
中的数据存储为char*
。 我能想到的最便携的方式是使用std::vector
,它保证将数据存储在连续的内存块中:
std::vector<char> buffer(100); FunctionInDLL(&buffer[0], buffer.size()); std::string stringToFillIn(&buffer[0]);
这当然要求数据被复制两次,这有点低效。
经过大量的阅读和挖掘,我发现string :: c_str和string :: data可以合法地返回一个指向缓冲区的指针,这个缓冲区与string本身的存储方式无关。 例如,string可能存储在段中。 写入这些缓冲区对string的内容有一个未定义的影响。
此外,不应使用string :: operator []来获取指向字符序列的指针 – 它只能用于单个字符。 这是因为指针/数组等价不适用于string。
这是非常危险的,它可以在一些实现工作,但突然之间在未来的日子没有明显的原因突破。
因此,像其他人所说的那样,唯一安全的方法就是避免直接写入string缓冲区并使用向量,将指针传递给第一个元素,然后从返回的向量中分配stringDLL函数。
在C ++ 98中,你不应该改变由string::c_str()
和string::data()
返回的缓冲区。 另外,正如其他答案中所解释的,不应该使用string::operator[]
来获取指向字符序列的指针 – 它只能用于单个字符。
从C ++ 11开始,string使用连续的内存,所以可以使用&string[0]
来访问内部缓冲区。
我不会构造一个std ::string,并通过DLL边界传递一个指向内部缓冲区的指针。 相反,我会使用一个简单的字符缓冲区(静态或dynamic分配)。 在对dll的调用返回后,我会让一个std :: string接pipe结果。 让被调用者在内部的类缓冲区中写入,感觉是错误的。
只要C ++ 11提供连续的内存保证,在生产实践中,这种“哈克”方法非常stream行:
std::string stringToFillIn(100, 0); FunctionInDLL(stringToFillIn.data(), stringToFillIn.size());
考虑到帕特里克的评论,我会说,直接写入std :: string是可行的,方便/高效的。 我将使用&s.front()
来获取char *
,就像这个mex的例子:
#include "mex.h" #include <string> void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] ) { std::string ret; int len = (int)mxGetN(prhs[0]); ret.reserve(len+1); mxGetString(prhs[0],&ret.front(),len+1); mexPrintf(ret.c_str()); }
std :: string的标准部分是API和一些行为,而不是实现的内存布局。
因此,如果你使用不同的编译器,你不能假设它们是相同的,所以你需要传输实际的数据。 正如其他人所说的传输字符,并推入到一个新的标准::string。
你们都已经解决了连续性问题(即不能保证是连续的),所以我只提到分配/释放点。 我以前有过问题,我已经在dll中分配了内存(即,dll返回一个string),导致在破坏时(dll外)造成错误。 要解决这个问题,你必须确保你的分配器和内存池在dll边界是一致的。 它会为您节省一些debugging时间;)