views:

397

answers:

9

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 it to a variable. However, the variable is a char * and c_str() returns a const char []. Is there a good way to get around this without having to roll my own function to do it?

edit: I am also trying to avoid calling new. I will gladly trade slightly more complicated code for less memory leaks.

+9  A: 

I guess there is always strcpy.

Or use char* strings in the parts of your C++ code that must interface with the old stuff.

Or refactor the existing code to compile with the C++ compiler and then to use std:string.

dmckee
Refactoring the existing code is a little out of the question right now. It is around 10k lines of hardcore, scientific C implementing a fairly complicated algorithm. However, I am pleased there is a simple answer I didn't think of.
Jergason
If you are avoiding new (as you say in another comment), you should probably consider using `strncpy` to avoid over running your stack or static allocated buffer...but then you'll have to deal with the std::string too long for buffer case another way.
dmckee
No need for strncpy. If you're copying from a std::string you know how long that is. Good advice doesn't hold everywhere.
Alex
+3  A: 

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.

Juan
+4  A: 

There's always const_cast...

std::string s("hello world");
char *p = const_cast<char *>(s.c_str());

Of course, that's basically subverting the type system, but sometimes it's necessary when integrating with older code.

mch
Are there any strange side-effects to using const_cast (besides now being able to modify the c string somewhere else in the code)?
Jergason
I don't think the standard currently requires contiguous storage, so make sure your implementation uses it. (It likely does). The next standard will require it, AFAIK.
GMan
You have to be careful, very careful, not to go off the end of the memory allocated for the string. If you modify the contents of the resulting pointer, you are modifying the internal buffer of the std::string. If you attempt to use the pointer after the std::string object goes out of scope, there will be trouble. It's only something to be used in a very limited scope when you absolutely have to (i.e. within say 10 lines of the const_cast). Don't take the resulting char* and pass it to other functions, return it, store it as a member variable, etc. Make a copy if you have to do that.
mch
Wait . . . `const_cast` **removes** the `const` qualification? Who picked the name for such a cast.
dreamlax
`const_cast` modifies the `const`-ness of it's argument.
GMan
There aren't any strange side effects to using const_cast. The assembly code generated can, in certain cases, not be as efficient because the compiler can't assume the string won't be changed. The main time I have used const_cast in this context is to pass strings to legacy C libraries in which the programmer didn't use 'const'. I re-iterate mch's 'careful'.
mcdave
Ahhhh, that makes sense . . . I think.
dreamlax
-1. You will get undefined behavior here when you will try to modify const object.
Kirill V. Lyadvinsky
+1: just check whether the string won't be changed.
stefaanv
`const_cast` is often a bad idea. In this particular case, if you modify the internal buffer (without overrunning), how can you make sure that... the length is still correct ? Most of the string implementations I have seen buffered the length variable (so that .size() is O(1) and not O(n)).
Matthieu M.
-1 undefined behavior (C++ Standard)
Alexey Malistov
Modifying the characters pointed to by p is undefined behavior - if the reason the cast is needed is because the function being called just neglected to mark its parameter const, that might be fair enough. If the reason a non-const pointer is needed is because the function being called modifies the string, then it isn't fair enough.
Steve Jessop
A: 

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.

Matt Davis
That's what const_cast is for, though. Slightly smaller sledgehammer.
Timo Geusch
Thing is, is you ever want to clean that up, it's a whole lot easier to look for `const cast` in the code than `char *`.
David Thornley
A: 

If c_str() is returning to you a copy of the string object internal buffer, you can just use const_cast<>.

However, if c_str() is giving you direct access tot he string object internal buffer, make an explicit copy, instead of removing the const.

Franci Penov
A: 

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;
Chris Lutz
Use a `std::vector`.
GMan
I'm trying to avoid calling new. Edited the original question to make that more clear.
Jergason
@Jergason - I did provide you with a way to pass in your own (stack allocated) buffer, thus completely avoiding the call to `new`. And @GMan - Why would I use a `std::vector` when the question calls for a `char *` ?
Chris Lutz
You should pass a size parameter into convert if you are passing in a stack allocated buffer (or any buffer for that matter). Then use strncpy to avoid going off the end of the destination buffer.
mch
GMan
+6  A: 

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.

Joe Mabel
I think this is the key point.
Daniel Daranas
Thanks, Daniel.
Joe Mabel
A: 

Since c_str() gives you direct const access to the data structure, you probably shouldn't cast it. The simplest way to do it without having to preallocate a buffer is to just use strdup.

char* tmpptr;
tmpptr = strdup(myStringVar.c_str();
oldfunction(tmpptr);
free tmpptr;

It's quick, easy, and correct.

WarrenB
Nonstandard, also, but anybody competent in C can whip up a version pretty fast. (Just remember to malloc the extra byte for the null terminator.)
David Thornley
A: 

If you can afford extra allocation, instead of a recommended strcpy I would consider using std::vector<char> like this:

// suppose you have your string:
std::string some_string("hello world");
// you can make a vector from it like this:
std::vector<char> some_buffer(some_string.begin(), some_string.end());
// suppose your C function is declared like this:
// some_c_function(char *buffer);
// you can just pass this vector to it like this:
some_c_function(&some_buffer[0]);
// if that function wants a buffer size as well,
// just give it some_buffer.size()

To me this is a bit more of a C++ way than strcpy. Take a look at Meyers' Effective STL Item 16 for a much nicer explanation than I could ever provide.

Dmitry