void printLine(const wchar_t* str, ...)
{
// have to do something to make it work
wchar_t buffer[2048];
_snwprintf(buffer, 2047, ????);
// work with buffer
}
printLine(L"%d", 123);
I tried
va_list vl;
va_start(vl,str);
and things like this but I didn't find a solution.
Here's a simple C code that does this, you will have to include stdarg.h for this to work.
void panic(const char *fmt, ...){
char buf[50];
va_list argptr; /* Set up the variable argument list here */
va_start(argptr, fmt); /* Start up variable arguments */
vsprintf(buf, fmt, argptr); /* print the variable arguments to buffer */
va_end(argptr); /* Signify end of processing of variable arguments */
fprintf(stderr, buf); /* print the message to stderr */
exit(-1);
}
The typical invocation would be
panic("The file %s was not found\n", file_name); /* assume file_name is "foobar" */
/* Output would be:
The file foobar was not found
*/
Hope this helps,
Best regards,
Tom.
What you want to use is vsprintf it accepts the va_list argument and there is sample
code on MSDN in the link.
EDIT:
You should consider _vsnprintf which will help avoid buffer overrun issues that vsprintf will happily create.
Typically one calls into a variable args version of the function, that accepts va_list. For example _snwprintf internally calls _vsnwprintf; try calling that.
Other people have already pointed you to the vprintf-family of functions, but this also (not surprisingly) is answered by the comp.lang.c FAQ, if you want to familiarize yourself with the other FAQ entries. (They're worth reading, IMO.)
How can I write a function that takes a format string and a variable number of arguments, like printf, and passes them to printf to do most of the work?
Related
I'm working on patching some old code (15-20 year old) and I come across strange segments from time to time. Here is one that has me scratching my head.
27 void FormDefFileScanner::FormDefFileerror(char *fmt, ...)
28 {
29 va_list va;
30 va_start(va, fmt);
31 /* This is related to some sort of debuging */
32 if (FormDefFilelineno)
33 fprintf(stderr, "%d: ", FormDefFilelineno);
34 /* This is where I'm unsure */
35 (void) vfprintf(stderr, fmt, va);
36 fputc('\n', stderr);
37 va_end(va);
... /* rest of the program */
... }
I know from my research on the "..." argument how va_list is supposed to work. By that I mean a va_arg() call with the list and variable type are required to pull values correctly from va_list. I guess I'm wondering how a vprintf() call can correctly parse a va_list. I assume the format string helps, but I'm not sure that every thing in va_list has the same word size. Any insight would be appreciated.
Lets play "Imagination". Imagine this code:
typedef char* va_list;
#define va_start(va_bytes, arg) (va_bytes=reinterpret_cast<char*>((&arg)+1))
#define va_end(va_bytes)
#define va_arg(va_bytes,type) (*reinterpret_cast<type*>((va_bytes+=sizeof(type))-sizeof(type)))
So your code becomes this:
void FormDefFileScanner::FormDefFileerror(char *fmt, ...)
{
char* va_bytes;
va_bytes = reinterpret_cast<char*>((&fmt)+1); //points at first byte of ...
vfprintf(stderr, fmt, va_bytes); //passes a pointer to the bytes to vfprintf.
Then vprintf can do this:
void vfprintf(FILE*, char* format, char* va_bytes)
{
if (strcmp(format,"%d")==0) { //now we know the first param is an int
//I'm splitting the macro into two lines here for clarity
int value = *reinterpret_cast<int*>(va_bytes);
va_bytes += sizeof(int); //va_bytes now points at the second parameter
} else if (strcmp(format,"%llu")==0) { //first param is an long long unsigned int
//I'm splitting the macro into two lines here for clarity
long long unsigned value = *reinterpret_cast<long long unsigned*>(va_bytes);
va_bytes += sizeof(long long unsigned); //va_bytes now points at the second parameter
}
At all times, va_bytes points at the start of the next parameter. When given a va_arg, it converts those bytes to that type, and advances the pointer to just after that, which is the start of the subsequent parameter. It can't advance until you tell it the type via va_arg, because it doesn't know the types, and thus it doesn't know how many bytes are in each argument.
The real va_arg macro is far more complicated, because it deals with type alignments and such, and vfprintf clearly works nothing like I coded, but these should help clarify the general concepts.
I guess I'm wondering how a vprintf() call can correctly parse a
va_list. I assume the format string helps, but I'm not sure that every
thing in va_list has the same word size.
Things in a va_list do not have the same size. vprintf wouldn't have to "parse a va_list" manually -- it can just use va_arg. It knows what type the next argument needs to be, because of the format specifier in the format string. So you can imagine that if you were to write vprintf, inside the loop that parses the format string, you would have cases for each format specifier, and then in each case they would use va_arg to get the next argument of the correct type:
if (strcmp(format,"%d")==0) {
int value = va_arg(va, int);
// do stuff ...
} else if (strcmp(format,"%f")==0) {
double value = va_arg(va, double);
// do stuff ...
}
//...
As for how va_list, va_arg, etc. are implemented, that is something that is opaque and you shouldn't care about, because it varies depending on architecture and system. You just need to know that if you follow the contract of va_start, va_arg, etc., it works.
following is the implementation of my method
static VALUE myMethod(VALUE self, VALUE exc, const char* fmt, ...) {
// Need to get all the arguments passed to this function and print it
}
function is called as follows:
myMethod(exception, ""Exception message: %s, Exception object %d",
"Hi from Exception", 100);
Can you provide the code for myMethod() that will access all the arguments and print them out.
Thanks in advance.
The va_start and va_arg macro's are used to get the variable arguments in a function.
An example can be found on the Microsoft site: http://msdn.microsoft.com/en-us/library/kb57fad8(v=vs.71).aspx
In your case it's a bit trickier, since you need to parse the format string to exactly know how many arguments should be given and of which type they are. Luckily, the CRT contains a function for that. The vfprintf function can be given a va_list (which you get from va_start). vfprintf will use this one to process all the extra arguments. See http://www.cplusplus.com/reference/clibrary/cstdio/vfprintf/ for an example.
One way is to use vsnprintf().
Sample code:
char buf[256];
va_list args;
va_start(args, fmt);
if(vsnprintf(buf, sizeof(buf), fmt, args) > 0)
fputs(buf, stderr);
va_end(args);
You need to use va_start and va_arg macros to get the arguments.
You can take a look at this - it has some examples.
http://www.go4expert.com/forums/showthread.php?t=17592
TraceMessage is an WinAPI function with variable number of arguments. It is a tracing function, with a notation similar to printf, which generates a trace message in Windows tracing. The weird part here is that it receive a format string as part of the ellipsis, not as a dedicated argument.
It is possible to 'override' this function with a function of my own, which then needs to call TraceMessageVa (which is the same as TraceMessage, just with va_args rather than ellipsis).
So far so good; but now I want to access the traced message using a sprintf-like function, which has the format string out of the ellipsis. Thus I need to
- get the format string argument out of the ellipsis ;
- create a new va_list without the first argument.
Any idea about to how do it? Solutions specific to Visual Studio compiler are also acceptable. Thanks!
With a va_list you can pass it to a function which takes a va_list after having used va_arg on it already to have extracted one or more arguments. The va_list will then act like it "contains" only the rest of the arguments.
I have no experience with TraceMessage itself, but I've given an example using standard vprintf and a test function. You should be able to adapt as appropriate.
E.g.
#include <stdio.h>
#include <stdarg.h>
void test(int a, ...)
{
va_list va;
const char* x;
va_start(va, a);
x = va_arg(va, const char*);
vprintf(x, va);
va_end(va);
}
int main(void)
{
test(5, "%d\n", 6);
return 0;
}
This question already has answers here:
Closed 13 years ago.
Possible Duplicate:
C Programming: Forward variable argument list.
What I'd like to do is send data to a logging library (that I can't modfify) in a printf kind of way.
So I'd like a function something like this:
void log_DEBUG(const char* fmt, ...) {
char buff[SOME_PROPER_LENGTH];
sprintf(buff, fmt, <varargs>);
log(DEBUG, buff);
}
Can I pass varargs to another vararg function in some manner?
You can't forward the variable argument list, since there's no way to express what's underneath the ... as a parameter(s) to another function.
However you can build a va_list from the ... parameters and send that to a function which will format it up properly. This is what vsprintf is for. Example:
void log_DEBUG(const char* fmt, ...) {
char buff[SOME_PROPER_LENGTH];
va_list args;
va_start(args, fmt);
vsprintf(buff, fmt, args);
va_end(args);
log(DEBUG, buff);
}
You can send it to another function that takes a va_list as an argument. There is no other way, short of resorting to hand crafted asm, or doing some kind of horrifying guessing game to figure out the 'number' of parameters.
This would work:
void log_DEBUG(const char* fmt, ...)
{
va_list va;
va_start(va,fmt);
char buff[blah];
vsprintf(buff,fmt,va);
log(DEBUG,buff);
va_end(va);
}
Basically, whenever you write a function that takes ..., you should write another version that takes a va_list - its the polite thing to do if you want to enable this type of chaining call.
This is why you have the vprintf family of functions.
For your specific requirement, you can use vsprintf. My C/C++ is too rusty to recall if there's a straightforward way to do it when the other function isn't designed for it (without getting into ugly stack manipulation), but I tend to think not.
Not in standard C++.
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.
Use vfprintf, like so:
void Error(const char* format, ...)
{
va_list argptr;
va_start(argptr, format);
vfprintf(stderr, format, argptr);
va_end(argptr);
}
This outputs the results to stderr. If you want to save the output in a string instead of displaying it use vsnprintf. (Avoid using vsprintf: it is susceptible to buffer overflows as it doesn't know the size of the output buffer.)
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);
}
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);
}
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:
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)
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!
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
}
Using functions with the ellipses is not very safe. If performance is not critical for log function consider using operator overloading as in boost::format. You could write something like this:
#include <sstream>
#include <boost/format.hpp>
#include <iostream>
using namespace std;
class formatted_log_t {
public:
formatted_log_t(const char* msg ) : fmt(msg) {}
~formatted_log_t() { cout << fmt << endl; }
template <typename T>
formatted_log_t& operator %(T value) {
fmt % value;
return *this;
}
protected:
boost::format fmt;
};
formatted_log_t log(const char* msg) { return formatted_log_t( msg ); }
// use
int main ()
{
log("hello %s in %d-th time") % "world" % 10000000;
return 0;
}
The following sample demonstrates possible errors with ellipses:
int x = SOME_VALUE;
double y = SOME_MORE_VALUE;
printf( "some var = %f, other one %f", y, x ); // no errors at compile time, but error at runtime. compiler do not know types you wanted
log( "some var = %f, other one %f" ) % y % x; // no errors. %f only for compatibility. you could write %1% instead.
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).