C++ va_lists and what-not or another problems.

Started by
7 comments, last by Zahlman 15 years, 3 months ago
I am having a problem with taking extra arguments in a function and converting it to a string. I am not sure where I am going wrong, but it could be because I am tired. Anyway, I am going to have to type the code from memory being as it is unavailable to me at this time. Any help is appreciated! Thanks guys and merry Christmas and all of those other holidays; and if you don't celebrate any of these holidays, have a good day to you too anyway. :]

void DrawText( const int _pos_x, const int _pos_y, const std::string _string )
{
     //My drawing stuff...
}

const std::string MakeStringFromArgs( const std::string _string, ... )
{
     const char* format = _string.c_str(); //Only because va_ stuff won't take std::string. Darn them.
     char result[255];
     va_list args;

     va_start( format, args ); 
     vsprintf( result, format, args );
     va_end( args );

     std::string _return; 

     for( signed int i = 0; i < (signed) strlen(result); i++ )
          _resturn._push_back( result );

     return _return;
}

int main( int argc, char** argv )
{
     //to my drawing stuff
     DrawText( 320, 240, MakeStringFromArgs( "There are %i %s in your closet.". 100, "skeletons" );

     return 0;
}

This is all from memory but I believe this is how my code was somewhat set up. Anyway, the problem is not the conversion itself I believe but the va_ stuff. The output shows the strings in order but the 100, for instances end up as -8000000+ something ( it is a list of numbers not known off the top of my head but I am for sure it was long, started with an 8, and negative ) Any help is obliged. Thank you all again. :]
Holy crap, you can read!
Advertisement
The easiest way is to use std::stringstream to create a string in memory. This type acts like a mixture of std::cout and std::cin. Use of va_list is generally not recommended.
#include <sstream>int main( int argc, char** argv ){     int number = 10;     std::string object = "skeletons";     std::stringstream stream;     stream << "There are " << number << object << " in your closet.";     DrawText( 320, 240, stream.str());     return 0;}
I understand that it would help, but I'm trying to create a function that takes the "..." and the string before to make into a proper string to draw. Doing it this way will allow me to do this:

DrawText( 320, 300, MakeStringFromArgs( "%s I can %s this. %i and %f are %s the same thing%s", "Now", "do", 10, 10.4f, "not". "!!!" ) );//drawn text will be "Now I can do this! 10 and 10.4f are not the same thing!!!"


You see? Thanks though. :]
Holy crap, you can read!
Quote:Original post by PCN
I understand that it would help, but I'm trying to create a function that takes the "..." and the string before to make into a proper string to draw. Doing it this way will allow me to do this:

You see? Thanks though. :]



typedef std::stringstream str_t;DrawText(320, 300, (str_t() << "Now I can do this. " << 10 << " and " << 10.4f << " are not the same thing!!!").str());


It's so much better C++ wise, and not error prone like ... is.
The first parameter to va_start is the args list. The second parameter must be the first argument to the function before the "...".

eg:
void f(A a,B b,C c,D d,...){ va_list args; va_start(args, d); char * format = g(a,b,c,d); char buffer[256]; vsnprintf(buffer, sizeof(buffer), format, args);}


Notice d was the last argument before the ..., so d is the second param to va_start.

Use of va_end is actually unnecessary. Try looking up the implementation to see why.

By the way, I notice you passing std::string by value. That's going to lead to code bloat and poor performance. The last parameter before the ... must be a value type to be compatible with varargs (i.e. not an array, not a function, not a reference) so to solve both these issues at once, so I would recommend just using a const char * for the string, and if you use string other places in your program, use .c_str() before passing it to your varargs function.

EDIT: Also I recommend using vsnprintf instead of vsprintf, to avoid buffer overflows.

EDIT2: for reference, here's how I would write the function:

void formatString(std::string& str, const char * format, ...){	char buffer[4096];	va_list args;	va_start(args, format);	int length = vsnprintf(buffer, sizeof(buffer), format, args);	str.assign(buffer, length);}
Quote:Original post by PCN
I understand that it would help, but I'm trying to create a function that takes the "..." and the string before to make into a proper string to draw. Doing it this way will allow me to do this


You can acomplish similar -- only with type safety, the ability to pass objects, and argument count safety as well -- with boost::format, or the exact same thing (except with the safety again) with boost::format, BOOST_PP, and templates.

va_args, on the other hand, have a tendency to constantly require debugging when the code around them is written, changed, or so much as even thought about.
class DrawText {  int x, y;  std::stringstream buffer;  // Should not be copied.  DrawText(const DrawText&);  DrawText& operator=(const DrawText&);  public:  ~DrawText() {    // old drawing logic here; use buffer.str() to get the text  }  DrawText(int x, int y) : x(x), y(y), buffer() {}  template <typename T>  DrawText& operator()(const T& t) {    buffer << t; return *this;  }};// DrawText(320, 300)("Now I can do this. ")(10)(" and ")(10.4f)(" are")(10 == 10.4f ? " " : " not")(" the same thing!!!");
For me today, code in source boxes is chopped off at the height of the box unless replying. Wasn't that a bug on this forum software from ages ago?
Edit: No wait, after posting my reply I can read the whole source too - odd.

Very cool solution Zahlman, once I could see the whole thing.
Just wondering though, do you usually include things in the constructor initialisation list that are default initialised, or was it just for clarity in this example?
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Quote:Original post by iMalc
Very cool solution Zahlman, once I could see the whole thing.


I've written something very similar many times for the forum...

Quote:Just wondering though, do you usually include things in the constructor initialisation list that are default initialised, or was it just for clarity in this example?


IIRC members of primitive type will be uninitialized if you don't do this, so I just do the easy-to-remember thing and make it explicit. (My feeling is that where the Zen of Python says "explicit is better than implicit", this is the kind of explicit-ness they're talking about.)

This topic is closed to new replies.

Advertisement