[C++] std::string and c_str() problem

Started by
8 comments, last by ToohrVyk 17 years, 6 months ago
hey, i'd to know how could i store a 'c string' into std::string, the c_str() method seems to be read-only. so, is there another way, or will i have to use char*? error:
Quote: 75 invalid conversion from `const void*' to `void*' 75 initializing argument 2 of `int SDLNet_TCP_Recv(_TCPsocket*, void*, int)'
code:
Quote: std::string msg; msg.reserve(MAX_LEN); SDLNet_TCP_Recv(sock,msg.c_str(),MAX_LEN); //(void*)msg.c_str() == NULL
Advertisement
You cannot modify a string through its c_str member. Pass a char[] buffer to be filled, and then initialize the string with the buffer.

EDIT: alternatively, use a std::vector to store raw bytes. You can use an std::vector as a buffer, since its storage is guaranteed to be contiguous (although you need to initialize it with a correct size, since only reserving memory will not be sufficient).
I believe you need to do something like this:

string msg;char *tmp = new char [MAX_LEN];SDLNet_TCP_Recv(sock, tmp, MAX_LEN);msg = tmp;delete[] tmp;
Quote:Original post by drakostar
I believe you need to do something like this:

string msg;char *tmp = new char [MAX_LEN];SDLNet_TCP_Recv(sock, tmp, MAX_LEN);msg = tmp;delete[] tmp;

MAX_LEN is most likely a compile-time constant, so:
char tmp[MAX_LEN];SDLNet_TCP_Recv(sock, tmp, MAX_LEN);std::string msg = tmp;

should do it. Anyway this was exactly what ToohrVyk said.
Quote:Original post by drakostar
msg = tmp;


This is unsafe, because tmp might not be a nul-terminated C-style string. This operation might therefore not terminate, or it might terminate earlier if a nul character is in the middle of the buffer. In practice, this should be:

msg = std::string(tmp,tmp + MAX_SIZE);

To return to my previous std::vector proposition:

std::vector<char> data(MAX_LEN);SDLNet_TCP_Recv(sock,&(data[0]),MAX_LEN); 
Quote:Original post by ToohrVyk
This operation might therefore not terminate, or it might terminate earlier if a nul character is in the middle of the buffer. In practice, this should be:

msg = std::string(tmp,tmp + MAX_SIZE);

To return to my previous std::vector proposition:

std::vector<char> data(MAX_LEN);SDLNet_TCP_Recv(sock,&(data[0]),MAX_LEN); 


Yep, the latter suggestion is better. As an example:

std::string a;
a.push_back( 'c' );
a.push_back( 'h' );
a.push_back( '\0' );
a.push_back( 'c' );
a.push_back( 'o' );
a.push_back( 'l' );
a.push_back( 'a' );
a.push_back( 't' );
a.push_back( 'e' );
std::cout << a.size() << std::endl;
std::cout << a.c_str() << std::endl;


procudes

9
ch


(even though it is storing all 9 chars). Can be misleading / unsafe.
--== discman1028 ==--
Quote:Original post by theSecondt
hey, i'd to know how could i store a 'c string' into std::string, the c_str() method seems to be read-only.
so, is there another way, or will i have to use char*?


Um, we're not talking about something like this, are we?
char[] foo = "Hello world";std::string str(foo);


Or maybe a std::stringstream?

Maybe I'm just not clear on exactly what you want
A good example of what to watch out for is demonstrated by your friendly neghborhood telnet client. If you type slow enough each keypress is sent through a single char at a time. Try pressing tab or even backspace or arrow keys. Depending on how you parse the data on the receiving end this can cause lot's of problems.

One thing I have done in the past (and still use) in a text based MUD is a receiving buffer that fills up until a newline character is found. Once a newline is found, that portion of the buffer is parsed to find any special characters or escape sequences and interpreted accordingly. The resulting string is pushed onto a queue of command lines to be parsed etc...

Now, if I ever received a '\0' in the middle of a string that would be an error for me since only text should be passed back and forth for me and I drop their connection because something went wrong. I don't know what kind of information you are expecting from the end user but this is something to consider as well.

Take for example the code below:
Uint32 Recv( TCPsocket sock, std::string &buffer ){  Uint32 temp,total;  char data[MAX_LEN];  total = 0;   while( (temp=SDLNet_TCP_Recv(sock,&data,MAX_LEN)) != 0 )  {    buffer += std::string(data,temp);    total += temp;    // if the sender is spamming, possibly continuing from a previous send    if ( buffer.size() >= MAX_BUFFER_SIZE )        // kill the connection and move on  }  return total;}


That will continue to fill buffer until there is nothing left to receive or you go over a pre-determined amount. It is still up to you to parse buffer according to what you are expecting be there NULL's in the middle or not. I can't stress enough though how important it is to pay attention to how much is received and check it against how much you have parsed. This will help you find potential problems that can be very difficult to track down especially once you move on to higher level things that rely on the socket code to be solid.
Evillive2
Quote:Original post by ToohrVyk
Quote:Original post by drakostar
msg = tmp;


This is unsafe, because tmp might not be a nul-terminated C-style string. This operation might therefore not terminate, or it might terminate earlier if a nul character is in the middle of the buffer. In practice, this should be:

msg = std::string(tmp,tmp + MAX_SIZE);


Or better yet, avoid declaring variables until you know enough to know how to initialize them, and then actually initialize them.

Putting that together with the std::vector suggestion yields:

std::vector<char> data(MAX_LEN);SDLNet_TCP_Recv(sock,&(data[0]),MAX_LEN);// if the data is a "receive buffer" that might not be null-terminated:std::string msg(data.begin(), data.end()); // If the data is instead a "destination string" that will be null-terminated// (seems unlikely from the function name):// std::string msg(&(data[0]));

Quote:Original post by evillive2
That will continue to fill buffer until there is nothing left to receive or you go over a pre-determined amount. It is still up to you to parse buffer according to what you are expecting be there NULL's in the middle or not.


You meant a NUL, not a NULL.

Quote:Original post by Zahlman
Or better yet, avoid declaring variables until you know enough to know how to initialize them, and then actually initialize them.


Quoted for emphasis. Although in this particular case, I'd wager the character buffer is, in fact, a real character buffer and not a string, so I wouldn't bother creating the std::string at all.

This topic is closed to new replies.

Advertisement