直接写入std :: string内部缓冲区

时间:2009-06-25 09:44:42

标签: c++ string

我一直在寻找一种方法将一些数据填充到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] );

9 个答案:

答案 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