Skip to main content
gamedev.net
🔒 Locked

To implement a printf like function

Started by Taha Ansari Jun 29, 2006 at 5:49 AM 5 replies 1.7k views
Original Post
Taha Ansari
Taha Ansari
Description of printf function is given by msdn as follows:

int printf(
   const char *format [,
   argument]... 
);
in the stdio.h file it is as follows:

_CRTIMP int __cdecl printf(const char *, ...);
I copy latter part as something like:

int abc(const char *, ...);
I also have a body for it. The next thing I want is the 'detect' what arguments were passed in this function. Is there any way to implement such custom made functions? Thank you.
rip-off
rip-off
A quick google for "variadic functions in c" gives this, and also this.

It is preferable not to use variadic functions if you are using c++, the way the iostream classes use chaining is more type safe.
Aardvajk
Aardvajk
You need to look into the va_args and va_start etc macros defined in stdarg.h but I warn you it is not pretty.

There are normally better alternatives in C++ to variable length argument lists, which are depreciated, type-unsafe and very error prone.

If you are really insistent, another approach is to use existing standard library functions to implement what you want.

For example, if you wanted a printf-like function that passed its result to a window routine expecting a const char *str,

#include <stdarg.h>#include <stdio.h>void output(const char *s,...){    va_list va; va_start(va,s);    char buf[1024]; // beware of overflow - very hard to check with the                    // following functions    vsprintf(buf,va);    OutputToWindowOrWhatever(buf);    va_end(va);}


This will support all the formatting etc offered by printf but it is next to impossible to check for overflowing the buf array and not really recommended.

I probably shouldn't admit this on GameDev but I use this approach quite a lot for debugging and in programs that only I will ever use, but I would never consider it for production or library code.

HTH Paul
romer
romer
Quote:
Original post by EasilyConfused
There are normally better alternatives in C++ to variable length argument lists, which are depreciated, type-unsafe and very error prone.

*snip*

This will support all the formatting etc offered by printf but it is next to impossible to check for overflowing the buf array and not really recommended.

Well sure, that's if you elect to use functions like vsprintf(). While I'll agree that writing code to handle variadic parameters can get messy pretty fast, that doesn't mean writing safe code in this case is like some esoteric process. It just means you have to be a little smarter about how you're managing your buffers and to make sure you are using the right functions for the job.

For instance, take your code example above. I sure as hell would not be using vsprintf() to construct the final string. I would use vsnprintf(), pass in the length of the buffer, thus ensuring that it won't write more characters than the buffer can hold. Depending on what platform your on, you can even use its return value to determine if the string got truncated and use it to construct a new buffer of the precise length required to hold the final formatted string:

/* NOTE: caller is responsible for freeing memory of returned pointer.         returns NULL on error. */char *make_me_a_string( const char *format, ... ){    char    *string        = NULL;    char     nul_buffer[1] = { 0 };    int      length        = 0;    va_list  args;    /* get the argument list */    va_start( args, format );    /* find out how big we need the string */    if( (length = vsnprintf( nul_buffer, sizeof(nul_buffer),                             format, args )) < 0 )    {        va_end( args );        return NULL;    }    /* allocate a new buffer big enough.  add 1 for trailing terminator. */    if( (string = malloc( length + 1 )) == NULL )    {        va_end( args );        return NULL;    }    /* write our final string to the buffer */    if( vsnprintf( string, length + 1, format, args ) < 0 )    {        free( string );        va_end( args );        return NULL;    }    va_end( args );    return string;}

That code above with work on Linux-type systems. Microsoft's implementation of vsnprintf() and snprintf() differ in how it handles the case when the final string would be longer than the buffer supplied as well as what is returned in that case (and, in my opinion, is a more retarded way of doing things). They use the value -1 to signify both an IO error and in the case when the number of characters needed for the format string is longer than the supplied count. Thus, you can't do what's done above and easily figure out how big of a buffer you need. I know I've written something in the past that works around this issue and basically emulates what goes up above, but I can't remember it off the top of my head right now. If I remember it or I actually find the code that does this, I'll post it up. Moral of the story, though, is that writing safe code for this stuff isn't difficult. You just gotta be smart about how you do it. :)
Aardvajk
Aardvajk
vnsprintf is non-standard as far as I know and the difference in the way implementations handle it are a very good reason to avoid it.

No matter how "safe" you make it, variable length argument lists are a bad idea in modern programming and should be avoided. You can not guarantee that the caller will use your function safely, even if the caller is you at a later date, and no amount of clever gubbins in the function will protect against parameters that do not match the formatting string.
Taha Ansari
Taha Ansari
Initially I was thinking of using variadic functions to be a part of a function, being part of a dll and called externally. It was good to envision that maybe it will be called as:

SOMEDLL_API int extFunc( char* ip, ... );

where that ... would represent something like.. one/two/three/four.. unsigned long* etc. Then reading howmany such arguemtents were passed, and of what type, i'll perform action on them- reading from all your posts make me think other wise...my initial idea was of a 'generic sort of dll exported function'.

Thank you.
Zahlman
Zahlman
Quote:
Original post by EasilyConfused
vnsprintf is non-standard as far as I know and the difference in the way implementations handle it are a very good reason to avoid it.

No matter how "safe" you make it, variable length argument lists are a bad idea in modern programming and should be avoided. You can not guarantee that the caller will use your function safely, even if the caller is you at a later date, and no amount of clever gubbins in the function will protect against parameters that do not match the formatting string.


Quoted for emphasis.

Especially nasty:

std::string x;std::cout << x << std::endl; // ok; automatically handles the typeprintf("%s", x); // boom, even though we thought about the format string.                  // The std::string has to be .c_str()'d first.

Topic Locked

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

Sign in to reply to this topic.