我一直在寻找一种方法将一些数据填充到DLL边界的字符串中。因为我们使用不同的编译器,所有的dll接口都是简单的char *。
是否有正确的方法将指针传递给dll函数,以便能够直接填充字符串缓冲区?
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() ) );
看起来最有希望的是&amp; stringToFillIn [0]但是这是一个正确的方法,假设你认为string :: data()==&amp; string [0]?这似乎不一致。
或者最好是吞下额外的分配并避免这个问题:
vector<char> vectorToFillIn(100);
FunctionInDLL( &vectorToFillIn[0], vectorToFillIn.size() );
string dllGaveUs( &vectorToFillIn[0] );
答案 0 :(得分:23)
我不确定标准是否保证std::string
中的数据存储为char*
。我能想到的最便携的方法是使用std::vector
,保证将其数据存储在连续的内存块中:
std::vector<char> buffer(100);
FunctionInDLL(&buffer[0], buffer.size());
std::string stringToFillIn(&buffer[0]);
这当然要求将数据复制两次,这样效率有点低。
答案 1 :(得分:19)
经过更多的阅读和挖掘后,我发现string :: c_str和string :: data可以合法地返回一个指向缓冲区的指针,该缓冲区与字符串本身的存储方式无关。例如,字符串可能存储在段中。写入这些缓冲区会对字符串的内容产生不确定的影响。
此外,不应使用string :: operator []来获取指向字符序列的指针 - 它只应用于单个字符。这是因为指针/数组的等价不能保持字符串。
对此非常危险的是它可以在某些实现上工作,但在未来的某个日期突然中断,没有明显的理由。
因此,正如其他人所说,唯一安全的方法是避免任何尝试直接写入字符串缓冲区并使用向量,将指针传递给第一个元素然后从向量中分配字符串从dll函数返回。
答案 2 :(得分:9)
在C ++ 98中,您不应更改string::c_str()
和string::data()
返回的缓冲区。此外,正如其他答案中所解释的那样,您不应该使用string::operator[]
来获取指向字符序列的指针 - 它应该只用于单个字符。
从C ++ 11开始,字符串使用连续内存,因此您可以使用&string[0]
来访问内部缓冲区。
答案 3 :(得分:4)
只要C ++ 11提供连续的内存保证,在生产实践中这就是“hacky”。方法很受欢迎:
std::string stringToFillIn(100, 0);
FunctionInDLL(stringToFillIn.data(), stringToFillIn.size());
答案 4 :(得分:3)
我不会构造一个std :: string并在dll边界上发送指向内部缓冲区的指针。相反,我会使用一个简单的char缓冲区(静态或动态分配)。在调用dll之后,我会让std :: string接管结果。让callees在内部类缓冲区中写入是非常错误的。
答案 5 :(得分:2)
考虑到Patrick的评论我会说,直接写入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());
}
答案 6 :(得分:0)
std :: string的标准部分是API和一些行为,而不是实现的内存布局。
因此,如果您使用不同的编译器,则不能认为它们是相同的,因此您需要传输实际数据。正如其他人所说,传输char并推入新的std :: string。
答案 7 :(得分:0)
你们都已经解决了连续性问题(即它不能保证是连续的)所以我只提一下分配/解除分配点。我曾经遇到过在dll中分配内存的问题(即dll返回一个字符串),这些问题在破坏时会导致错误(在dll之外)。要解决此问题,必须确保分配器和内存池在dll边界上保持一致。它会节省一些调试时间;)
答案 8 :(得分:0)
您可以使用在unique_ptr中分配的char缓冲区代替vector:
{'date': '2019-10-21', 'hour': 3, 'id': '1'},
{'date': '2019-10-21', 'hour': 4, 'id': '1'},
{'date': '2019-10-20', 'hour': 0, 'id': '1'},
{'date': '2019-10-20', 'hour': 1, 'id': '1'},
{'date': '2019-10-21', 'hour': 0, 'id': '1'},
{'date': '2019-10-20', 'hour': 0, 'id': '1'},
{'date': '2019-10-19', 'hour': 5, 'id': '1'},
{'date': '2019-10-20', 'hour': 0, 'id': '2'},
{'date': '2019-10-20', 'hour': 0, 'id': '3'}
您不能使用&str [0]和str.data()之类的方式直接写入字符串缓冲区:
// allocate buffer
auto buf = std::make_unique<char[]>(len);
// read data
FunctionInDLL(buf.get(), len);
// initialize string
std::string res { buf.get() };
实时example。