tags:

views:

1160

answers:

7

I have a class that holds an "error" function that will format some text. I want to accept a variable number of arguments and then format them using printf.

Example:

class MyClass
{
public:
    void Error(const char* format, ...);
};

The Error method should take in the parameters, call printf/sprintf to format it and then do something with it. I don't want to write all the formatting myself so it makes sense to try and figure out how to use the existing formatting.

+4  A: 

have a look at vsnprintf as this will do what ya want http://www.cplusplus.com/reference/clibrary/cstdio/vsprintf/

you will have to init the va_list arg array first, then call it.

Example from that link: /* vsprintf example */

#include <stdio.h>
#include <stdarg.h>

void Error (char * format, ...)
{
  char buffer[256];
  va_list args;
  va_start (args, format);
  vsnprintf (buffer, 255, format, args);


  //do something with the error

  va_end (args);
}
Lodle
Thanks! That's exactly what I was looking for!
Well selected it as the correct answer by clicking the tick :P
Lodle
I selected John Kugelman's response as it gave a more insight into the final answer and addressed the buffer issues both you and I had in our sample code.
Np's thats stack overflow for ya :P
Lodle
+1  A: 

I should have read more on existing questions in stack overflow.

C++ Passing Variable Number of Arguments is a similar question. Mike F has the following explanation:

There's no way of calling (eg) printf without knowing how many arguments you're passing to it, unless you want to get into naughty and non-portable tricks.

The generally used solution is to always provide an alternate form of vararg functions, so printf has vprintf which takes a va_list in place of the .... The ... versions are just wrappers around the va_list versions.

This is exactly what I was looking for. I performed a test implementation like this:

void Error(const char* format, ...)
{
    char dest[1024 * 16];
    va_list argptr;
    va_start(argptr, format);
    vsprintf(dest, format, argptr);
    va_end(argptr);
    printf(dest);
}
The final 'printf(dest);' is mal-formed - it needs at least a format string too.
Jonathan Leffler
It doesnt as the string is the format string i.e. printf("a string"); is fine
Lodle
You can get away with printf(dest) up until dest happens to contain "%s" or "%d", then *BOOM*. Please use printf("%s", dest).
John Kugelman
Brain death - you're right...confusing it with 'fprintf()'. But - you've got a different problem there. You should use 'vprintf()'. If the formatted output of 'vsprintf()' contains any % symbols, then you have a core dump looming.
Jonathan Leffler
A: 

Have a look at the example http://www.cplusplus.com/reference/clibrary/cstdarg/va_arg/, they pass the number of arguments to the method but you can ommit that and modify the code appropriately (see the example).

stefanB
+1  A: 

Simple example below. Note you should pass in a larger buffer, and test to see if the buffer was large enough or not

void Log(LPCWSTR pFormat, ...) 
{
    va_list pArg;
    va_start(pArg, pFormat);
    char buf[1000];
    int len = _vsntprintf(buf, 1000, pFormat, pArg);
    va_end(pArg);
    //do something with buf
}
DougN
A: 

You are looking for variadic functions. printf() and sprintf() are variadic functions - they can accept a variable number of arguments.

This entails basically these steps:

  1. The first parameter must give some indication of the number of parameters that follow. So in printf(), the "format" parameter gives this indication - if you have 5 format specifiers, then it will look for 5 more arguments (for a total of 6 arguments.) The first argument could be an integer (eg "myfunction(3, a, b, c)" where "3" signifies "3 arguments)

  2. Then loop through and retrieve each successive argument, using the va_start() etc. functions.

There are plenty of tutorials on how to do this - good luck!

rascher
+13  A: 

Bad

void Error(const char* format, ...)
{
    char dest[1024 * 16];
    va_list argptr;
    va_start(argptr, format);
    vsprintf(dest, format, argptr);
    va_end(argptr);
    printf(dest);
}

This code is not so good. It uses a fixed-size character buffer which can lead to a buffer overrun error if the string is pathologically long. The arbitrary large 1024*16 size should set off a flag in your head. Also, the printf call could run into problems if dest ends up containing formatting codes. Better would be printf("%s", dest). But even better still would be using vprintf or vfprintf:

Good

void Error(const char* format, ...)
{
    va_list argptr;
    va_start(argptr, format);
    vfprintf(stderr, format, argptr);
    va_end(argptr);
}

If you want to manipulate the string before you display it and really do need it stored in a buffer first, please please please use vsnprintf instead of vsprintf. vsnprintf will prevent an accidental buffer overflow error.

John Kugelman
John, thanks for the update and the amusing commentary. :)The code I put in the example was my first "yay, I figured out how to make it work" pass. I had discovered vsnprintf and that gave me ultimately what I wanted. Thanks!
Note: using varargs isn't necessarily a best practice. Tread carefully.
sheepsimulator
+2  A: 
Kirill V. Lyadvinsky