Jump to content
  • Advertisement
Sign in to follow this  
Maverick Programmer

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

This topic is 3463 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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. :]

Share this post


Link to post
Share on other sites
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;
}

Share this post


Link to post
Share on other sites
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. :]

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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);
}

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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!!!");

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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.)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!