I am modifying a function that accepts a const char* and uses a function, ProcessString. ProcessString is a function that expects a null-terminated character buffer as a char*. The characters in the buffer may or may not be modified, as defined by the function signature below. To "bridge the gap", I am using a temporary std::string:
void ProcessString( char* str );
void SomeFunction( const char* str )
{
string temp(str);
ProcessString( &temp[0] );
}
My primary question is about the guarantees of std::string::operator[] and whether the address returned by the &temp[0] above is a usable, null-terminated buffer as a char*. Secondly, and very much secondly, is there a better way to do this?
I am using C++03.
That only has well-defined behavior in C++11; in previous standards, std::string
did not guarantee contiguous storage for its internal buffer.
However while that code is completely fine in C++11, the more idiomatic approach is to use std:vector<char>
, which has guaranteed contiguous storage since C++03:
void ProcessString(char* str);
void SomeFunction(char const* str)
{
// + 1 for terminating NUL
std::vector<char> temp(str, str + std::strlen(str) + 1);
ProcessString(&temp[0]); // or temp.data() in C++11
}
I have created a small class to face this kind of problem, I have implemented the RAII idiom.
class DeepString
{
DeepString(const DeepString& other);
DeepString& operator=(const DeepString& other);
char* internal_;
public:
explicit DeepString( const string& toCopy):
internal_(new char[toCopy.size()+1])
{
strcpy(internal_,toCopy.c_str());
}
~DeepString() { delete[] internal_; }
char* str() const { return internal_; }
const char* c_str() const { return internal_; }
};
And you can use it as:
void aFunctionAPI(char* input);
// other stuff
aFunctionAPI("Foo"); //this call is not safe. if the function modified the
//literal string the program will crash
std::string myFoo("Foo");
aFunctionAPI(myFoo.c_str()); //this is not compiling
aFunctionAPI(const_cast<char*>(myFoo.c_str())); //this is not safe std::string
//implement reference counting and
//it may change the value of other
//strings as well.
DeepString myDeepFoo(myFoo);
aFunctionAPI(myFoo.str()); //this is fine
I have called the class DeepString because it is creating a deep and unique copy (the DeepString is not copyable) of an existing string.
If you need to go from a const char*
to a char *
, why not use strdup, and then free the buffer when ProcessString is done?
Going through std::string
seems unnecessary to me.