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

making a variable argument function(C++)

Started by Lazy Foo Feb 19, 2005 at 5:44 PM 6 replies 1.6k views
Original Post
Lazy Foo
Lazy Foo
How would I go about making a variable argument function? Here's is my bitmap font engine for my SDL games (the proplem doesn't really have to do directly with the SDL API), and I want to be able to to take a variable amount of arguments, clump them to one string (like I could with sprinf)then apply the text. how do I work with a variable arguments? my problems are the lines commented /* like this */

///////////////////////////////////////////////////////////////////////////////////
void LFont::text_out( int x, int y, SDL_Surface *thisSurface, char *myString, ... )
///////////////////////////////////////////////////////////////////////////////////
{
	//string buffer
	string buf;

	/* get the arguments */

	//get the new x and y coordinates
	this->y = y;
	this->x = x;

	//go thorugh the string
	for( int copy = 0; myString[ copy ] != '\0'; copy++ )
	{
		if( myString[ copy ] == '%' )
		{
			copy++;

			switch( myString[ copy ] )
			{
				case '%': 
				buf += '%';
				break;

				case 'i':
				/* get the next argument as an integer */
				/* append it to the string */
				break;

				case 's':
				/* get the next argument as a C string ( char * ) */
				/* append it to the string */
				break;

				case 'S':
				/* get the next argument as a string object */
				/* append it to the string */
				break;
			}
		}
		else
		{
			buf += myString[ copy ];
		}
	}

	//apply the string to the surface
	apply_text( buf.c_str(), thisSurface );
}

I would like to learn a cross platform method of doing this.
Learn to make games with my SDL 2 Tutorials
Kibble
Kibble
You should check out vsprintf, as it will be very very difficult to completely replicate all the formating types and flags.
TCHAR Buffer[g_StringFormatBuffer];va_list arguments;va_start(arguments, Fmt); // the second argument to this 'function' is the first argument before the var args, so you would put myString there._vsnprintf(Buffer, g_StringFormatBuffer, Fmt, arguments); // or vsprintf(Buffer, Fmt, arguments)va_end(arguments);
hplus0603
hplus0603
Typically, you'll use the string stream, and use operator<<() to put all your formatting into that container. C++ function overloading based on type makes this a safer alternative than %-style printf strings.

You can also roll your own in much the same way. The trick is to return a temporary formatter object, which when destructed, actually outputs the data, so it's efficient and keeps state only during the output statement:

class Buffer;class BufferPrinter {  ...  std::string formatted_; // or whatever  BufferPrinter( Buffer & target ) : target_( target ) {}  ~BufferPrinter() {    target_->receive( formatted_.c_str() );  }  BufferPrinter & operator<<( int i ) {    format an int onto "formatted_" member    return *this;  }  BufferPrinter & operator<<( char const * str ) {    format a string onto "formatted_" member    return *this;  }  ...};class Buffer {  ...  template< typename T > BufferPrinter & operator<<( T const & t ) {    return BufferPrinter( *this ) << t;  }  void receive( char const * formattedString );};

enum Bool { True, False, FileNotFound };
Kibble
Kibble
If you aren't set on using varargs, what hplus said is good, but also check out Boost's Format library.
Lazy Foo
Lazy Foo
its works now but I wonder if the code is cross platform ( in terms of the way I handle the variable argument list ):

///////////////////////////////////////////////////////////////////////////////////void LFont::text_out( int x, int y, SDL_Surface *thisSurface, char *myString, ... )///////////////////////////////////////////////////////////////////////////////////{	//string buffer	string buffer;	//get the new x and y coordinates	this->y = y;	this->x = x;	va_list Args;	va_start( Args, myString );	//go thorugh the string	for( int copy = 0; myString[ copy ] != '\0'; copy++ )	{		if( myString[ copy ] == '%' )		{			copy++;			switch( myString[ copy ] )			{				case '%': 				buffer += '%';				break;				case 'i':				{					char tempString[ 32 ]; 					sprintf( tempString, "%d", va_arg( Args, int ) );					buffer += tempString;				}				break;				case 's':				{					buffer += va_arg( Args, char* );				}				break;			}		}		else		{			buffer += myString[ copy ];		}	}	va_end ( Args );   	//apply the string to the surface	apply_text( buffer.c_str(), thisSurface );}
Learn to make games with my SDL 2 Tutorials
Promit
Promit
It's cross-platform, yes, but it's not good code. You really ought to use std::stringstream.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Zahlman
Zahlman
class textOutputter {  int x, y;  SDL_Surface* target;  std::stringstream msg;  LFont* font;  public:  textOutputter(int x, int y, LFont* font, SDL_Surface* target, std::string msg = "") :    x(x), y(y), font(font), target(target), msg(msg) {}  template <typename T>  void textOutputter& operator << (const T& input) {    msg << input; return *this;  }  ~textOutputter() {    font->text_out(x, y, target, msg.str());  }}void LFont::text_out( int x, int y, SDL_Surface *target, const string& data) {	this->y = y;	this->x = x;	apply_text(data.c_str(), target);}// ...{  textOutputter(0, 0, myLFont, mySDLSurface) << "hello world" << 123 << endl;  // Works with all the other things you might put onto a stream, too} // Message is output at end of scope, when the object is destructed// Depending on the details of LFont implementation, you may be able to do // better integration...
MaulingMonkey
MaulingMonkey
Reimplemented using boost.format :

///////////////////////////////////////////////////////////////////////////////////void LFont::text_out( int x, int y, SDL_Surface *thisSurface, const std::string & str )///////////////////////////////////////////////////////////////////////////////////{	//get the new x and y coordinates	this->y = y;	this->x = x;	//apply the string to the surface	apply_text( str.c_str(), thisSurface );}


Usage: instead of bad printf-method formatting which won't throw an exception, but rather most likely screw up the stack in a very nasty manner if using some calling methods, which is not typesafe and the result of which is completely undefined for objects:

font.text_out( 5 , 5 , surface , "%dx%d" , arg1 , arg2 );


We do the following, which is type safe and will throw an exception if things don't match correctly, and will work properly for user-made types that have stream operators implemented:

font.text_out( 5 , 5 , surface , str( format("%dx%d") % arg1 % arg2 ) );


text_out could also be implemented to take a boost::format object reference instead, eliminating the need for the str() wrapper.

Topic Locked

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

Sign in to reply to this topic.