I have the following helper function:
inline void DebugMessage(const TCHAR* fmtstr, ...)
{
va_list args;
va_start(args, fmtstr);
TCHAR buffer[256];
StringCbVPrintf(buffer, 256, fmtstr, args);
OutputDebugString(buffer);
va_end(args);
}
And I call it twice like so:
DebugMessage(_T("Test %d\n", 1)); // incorrectly closed _T()
DebugMessage(_T("Test %d\n"), 1); // correctly closed _T()
I get the following output:
Test 0
Test 1
The 2nd case works as expected. I am confused why the first case functions at all, rather than being an error?
_T is not a function, it's a macro that (in a Unicode build) expands to L ## x. The misplaced bracket doesn't cause a compile error, it simply changes which parts of the line gets consumed by the macro.
The macro only takes one parameter (x) and so in the first case, with the incorrect closure, the second parameter (1) is simply discarded, and the number you get in your output is simply a result of random data on the stack.
Note that by default, VS 2012 will issue a C4002 warning about this (too many actual parameters for macro) so you may want to check that you have warnings enabled properly.
Related
I was trying to reduce my code and I found something weird with variadic functions (it's most certainly due to my lack of knowledge though)
So I have that piece of code that works :
void Text::textTypePtr(Root* text, ...)
{
va_list args;
va_start(args, text);
Interact::ModifiedKey modKey = va_arg(args, Interact::ModifiedKey);
Keyboard::Key key = va_arg(args, Keyboard::Key);
static_cast<Text*>(text)->textType(modKey, key);
va_end(args);
}
and then, this piece of code that reverses the args order
void Text::textTypePtr(Root* text, ...)
{
va_list args;
va_start(args, text);
static_cast<Text*>(text)->textType(va_arg(args, Interact::ModifiedKey), va_arg(args, Keyboard::Key));
va_end(args);
}
I'm now scared that the first piece of code worked by some miracle, can someone maybe help me to understand what's going on?
The first version is correct. Statements are executed in order, so you can be confident the first variadic argument will be assigned to modKey and the second will be assigned to key.
The second version is depending on unspecified behavior. The relative order of evaluation of arguments to a function is unspecified. So it can evaluate either va_arg() expression first, which means that it could assign the wrong variadic argument to each parameter of the textType() function.
I have a C++ class that is the frontend for a logging system. Its logging function is implemented using C++11's variadic templates:
template <typename... Args>
void Frontend::log(const char *fmt, Args&&... args) {
backend->true_log(fmt, std::forward<Args>(args)...);
}
Each logging backend implements its own version of true_log, that, among other things, uses the forwarded parameters to call vsnprintf. E.g.:
void Backend::true_log(const char *fmt, ...) {
// other stuff..
va_list ap;
va_start(ap, fmt);
vsnprintf(buffer, buffer_length, fmt, ap);
va_end(ap);
// other stuff..
}
Everything works great, and I am happy.
Now, I want to add a static check on the log() parameters: specifically, I would like to use GCC's printf format attribute.
I started by tagging the log() function with __attribute__ ((format (printf, 2, 3))) (as this is the first "hidden" parameter, I need to shift parameter indices by one). This does not work, because if fails with a compilation error:
error: args to be formatted is not ‘...’
Then, I tried to add the same attribute to the true_log() function. It compiles, but no error checking is actually performed: I tried to pass to log() some invalid format/variable combinations, and no warning was issued. Maybe this kind of check is "too late", or, in other words, the information about the variable has been lost in the chain of calls?
As a last resort, if I annotated log() with __attribute__ ((format (printf, 2, 0))), I would receive warnings about wrong format strings, but no diagnostic would be issued for invalid format/variable combinations.
Summarizing the problem: how can I have full format checking from GCC if I use C++11's variadic templates?
I don't believe you can. I bet that GCC only verifies the format string if it's a literal. This is why putting the format attribute on true_log doesn't work - that function is called with what looks (syntactically) like a runtime-determined string. Putting it on log directly would circumvent that, but would require format attributes to support variadic template, which you proved it doesn't.
I suggest that you look at more C++-ish ways to do formatted output. There is, for example, boost::format which works kind of like printf, but dynamically verifies that the number and types of the parameters types match the format string. It doesn't use variadic templates, though, but instead consumes parameters fed to it (via operator %) one-by-one.
For the record, I ended up removing the C++11 variadic templates altogether, and using a traditional va_list.
__attribute__((format(printf, 2, 3)))
void Frontend::log(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
backend->true_log(fmt, ap);
va_end(ap);
}
void Backend::true_log(const char *fmt, va_list ap) {
// log the message somehow
}
There is a workaround if you are willing to use a macro.
There are constructs that will cause the compiler to do the checking for you, but will not generate any called code. One such construct is sizeof. So, you could use a macro for your logger to pass the arguments to printf directly but in the context of a sizeof calculation, and then call the logger itself.
The reason to use a macro is to make sure the format string is treated just like a string literal would be treated.
In the illustration below, I treat the sizeof calculation as a throwaway argument, but there should be other ways to apply the same technique.
template <typename... Ts>
void Frontend::log(size_t, const char *fmt, Ts&&... args) {
backend->true_log(fmt, std::forward<Ts>(args)...);
}
#define log(...) log(sizeof(printf(__VA_ARGS__)), __VA_ARGS__)
Try it online!
Of course, this is a workaround. There are numerous reasons not to use a macro. And in this case, the log macro would interfere with any other function or method with the same name.
I have been reading that some compilers support va_list with macros and users were able to overload the functionality with other macros in order to count the va_list.
With visual studio, is there a way to determine if the va_list is empty (aka count==0)? Basically I would like to know this condition:
extern void Foo(const char* psz, ...);
void Test()
{
Foo("My String"); // No params were passed
}
My initial thought was to do something like this:
va_list vaStart;
va_list vaEnd;
va_start(vaStart, psz);
va_end(vaEnd);
if (vaStart == vaEnd) ...
The problem is that va_end only sets the param to null.
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap) ( ap = (va_list)0 )
I was thinking of maybe incorporating a terminator but I would want it to be hidden from the caller so that existing code doesnt need to be changed.
There is no way to tell how many arguments are passed through ..., nor what type they are. Variadic function parameters can only be used if you have some other way (e.g. a printf-style format string) to tell the function what to expect; and even then there is no way to validate the arguments.
C++11 provides type-safe variadic templates. I don't know whether your compiler supports these, or whether they would be appropriate for your problem.
I realize this question is fairly old, but I thought it might be helpful to flesh it out a bit. As Mike Seymour answered quite correctly, there is no absolutely reliable way to determine the number of arguments in a va_list. That's why the conventional way to define a variadic function is to include a parameter that has that information, like so: void func(const char *str, int count, ...);, which defines a contract your callers are supposed to abide by.
EDIT: The standard (7.16.1.1.3) is actually silent on the value returned by va_arg(vl, type) for any call past the end of the variable argument list. The most common case for most types is typed-zero. However, CAVEAT EMPTOR - it doesn't have to be.
The value returned from va_arg(vl, type) when there are no more arguments is a typed-zero value. For numeric types, it is 0. For pointers, it is a NULL pointer. For structs, it is a struct with all fields zeroed. If you elect to copy the va_list and try to count the copy, like so:
void func(const char *str, ...) {
va_list vl;
va_list vc;
int x;
int count;
count = 0;
va_start(vl, str);
va_copy(vc, vl);
do {
x = va_arg(vc, int);
if (x == 0) break;
count++;
} while (1)
va_end(vc);
.
.
. // do something, or something else,
. // based on the number of args in the list
.
va_end(vl);
You would have to make the assumption that the caller would abide by the contract not to pass a NULL or zero value in the list. Either way, you have to understand that the caller of a variadic function is responsible for abiding by the stated contract. So write your function, publish the contract, and sleep easy.
I am aware that my answer isn't an "orthodox" answer, however due to the limitation of the va_list macro, I found the following solution to work. We can look at a va_list as an array of chars, and even better, a null terminated one, so you can try
va_list argptr;
if(strcmp(argptr,""))
{
// not empty
}
else
{
// empty
}
I tried that and it worked for me.
I used Visual Studio 2013 Ultimate. The project type is Win32 Console Application. No compilation errors.
A (very long) while ago I regularly used the following code - then on MSVC 6 - to determine the memory needed to format a string for a function with variadic arguments:
void LogPrint(const char *pszFormat, ...)
{
int nBytes;
char *pszBuffer;
va_list args;
va_start(args, pszFormat);
nBytes = vsnprintf(0, 0, pszFormat, va);
va_end(args);
// error checking omitted for brevity
pszBuffer = new char[nBytes + 1];
va_start(args, pszFormat);
vsnprintf(pszBuffer, nBytes, pszFormat, va);
va_end();
// ...
}
The obvious error you're getting in a more recent version of MSVC (I'm using 2010 now) is:
warning C4996: 'vsnprintf': This function or variable may be unsafe. Consider using vsnprintf_s instead. To disable deprecation use _CRT_SECURE_NO_WARNINGS. See online help for details.
I'm a big fan of the "treat warnings as errors" option for any C(++)-compiler, and obviously my build fails. It feels like cheating to me to simply employ #pragma warning (disable:4996) and get on with it.
The suggested "safer" alternative vsnprintf_s(), however is doomed to return -1 when input conditions of its "unsafe" predecessor occur.
TL/DR: Is there a way to implement the expected behavior of vsnprintf() to return the memory needed to fulfil its task using the new, safer variants of it?
EDIT: simply defining _CRT_SECURE_NO_WARNINGS won't cut it; there's a lot of strcpy() flying around, too. The new variant of which isn't broken, so I'd like to still see these.
The function you want to look at is _vscprintf, which "returns the number of characters that would be generated if the string pointed to by the list of arguments was printed or sent to a file or buffer using the specified formatting codes". There's a widechar variant (_vscwprintf) as well.
I have a logging function that takes in a variable number of arguments and uses _vsnprintf to format them. My problem is that when I debug my OCR automation the string it returns is sent to the log, so if the file said something like this:
This bitmap says %n
then it would get sent to my logging function like this:
void log(LPCSTR msg, ...)
{
char log[MAX_ALLOWED];
int length = sizeof(log) / sizeof(log[0]);
va_list argptr;
va_start( argptr, pzMsg );
// our msg accidentally has a %
if ( strchr(msg, '%') ) {
// debug assertion - no parameters were passed
_vsnprintf( log, length, msg, argptr );
}
log[length-1] = (char)0;
va_end( arg_ptr );
}
is there a way, along with the check for '%' that i can check if there were no arguments? thank you.
The traditional way to make sure something can't be expanded by printf is
log("%s", yourString);
Of course, you could also add a variant of log that only takes one argument, or you could count the number of variable arguments and not format the string if there aren't any.
If I understand you correctly, you would like to check the number of arguments actually passed to log().
Unfortunately, this is highly machine-specific. I know of only one architecture which provides an unambiguous argument count. That is the VAX. All the others depend on the caller and the callee to "get it right".