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".
Related
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.
I'm writing a logger in C++ and to simplify entering lines I use vsnprintf function to build log line
void CLogger::RegManLog(const LogLevel & logLevelMask, char * Format, ...)
...
...
va_start(marker_, Format);
vsnprintf(buffer_ ,MaxLogBuffSize , Format, marker_)
va_end(marker_);
printer_ += buffer_;
...
...
every thing works great up until I accidently enter a number into a string
integer test = 10;
eg.: "now I'll show a string %s" , test
tried to add "try and catch", but I think vsnprintf does have throw, so it crashes any way.
tried to get return value from vsnprintf, it return value, while string are fine, when reaching the same issue, it crashes
any thoughts of I can i solve this issue?
thanks
%s expects to get a char*. When you pass in 10, it treats it like an address, goes there and kills your program.
If you wish to print integers, use %d. For more information look at http://pubs.opengroup.org/onlinepubs/009695399/functions/printf.html
In C++ it is best to use other methods to accomplish what you need, like std::stringstream
You could avoid printf and friends and instead use std::stringstream or boost::format
Well, when passed to printf() and family, the %s formatter is here as a placeholder for a -eventually const- char * pointer.
What is happening, is that your integer is read as a pointer, and it's likely that the memory address (10 in your example) is invalid.
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I haven't been able to find an answer to the following question, and I am having some issues related to the functions.
My main programming is done in C#, and never really learned C++ while studying, but in my current job I have to do some C++ programming aswell.
Most of the C++ programming have been done by a former employee, and he made a function for logging.
Once in a while this function results in an error (Access Violation) - this is not shown to the user, but I see it while running the code through debugger.
When the error happens it points to this line of code:
vfprintf( LogFile, fmt, va );
I then took a closer look at the code before and after, and to put the above into a context the code around is:
void FileLog( char *fmt, ... )
{
va_list va;
struct time t;
struct date d;
long clk;
static int ReEntrant = 0;
if( FileLogEnabled == false )
return;
ReEntrant++;
if( ReEntrant > 1 )
return;
if( LogFile == NULL )
LogFile = fopen( LogFileName, "a+" );
if( LogFile != NULL )
{
gettime( &t );
getdate( &d );
fprintf( LogFile, "\n%d-%02d-%02d %2d:%02d:%02d.%02d0> ", d.da_year, d.da_mon, d.da_day, t.ti_hour, t.ti_min, t.ti_sec, t.ti_hund );
va_start( va, fmt );
vfprintf( LogFile, fmt, va );
va_end( va );
fflush( LogFile );
...
}
ReEntrant = 0;
}
Actually I dont understand why it is needed (and if it is?) call both fprintf and then vfprintf? I would think the first fprintf call would write the formatted string to the stream (File), and that would be enough?
A little explanation or some information would be much appreciated :)
EDIT: After the comment from nos - I tracked down the specific call to the function which today have often been causing this error.
FileLog( "TimerRestore[%d], Name=%s", Package.CurGame->Timers[ Index ].Name.c_str() );
I indeed think this could cause some trouble, as "TimerRestore[%d], Name=%s" should be followed by a decimal and string arguemtn, however only a string argument is given. I need to do some testing, but Im sure the author who wrote this code meant to write:
FileLog( "TimerRestore[%d], Name=%s", Index, Package.CurGame->Timers[ Index ].Name.c_str() );
However I still do not understand why the function call does not always seem to result in an error. Or maybe it is cause of the "ReEntrant" variable in FileLog function is blocking it when it does not fail?
Thanks a lot for all the feedback and information.
vprintf() (and friends) allow to use va_list as argument, which is useful when your function has a variable amount of arguments:
void log(FILE *file, const char* format, ... )
{
va_list args;
va_start (args, format);
fprintf(file, "%s: ", getTimestamp());
vfprintf (file, format, args);
va_end (args);
}
In your application you can call this function with a variable amount of arguments:
log(file, "i=%d\n", i); // 3 arguments
log(file, "x=%d, y=%d\n", x, y); // 4 arguments
I do not know why your function results in an error. Your code snippet does not provide enough details. The amount of type of provided function arguments might be the cause.
First off, using fprintf() and (especially) vfprintf() in C++ is evil.
To the point: fprintf() is a variadic function, it accepts an arbitrary number of arguments. Internally, variadic functions are implemented by "unpacking" the variadic arguments using va_list, va_start() and va_end().
vfprintf() is used when you want to access the functionality of fprintf() from your own variadic function after you've unpacked your own variadic arguments (that is, you have access to a va_list instance). vfprintf() is not variadic; it accepts a va_list storing the arguments.
You haven't posted the declaration of your function calling fprintf() and vfprintf(), but we can assume it's variadic. It first uses fprintf() to print some data into the LogFile, and then uses vfprintf() to print its own variadic arguments there.
vfprintf Allows you to have a variable argument list which means that you can create your list of parameters dynamically at runtime.
This is pretty common for logging. You want to make a printf-style log message like:
Log("The value of x is now %d", x);
But that requires variable arguments. So you need vfprintf. The reason fprintf is used as well is because it wants to write a date/time stamp and you can't add that extra stuff to the existing format passed to vfprintf.
Another way to do it is to use the string version vsprintf, and make one large string, then write it to file. But that's more prone to errors (like buffer overflow).
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.
Here is my code:
void subroutine(const char *message) { printf(message); }
And here is the error I get:
Error: In function ' ': warning: format not a string literal and no format arguements [-Wformat-security]
What is the error here? I can't solve it.
Any suggestions?
You should use
printf("%s", message);
Longer explanation:
printf treats its first argument as format specifier. If you are lucky and the message doesn't contain %s or other substrings special for printf, the message will be printed "as is".
But if the message contains something like that, your program will try to interpret other arguments to printf as the parameters. As there are no actual arguments, it will, for example, consider some arbitrary memory location as a pointer, and try to dereference it. This would in the best case lead to a crash; in the worst case, this may leak some sensitive data.
(printf can even overwrite some memory if %n is encountered in the format string.)
You can solve it by doing
printf("%s", message);
Or use something else, such as fputs() instead of printf.
gcc warns you because it doesn't know the format string you supply to printf, and thus the arguments cannot be verified.
Imagine you call your function like
message("It's 100%s");
That ends up being printf("It's 100%s"); , which is wrong and ends up causing undefined behavio since the format string contains a %s, and you need to supply an additional argument to printf that's a string..
If you pass "%d" to subroutine, you're going to be in big trouble since printf will look for another argument.
The compiler kindly warns you that if message contains format specification, the program may crash.
You can use vprintf if you intend to write something like
void subroutine(const char *message, ...)
{
va_list args;
va_start(args, format);
vprintf(message, args);
va_end(args);
}
but if you just want to display message, use puts(message) or printf("%s", message).