tags:

views:

430

answers:

6

I was looking for a way to stuff some data into a string across a DLL boundary. Because we use different compilers, all our dll interfaces are simple char*.

Is there a correct way to pass a pointer into the dll function such that it is able to fill the string buffer directly?

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() ) );

The one that looks most promising is &stringToFillIn[0] but is that a correct way to do this, given that you'd think that string::data() == &string[0]? It seems inconsistent.

Or is it better to swallow an extra allocation and avoid the question:

vector<char> vectorToFillIn(100);
FunctionInDLL( &vectorToFillIn[0], vectorToFillIn.size() );
string dllGaveUs( &vectorToFillIn[0] );
A: 
FunctionInDLL( stringToFillIn.c_str(), stringToFillIn.length() );

With length() instead of size()?

stefaanv
string::length and string::size always return the same value.
markh44
You're right, I answered too quickly. I would go for the solution that CAdaker proposed.
stefaanv
+1  A: 

I'd not construct a std::string and ship a pointer to the internal buffers across dll boundaries. Instead I would use either a simple char buffer (statically or dynamically allocated). After the call to the dll returns, I'd let a std::string take over the result. It just feels intuitively wrong to let callees write in an internal class buffer.

Magnus Skog
+5  A: 

I'm not sure the standard guarantees that the data in a std::string is stored as a char*. The most portable way I can think of is to use a std::vector, which is guaranteed to store its data in a continuous chunk of memory:

std::vector<char> buffer(100);
FunctionInDLL(&buffer[0], buffer.size());
std::string stringToFillIn(&buffer[0]);

This will of course require the data to be copied twice, which is a bit inefficient.

CAdaker
A: 

the standard part of std::string is the API and the some of the behavior, not the memory layout of the implementation.

Therefore if your using different compilers you can't assume they are the same, so you'll need to transport the actual data. As others have said transport the char's and push into a new std::string.

Simeon Pilgrim
+4  A: 

After a lot more reading and digging around I've discovered that string::c_str and string::data could legitimately return a pointer to a buffer that has nothing to do with how the string itself is stored. It's possible that the string is stored in segments for example. Writing to these buffers has an undefined effect on the contents of the string.

Additionally, string::operator[] should not be used to get a pointer to a sequence of characters - it should only be used for single characters. This is because pointer/array equivalence does not hold with string.

What is very dangerous about this is that it can work on some implementations but then suddenly break for no apparent reason at some future date.

Therefore the only safe way to do this, as others have said, is to avoid any attempt to directly write into the string buffer and use a vector, pass a pointer to the first element and then assign the string from the vector on return from the dll function.

markh44
C++0x is changing strings to use contiguous memory
Patrick
What Patrick says. Also, Herb Sutter in 2008, while in the midst of discussing this with the C++0x working group, did not know of an implementation that wasn't contiguous: http://herbsutter.wordpress.com/2008/04/07/cringe-not-vectors-are-guaranteed-to-be-contiguous/ (and scroll down to the comments).
Steve Jessop
markh44
That point in 20.3.3 (in my 3rd edition) is about something slightly different - the array/pointer equivalence doesn't apply to vectors either. I don't think Sutter is contradicting Stroustrup, and in any case when writing a reference work you want to avoid where possible statements of the form "at the moment, all implementations that I've looked at do X" in favour of "the standard guarantees Y, don't assume X". It's a bit different when you're discussing future versions of the standard and/or writing actual code that only has to work on implementations Sutter knows about...
Steve Jessop
Steve Jessop
A: 

You all have already addressed the contiguity issue (i.e. it's not guaranteed to be contiguous) so I'll just mention the allocation/deallocation point. I've had issues in the past where i've allocated memory in dlls (i.e. had dll return a string) that have caused errors upon destruction (outside the dll). To fix this you must ensure that your allocator and memory pool is consistent across the dll boundary. It'll save you some debugging time ;)

Faisal Vali