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).
Related
#include <cstdarg>
using namespace std;
void do_something( va_list numbers, int count ) {
// ^
// Should I call this by reference here? I mean, va_list & numbers?
//... stuff
va_end( numbers );
}
void setList( int count, ... ) {
va_list numbers;
va_start( numbers, count );
do_something( numbers, count );
}
int main() {
setList( 2, 0, 1 );
return 0;
}
When taking va_list to another function, how should I pass it to that function? I know that va_end must be called when things are done with va_list I'm confused whether I should call it by reference or not. Will va_list be properly ended even though it is not called by reference?
Variable argument lists:
You should never use va_end in a function taking the va_list as argument!
From the (Linux) man-page for va_arg:
Each invocation of va_start() must be matched by a corresponding
invocation of va_end() in the same function.
(Emphasis mine)
In a function having a va_list argument, take the va_list by value (as C-library functions like vprintf, ... do, too).
Example:
#include <cstdarg>
#include <iostream>
void v_display_integers(int count, va_list ap) {
while(0 < count--) {
int i = va_arg(ap, int);
std::cout << i << '\n';
}
}
void display_integers(int count, ...) {
va_list ap;
va_start(ap, count);
v_display_integers(count, ap);
va_end(ap);
// You might use 'va_start' and 'va_end' a second time, here.
// See also 'va_copy'.
}
int main() {
display_integers(3, 0, 1, 2);
}
Note:
In C++ you should avoid variable argument lists. Alternatives are std::array, std::vector,std::initializer_list and variadic templates.
I prefer to call va_end in the same function in which I called va_start or va_copy, like any other resource allocation/deallocation pair. That style is required by the standard, although some implementations are more tolerant.
Normally, va_lists are passed by value. They are small objects.
The macro function va_end is implemented as either void statement or resetting the va_list variable.
http://research.microsoft.com/en-us/um/redmond/projects/invisible/include/stdarg.h.htm
Theoretically, you can call it anywhere, or even skip it. However, to be a good programmer, we need to put it after the va_ functions
Very frequently in my C++ code I use the following type of helper function:
static inline std::string stringf(const char *fmt, ...)
{
std::string ret;
// Deal with varargs
va_list args;
va_start(args, fmt);
// Resize our string based on the arguments
ret.resize(vsnprintf(0, 0, fmt, args));
// End the varargs and restart because vsnprintf mucked up our args
va_end(args);
va_start(args, fmt);
// Fill the string
if(!ret.empty())
{
vsnprintf(&ret.front(), ret.size() + 1, fmt, args);
}
// End of variadic section
va_end(args);
// Return the string
return ret;
}
It has a few upsides:
No arbitrary limits on string lengths
The string is generated in-place and doesn't get copied around (if RVO works as it should)
No surprises from the outside
Now, I have a few of problems with it:
Kind of ugly with the rescanning of the varargs
The fact that std::string is internally a contiguous string with space for a null terminator directly after it, does not seem to actually be explicitly stated in the spec. It's implied via the fact that ->c_str() has to be O(1) and return a null-terminated string, and I believe &(data()[0]) is supposed to equal &(*begin())
vsnprintf() is called twice, potentially doing expensive throw-away work the first time
Does anybody know a better way?
Are you married (pun intended) to std::sprintf()? If you are using C++ and the oh so modern std::string, why not take full advantage of new language features and use variadic templates to do a type safe sprintf that returns a std::string?
Check out this very nice looking implementation: https://github.com/c42f/tinyformat. I think this solves all your issues.
As you added the tag c++11 I suppose that you can use it. Then you can simplify your code to this:
namespace fmt {
template< class ...Args >
std::string sprintf( const char * f, Args && ...args ) {
int size = snprintf( nullptr, 0, f, args... );
std::string res;
res.resize( size );
snprintf( & res[ 0 ], size + 1, f, args... );
return res;
}
}
int main() {
cout << fmt::sprintf( "%s %d %.1f\n", "Hello", 42, 33.22 );
return 0;
}
http://ideone.com/kSnXKj
Don't do the first vsnprintf with size 0. Instead, use a stack buffer with some probable size (e.g. something between 64 to 4096), and copy that into the return value if it fits.
Your concerns about std::string's contiguity are misplaced. It is well-defined and perfectly okay to rely on.
Finally - I would like to reecho that a compile-time format-checking library would be better. You need C++14 for full power, but C++11 supports enough that you should be able to #ifdef some header code without losing any expressive. Remember to think about gettext though!
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;
}
I am trying to implement a variadic function. I searched the web and ended up finding out that most examples handle only one type of arguments (for example calculating average of many integers). Im my case the argument type is not fixed. It can either involve char*, int or both at the same time. Here is the code i ended up with :
void insertInto(int dummy, ... ) {
int i = dummy;
va_list marker;
va_start( marker, dummy ); /* Initialize variable arguments. */
while( i != -1 ) {
cout<<"arg "<<i<<endl;
/* Do something with i or c here */
i = va_arg( marker, int);
//c = va_arg( marker, char*);
}
va_end( marker ); /* Reset variable arguments. */
Now this would work okay if i only had to deal with integers but as you see i have a char* c variable in comments which i would like to use in case the argument is a char*.
So the question is, how do I handle the returned value of va_arg without knowing if it is an int or a char* ?
since you're doing c++ there's no need to use untyped C-style variadic function.
you can simply define a chainable method like
class Inserter
{
public:
Inserter& operator()( char const* s )
{
cout << s << endl;
return *this;
}
Inserter& operator()( int i )
{
cout << i << endl;
return *this;
}
};
then use like
Inserter()( "blah" )( 42 )( "duh" )
variant with templated insert operation commonly used for building strings.
cheers & hth.,
So the question is, how do I handle the returned value of va_arg without knowing if it is an int or a char* ?
You can't. There is absolutely no way to determine the type of a variadic argument. You have to specify it somewhere else. That's why functions like printf require you to give the type of the arguments in the format string:
printf("%s %i\n", "Hello", 123); // works
printf("%s %i\n", 123, "Hello"); // also valid (though most modern compiles will emit a warning), but will produce garbage or crash
You need some way of knowing. Consider how printf solves this, with a format string. It's not the only possible approach, but it's a well-known one.
From http://en.wikipedia.org/wiki/Variadic_function#Variadic_functions_in_C.2C_Objective-C.2C_C.2B.2B.2C_and_D:
In some other cases, for example
printf, the number and types of
arguments are figured out from a
format string. In both cases, this
depends on the programmer to actually
supply the correct information.
So, to handle multiple types, the insertInto() need a concept like format string.
You can make the processing conditional on the dummy parameter. I have routines that take an "action" argument and treats the arguments differently based on the action
For example:
int i; char *c;
switch(dummy) {
case 1:
i = va_arg(marker, int);
// do something with i
break;
case 2:
c = va_arg(market, char *);
// do something with c
break;
}
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?