Const-correctness in C++ is still giving me headaches. In working with some old C code, I find myself needing to assign turn a C++ string object into a C string and assign i
Is it really that difficult to do yourself?
#include <string>
#include <cstring>
char *convert(std::string str)
{
size_t len = str.length();
char *buf = new char[len + 1];
memcpy(buf, str.data(), len);
buf[len] = '\0';
return buf;
}
char *convert(std::string str, char *buf, size_t len)
{
memcpy(buf, str.data(), len - 1);
buf[len - 1] = '\0';
return buf;
}
// A crazy template solution to avoid passing in the array length
// but loses the ability to pass in a dynamically allocated buffer
template <size_t len>
char *convert(std::string str, char (&buf)[len])
{
memcpy(buf, str.data(), len - 1);
buf[len - 1] = '\0';
return buf;
}
Usage:
std::string str = "Hello";
// Use buffer we've allocated
char buf[10];
convert(str, buf);
// Use buffer allocated for us
char *buf = convert(str);
delete [] buf;
// Use dynamic buffer of known length
buf = new char[10];
convert(str, buf, 10);
delete [] buf;
In CPP, if you want a char *
from a string.c_str()
(to give it for example to a function that only takes a char *
),
you can cast it to char *
directly to lose the const
from .c_str()
Example:
launchGame((char *) string.c_str());
You can use the copy method:
len = myStr.copy(cStr, myStr.length());
cStr[len] = '\0';
Where myStr
is your C++ string and cStr
a char *
with at least myStr.length()
+1 size. Also, len
is of type size_t
and is needed, because copy doesn't null-terminate cStr
.
std::string vString;
vString.resize(256); // allocate some space, up to you
char* vStringPtr(&vString.front());
// assign the value to the string (by using a function that copies the value).
// don't exceed vString.size() here!
// now make sure you erase the extra capacity after the first encountered \0.
vString.erase(std::find(vString.begin(), vString.end(), 0), vString.end());
// and here you have the C++ string with the proper value and bounds.
This is how you turn a C++ string to a C string. But make sure you know what you're doing, as it's really easy to step out of bounds using raw string functions. There are moments when this is necessary.
There is an important distinction you need to make here: is the char*
to which you wish to assign this "morally constant"? That is, is casting away const
-ness just a technicality, and you really will still treat the string as a const
? In that case, you can use a cast - either C-style or a C++-style const_cast
. As long as you (and anyone else who ever maintains this code) have the discipline to treat that char*
as a const char*
, you'll be fine, but the compiler will no longer be watching your back, so if you ever treat it as a non-const
you may be modifying a buffer that something else in your code relies upon.
If your char*
is going to be treated as non-const
, and you intend to modify what it points to, you must copy the returned string, not cast away its const
-ness.
If you know that the std::string
is not going to change, a C-style cast will work.
std::string s("hello");
char *p = (char *)s.c_str();
Of course, p
is pointing to some buffer managed by the std::string
. If the std::string
goes out of scope or the buffer is changed (i.e., written to), p
will probably be invalid.
The safest thing to do would be to copy the string if refactoring the code is out of the question.