views:

396

answers:

4

There are a number of Win32 functions that take the address of a buffer, such as TCHAR[256], and write some data to that buffer. It may be less than the size of the buffer or it may be the entire buffer.

Often you'll call this in a loop, for example to read data off a stream or pipe. In the end I would like to efficiently return a string that has the complete data from all the iterated calls to retrieve this data. I had been thinking to use std::string since it's += is optimized in a similar way to Java or C#'s StringBuffer.append()/StringBuilder.Append() methods, favoring speed instead of memory.

But I'm not sure how best to co-mingle the std::string with Win32 functions, since these functions take the char[] to begin with. Any suggestions?

+5  A: 

Rather than std::string, I would suggest to use std::vector, and use &v.front() while using v.size(). Make sure to have space already allocated!

You have to be careful with std::string and binary data.

s += buf;//will treat buf as a null terminated string
s += std::string(buf, size);//would work
Brian R. Bondy
it would probably be wise to set an initial size for the vector in the ctor, or alternatively call `resize()` on it. then pass it's `size()` as the length of the buffer.
Idan K
@Mike Weller : Thanks for the down vote... I don't see what your point is here... how about .data()? Of course I know what .c_str() does, but the OP said that the functions take in a buffer, and for arbitrary binary data, it's better to use std::vector rather than std::string.
Brian R. Bondy
Note that you either need to create the vector with size-setting constructor, or call resize() on it.
Nikolai N Fetissov
Ok so how would you use vector? I want to call for instance Win32's ReadFile() function, which takes LPVOID lpBuffer. How can you pass in the vector so that ReadFile ends up appending to what's already there? Or is there no clean way of doing this?
Leeks and Leaks
What about TCHAR buffer[256]; wstring ws; ws.append(buffer); ? Also it's worth noting that the data being read is a string.
Leeks and Leaks
std::vector<char> vc(100); size_t x = (last read amount); ReadFile(..., vc.begin()+x, vc.size()-x,...);
jmucchiello
+5  A: 

std::string has a function c_str() that returns its equivalent C-style string. (const char *)

Further, std::string has overloaded assignment operator that takes a C-style string as input.

e.g. Let ss be std::string instance and sc be a C-style string then the interconversion can be performed as :

ss = sc; // from C-style string to std::string
sc = ss.c_str(); // from std::string to C-style string

UPDATE :

As Mike Weller pointed out, If UNICODE macro is defined, then the strings will be wchar_t* and hence you would have to use std::wstring instead.

missingfaktor
Also note that TCHAR will change depending on the value of the UNICODE macro. If UNICODE is defined, then the strings will be wchar_t* and you would have to use std::wstring instead.
Mike Weller
writing directly to a `std::string` internal buffer can be dangerous, since the string also stores things like the length which might be invalid if you do this.
Idan K
Idan is correct. Don't use std::string for a function that will write to the string buffer.
jmucchiello
Using std::string as a memory buffer is incorrect because it is not guarantied by current standard to be contiguous in memory.
Dmitry
+5  A: 

If the argument is input-only use std::string like this

std::string text("Hello");
w32function(text.c_str());

If the argument is input/output use std::vector<char> instead like this:

std::string input("input");
std::vector<char> input_vec(input.begin(), input.end());
input_vec.push_back('\0');
w32function(&input_vec[0], input_vec.size());
// Now, if you want std::string again, just make one from that vector:
std::string output(&input_vec[0]);

If the argument is output-only also use std::vector<Type> like this:

// allocates _at least_ 1k and sets those to 0
std::vector<unsigned char> buffer(1024, 0);
w32function(&buffer[0], buffer.size());
// use 'buffer' vector now as you see fit

You can also use std::basic_string<TCHAR> and std::vector<TCHAR> if needed.

You can read more on the subject in the book Effective STL by Scott Meyers.

Dmitry
+1  A: 
  • You need a compatible string type: typedef std::basic_string<TCHAR> tstring; is a good choice.

  • For input only arguments, you can use the .c_str() method.

  • For buffers, the choice is slightly less clear:

std::basic_string is not guaranteed to use contiguous storage like std::vector is. However, all std::basic_string implementations I've seen do use contiguous storage, and the C++ standards committee consider the missing guarantee to be a defect in the standard. The defect has been corrected in the C++0x draft.

If you're willing to bend the rules ever so slightly - with no negative consequences - you can use &(*aString.begin()) as a pointer to a TCHAR buffer of length aString.size(). Otherwise, you're stuck with std::vector for now.

Here's what the C++ standard committee have to say about contiguous string storage:

Not standardizing this existing practice does not give implementors more freedom. We thought it might a decade ago. But the vendors have spoken both with their implementations, and with their voice at the LWG meetings. The implementations are going to be contiguous no matter what the standard says. So the standard might as well give string clients more design choices.

Joe Gauterin