Skip to main content
GameDev.net gamedev.net
🔒 Locked

std::string to char*

Started by EmrldDrgn Jul 28, 2006 at 12:30 AM 17 replies 4.1k views
Original Post
EmrldDrgn
EmrldDrgn
I'm trying to turn a std::string that I used for my function arguement into a char * to use as an arguement to another function. I tried using .c_str, but that returns a const char * (say, why can't you assign a char * to the value of a const one?). How can I get around this problem?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~I program in C++, on MSVC++ '05 Express, and on Windows. Most of my programs are also for windows.
Programmer16
Programmer16
A simple (char*) cast should work, but why do you want to convert it to a non-const string? You shouldn't modify the data directly. If you need to modify the data inside of a function, pass a const std::string reference instead of a char pointer.
Prototype
Prototype
Quote:
Original post by Programmer16
A simple (char*) cast should work, but why do you want to convert it to a non-const string? You shouldn't modify the data directly. If you need to modify the data inside of a function, pass a const std::string reference instead of a char pointer.

No, no, no. You should never cast away constness like that, even if you're 'sure' that your function never modifies the data. You are just undermining the security features of the language if you do that. If you really want to modify it, make a copy or else just have your function take a const char* as well.

Null and Void
Null and Void
I can't say I'd recommend the doing any const casting trickery, since the internals of basic_string are mostly left up to the implementation. If you really must pass a non-const character pointer to a string, you can do something along these lines:
#include <iostream>#include <string>#include <vector>int main() {	// A string	std::string s("Testing");	// Copy s into a vector, then add a null terminator	std::vector <char> v(s.begin(), s.end());	v.push_back(char(0));	// And, finally, direct access to a pointer	char *v_str = &v[0];	// Let's print it out for some reason	std::cout << v_str << std::endl;	// Say you need to put it back into the string now (edit: I like this way better, actually)	s.assign(&v[0], v.size()-1);}
EmrldDrgn
EmrldDrgn
I'm not entirely sure what you mean by that, so let me show you what I'm trying to do.

GLuint LoadGLTexture(std::string loader)
{
ilInit();
iluInit();
ilutRenderer(ILUT_OPENGL);
ilutInit();

loader = "Data/" + loader + ".bmp";
ILholder = loader;
texture = ilutGLLoadImage(ILholder);

return texture;
}

I'm wanting the std::string to do the concatenation. ILholder is a char *, which is what ilutGLLoadImage takes as a parameter. However, I can't get ILholder to be assigned loader. Also, I don't want to use const char *'s so that I can just make one, and not have to make a new one every time I call LoadGLTexture (which I do once a frame), because it's my understanding that'll drastically slow my program down.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~I program in C++, on MSVC++ '05 Express, and on Windows. Most of my programs are also for windows.
Null and Void
Null and Void
Okay, so (a) you need a string concatenation method and (b) you're just trying to interact with a function that should have its parameter declared const if it wasn't trying to be old-C compatible.

You can cheat in that case, I suppose :).
#include <sstream>// ...GLuint LoadGLTexture(const std::string &loader){  // ...  std::ostringstream sstr;  sstr << "Data/" << loader << ".bmp";  std::string str(sstr.str());    return ilutGLLoadImage(const_cast<char *>(str.c_str()));}
JohnBolton
JohnBolton
People have been complaining about this problem with DevIL for years (including Muhammad Haggag in 2003). I'm surprised it hasn't been fixed yet.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
mikeman
mikeman
Quote:

Also, I don't want to use const char *'s so that I can just make one, and not have to make a new one every time I call LoadGLTexture (which I do once a frame), because it's my understanding that'll drastically slow my program down.


This hasn't got to do with your original question, but if you call LoadGLTexture() each frame you will have a slowdown, but you can be sure that copying a string won't be the cause of it.
Programmer16
Programmer16
Quote:
Original post by Prototype
No, no, no. You should never cast away constness like that, even if you're 'sure' that your function never modifies the data. You are just undermining the security features of the language if you do that.


Yea, I know that. I guess I should have added a 'but don't do this' message.
Quote:
You shouldn't modify the data directly.

Oh wait, I did [razz]

Quote:
Original post by Prototype
If you really want to modify it, make a copy or else just have your function take a const char* as well.


Seems like I covered that as well.

And in the end, casting away constness was the answer [razz] (although using a C++ cast instead of a C cast.)
Hot Dog
Hot Dog
Hello EmrldDrgn,

Actually it is very simple to do what you want. Just add one more line that makes use of a C Standard Library string copier function strcpy.

I did not see a declaration for ILholder in your original code so I added my own.

#include


GLuint LoadGLTexture(std::string loader)
{
ilInit();
iluInit();
ilutRenderer(ILUT_OPENGL);
ilutInit();

loader = "Data/" + loader + ".bmp";
char* ILholder = new char[loader.length()];
texture = ilutGLLoadImage(strcpy(ILholder, loader.c_str());
delete []ILholder;

return texture;
}



strcpy is a function from the cstring header file. It basically does a copy from a char array to another char array. Its return value is the final char array pointer.

Hope this helps.

Regards,
Joshua
Brother Bob
Brother Bob
Quote:
Original post by Hot Dog
Actually it is very simple to do what you want.

Simple you say? Well, you failed. Your temporary buffer, ILholder, is too short, so strcpy will write outside it. Since you said it was so simple, I leave it up to you to figure out why and how to solve it.
MikeAinsworth
MikeAinsworth
Someone forgot the null terminator, well spotted!

Looks like making a non-const copy is the only correct way to solve this, although a bit of const correctness in the library would help!
Zahlman
Zahlman
Copying a string for use in a caveman-era function, and wrapping a return value, in the non-caveman era:

std::string doCStuffWith(const std::string& s) {  int len = s.length() + 1; // null terminators, rah rah rah  std::vector<char> raw(len); // or is that "raw raw raw"?  const char* str = s.c_str();  std::copy(str, str + len, raw.begin()); // Just say no to strcpy, memcpy etc.  brokenCFunction(&(raw[0]));   // Assuming the C function modifies the input string and we want to return the  // modified version  return std::string(&(raw[0]));  // vector storage is guaranteed contiguous now.  // No cleanup needed - handled by vector destructor.  // The C function could still screw things up by trying to increase the length  // of the passed-in string, but then it's REALLY broken.}

Conner McCloud
Conner McCloud
Quote:
Original post by Zahlman
Copying a string for use in a caveman-era function, and wrapping a return value, in the non-caveman era:

std::string doCStuffWith(const std::string& s) {  int len = s.length() + 1; // null terminators, rah rah rah  std::vector<char> raw(len); // or is that "raw raw raw"?  const char* str = s.c_str();  std::copy(str, str + len, raw.begin()); // Just say no to strcpy, memcpy etc.  brokenCFunction(&(raw[0]));   // Assuming the C function modifies the input string and we want to return the  // modified version  return std::string(&(raw[0]));  // vector storage is guaranteed contiguous now.  // No cleanup needed - handled by vector destructor.  // The C function could still screw things up by trying to increase the length  // of the passed-in string, but then it's REALLY broken.}

Is there a specific reason you didn't just do std::copy(s.begin(), s.end(), raw.begin()); and did away with str all together?

Another possibility is std::vector raw(s.begin(), s.end()); raw.push_back('\0'); But that's a little wasteful.

CM
Zahlman
Zahlman
Quote:
Original post by Conner McCloud
Quote:
Original post by Zahlman
Copying a string for use in a caveman-era function, and wrapping a return value, in the non-caveman era:

std::string doCStuffWith(const std::string& s) {  int len = s.length() + 1; // null terminators, rah rah rah  std::vector<char> raw(len); // or is that "raw raw raw"?  const char* str = s.c_str();  std::copy(str, str + len, raw.begin()); // Just say no to strcpy, memcpy etc.  brokenCFunction(&(raw[0]));   // Assuming the C function modifies the input string and we want to return the  // modified version  return std::string(&(raw[0]));  // vector storage is guaranteed contiguous now.  // No cleanup needed - handled by vector destructor.  // The C function could still screw things up by trying to increase the length  // of the passed-in string, but then it's REALLY broken.}

Is there a specific reason you didn't just do std::copy(s.begin(), s.end(), raw.begin()); and did away with str all together?


In case .c_str() returns a different pointer value on subsequent calls (pointing to an identical const representation). Pretty far-fetched, I suppose :
Quote:

Another possibility is std::vector raw(s.begin(), s.end()); raw.push_back('\0'); But that's a little wasteful.


Only wasteful if your implementation's initial capacity matches initial size exactly (While such a requirement may exist, I'm not aware of it). :) That's quite elegant, though. Doh on me.
MikeAinsworth
MikeAinsworth
Quote:
Original post by Zahlman
  // vector storage is guaranteed contiguous now.



How is that guarenteed? Does this not depend on the implimentation of vector?
Or do I missunderstand how you can use vectors?

It just looks like this bypasses the vector interface, which is not very OO at all.
In this case, the library uses a depreciated type, so wouldnt it be appropriate to us the standard libraries designed for the job? ie strcpy?

SiCrane
SiCrane
Quote:
Original post by MikeAinsworth
How is that guarenteed?

By ISO/IEC 14882:2003 Section 23.2.4 paragraph 1: "The elements of a vector are store contiguously...."
rip-off
rip-off
Quote:
Original post by MikeAinsworth
Quote:
Original post by Zahlman
  // vector storage is guaranteed contiguous now.



How is that guarenteed? Does this not depend on the implimentation of vector?
Or do I missunderstand how you can use vectors?



Par of std::vectors specification is that it holds elements contiguously.

Quote:

It just looks like this bypasses the vector interface, which is not very OO at all.


But the original problem is a workaround for a broken c interface. Not much that OO can do at all in that situation, std::string would perform admirably if it weren't for that.

Quote:

In this case, the library uses a depreciated type, so wouldnt it be appropriate to us the standard libraries designed for the job? ie strcpy?


strcpy() to what? A manually managed memory buffer? How is that more appropriate than using the c++ standard's buffer type, std::vector?
MikeAinsworth
MikeAinsworth
All fair points Zahlman. I didn't intend that as an argumentative reply, but I'm a stickler for detail and I don't like using solutions that havent been thought through properly.

The advantage this gives you is a nicely C++ managed container rather than messing about with new and delete. Which now that you point it out is considerable more OO friendly.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.