Skip to main content
gamedev.net
🔒 Locked

variable argument list + std::string

Started by Lucas W Jul 20, 2008 at 10:26 AM 14 replies 3.5k views
Original Post
Lucas W
Lucas W
Hello all! Im sorry about the post spam. My laptop totally went crazy and I got script errors and such from GameDev.net. The question is (as some people already figured out) how I could get around the fact that I cant use std::string with a variable argument list. Im using this list for a logfunction, and I would need it pretty bad. It works just like sprintf and it works like a charm except for one thing. I would like to pass a std::string variable to the list and then read it somehow. Is this possible?


//Function
void Log(std::string format, ...)
{
   //do stuff.
   
   char *p = va_arg(list, char*);
   
   log << p;

   //do some more stuff.
}

//Usage
Log("My name is %s", "Lucas"); //Works

std::string name = "Lucas";
Log("My name is %s", name); //Does obviously not work.


So is there any way to get around this? (Changing the variable to a char pointer is not an option). Thanks, and sorry for the mess. [Edited by - Lucas W on July 20, 2008 6:20:07 PM]
-----------------------------------------------------I demand more toasties!
deadstar
deadstar
This has had 4 posts, I think somethings wrong with his browser/connection/mousemat
"The right, man, in the wrong, place, can make all the dif-fer-rence in the world..." - GMan, Half-Life 2   A blog of my SEGA Megadrive development adventures: http://www.bigevilcorporation.co.uk
Rydinare
Rydinare
I think we can guess the question, though. The question is probably why std::strings won't work via an ellipsis. The answer is that it has to do with the way variable argument lists work. The real fix to avoiding this problem is not using variable argument lists. Instead, using something like boost::format will make your life much easier.
Rattenhirn
Rattenhirn
The c_str() method returns a const char pointer, that can be passed into an ellipsis.

However, I agree that avoiding variable argument lists altogether is the better approach, because they are not type safe at all.
Rydinare
Rydinare
Quote:
Original post by Rattenhirn
The c_str() method returns a const char pointer, that can be passed into an ellipsis.

However, I agree that avoiding variable argument lists altogether is the better approach, because they are not type safe at all.


I was a bit vague. I meant as part of the format string. You can't have the format string be a std::string. I was just guessing that that was his problem, since it happened to me, once, a while back. That was when I swore of using varargs -- too many risks. Other than, we're in agreement, which is good. [smile]
Lucas W
Lucas W
Please take a look again at my problem and tell me how I should solve this :)

What is this boost::format and how do I use it?
-----------------------------------------------------I demand more toasties!
nullsquared
nullsquared
template<typename T>Log &operator<<(const T &rhs){    // do stuff    log << rhs;    // do some more stuff    return *this;}std::string name = "Lucas";log << "my name is " << name;


Why are you using a C idiom in C++?
TheUnbeliever
TheUnbeliever
Quote:
Original post by Lucas W
What is this boost::format and how do I use it?


Boost.Format

Google + Excellent Boost documentation online ftw.
[TheUnbeliever]
Rydinare
Rydinare
Quote:
Original post by Lucas W
Please take a look again at my problem and tell me how I should solve this :)

What is this boost::format and how do I use it?


The parameter for the format string can't be a std::string or any C++ object. It has to be a raw string. So to be safe it should look like this:

//Functionvoid Log(const char* format, ...){   //do stuff.      char *p = va_arg(list, char*);      log << p;   //do some more stuff.}//UsageLog("My name is %s", "Lucas"); //Worksstd::string name = "Lucas";Log("My name is %s", name.c_str()); // Convert to raw string when passing in


But, as mentioned, you lose type safety. Another option is to do this:

//Functionvoid Log(const std::string& str){   //do stuff.      log << str;   //do some more stuff.}using boost::format;using boost::str;//UsageLog(str(format("My name is %s") % "Lucas")); //Worksstd::string name = "Lucas";Log(str(format("My name is %s") % name)); // Works with C++ string


Boost format gives you a more typesafe way to do your typical printf-like string operations. For more information, check here: http://www.boost.org/doc/libs/1_35_0/libs/format/doc/format.html

Finally, I did learn something new from this post. When I said the format string can't be a std::string, I believe I had tried this:

void Log(const std::string& format, ...){   ...}


Which cannot work. Hadn't occurred to me that passing by value would allow it to work. Either way, ultimately another nasty feature of var args. It's not important, though, since I refuse to use var args anymore, anyway. [smile]
Lucas W
Lucas W
Hmm those are pretty good answers but they doesnt realy fit to my function.
I would like to limit one log call to one function call (if you get what i mean). The log sorts the logmessages after class and function that the messages occurred in.

Example:

enginelog.txt+Texture   -Load(1)      Failed to load texture: tree.dds at location: ./Assets/+Engine   -Init      The users hardware doesnt support hardware vertexprocessing+Log   -Write      Major fuckup due to variable argument list


So this wouldnt be possible (or would it?) with the operator thingy.

Im using the Log function like this:

Log("Failed to load texture: %s at location: %s and some more info %s%i%d", "functionName", "ClassName");//and the Log function writes it to the file tabbed and nicely.//if im doing this:Log << "Failed to load: " << filename;//how would my function know if its a new message or a part of the last message?//also, where would I specify my function and classname which it occured in?//btw, this doesnt workLog("bla bla %s", stringVariable.c_str()); //I dont know why it doesnt work


Im afraid that my only way out of this is using variable argument lists :/
-----------------------------------------------------I demand more toasties!
ToohrVyk
ToohrVyk
Quote:
Original post by Lucas W
So this wouldnt be possible (or would it?) with the operator thingy.


There are always solutions. One example would be to wrap an output stream (such as a stringstream) in an object which is returned by a "log" function and which does something with the text when it's destructed (happens at the end of the statement where it's used).

Below is a sample implementation: you provide the object with a function, and that function is called on the result of the output sequence (so you can decorate it with the appropriate data). It prints "{Hello World! 42}".

#include <iostream>#include <ostream>#include <sstream>template<typename F>struct output_message{  F functor;  std::stringstream target;  bool used;  output_message(const F &functor) : functor(functor), used(false) {}  output_message(const output_message &o) : functor(o.functor), used(false) {}  template <typename T> std::ostream &operator<<(const T& t)  {    used = true;    return target << t;  }  ~output_message()  {    if (used)      functor(target.str());  }};template <typename F>output_message<F> print(F f){  return output_message<F>(f);}void to_stdout(const std::string &str){  std::cout << "{" << str << "}\n";}int main(){  print(to_stdout) << "Hello World! " << 42;}

lexs
lexs
This may be of help to you:
http://www.gamedev.net/community/forums/topic.asp?topic_id=493670&whichpage=2�
Works as printf() but using boost::format and templates.
Rydinare
Rydinare
Quote:
Original post by lexs
This may be of help to you:
http://www.gamedev.net/community/forums/topic.asp?topic_id=493670&whichpage=2
Works as printf() but using boost::format and templates.


Awesome. I couldn't find that thread last time I was looking for it. Bookmarked it now. Thanks! [smile]
Lucas W
Lucas W
Thanks! I'll check that link out.

That piece of code you posted helped me alot, thanks.

But shouldnt it be any easy way to "convert" a std::string to a char pointer to situations like this? .c_str() doesnt seem to work because it returns a const char pointer.
-----------------------------------------------------I demand more toasties!
ToohrVyk
ToohrVyk
Quote:
Original post by Lucas W
But shouldnt it be any easy way to "convert" a std::string to a char pointer to situations like this? .c_str() doesnt seem to work because it returns a const char pointer.


One reason why people don't mix C library functions and C++ code is because C++ code tends not to play nice with the C library functions. In particular, since you need a char*, this means that you need to create a modifiable copy (because you're not allowed to change the internal data of an std::string). And so, you'd have to clean it up after yourself, too. A possibility is:

std::vector<char> copy(str.begin(), str.end());copy.push_back('\0'); // Null-terminatefrobnicate(& copy.front()); // use a non-const pointer// the vector is cleaned up automatically here

Topic Locked

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

Sign in to reply to this topic.