Varargs? More like Arghargs!

This was a silly mistake on my part, but one which took me several hours to track down. In retrospect, I had everything at my disposal to tell me exactly what the problem was, I just didn’t notice it. It had to do with a varargs method pair for performing some logging operations. I had one logging method, and one helper logging method, both accessible to the caller. It turns out this was a bad idea.

My code looked like this:

static void Log( const wchar_t *format, va_list args ) {
  // Validation stuff removed

  // Write the arguments out to a string
  std::vector< wchar_t > str;
  str.resize( ::_vscwprintf( format, args ) + 1 );
  ::vswprintf_s( &str[ 0 ], str.size(), format, args );

  // Shove the string out to the file
  DWORD bytesWritten = 0;
  ::WriteFile( sLogFile, &str[ 0 ], str.size() * sizeof(wchar_t ), 
    &bytesWritten, NULL );

// Actual logging function
static void Log( const wchar_t *format, ... ) {
  va_list list;
  va_start( list, format );
  Log( false, format, list );
  va_end( list );

When using variable argument lists, I like splitting the varargs list off into its own helper function. I find it’s far cleaner to read since you don’t have that distracting … in the method signature.

However, this particular method pair turned out to be a huge problem that caused my code to crash any time I made use of the variable argument list to pass in a string. Can you guess what the problem was? Here’s an example of the problem:

const char *someData;  // Comes from somewhere
Log( L"The data is %S", someData );

The problem was basically stupidity of my own making. The two methods were named the same, so one is an overload of the other. However, on Windows, a va_list is a char * — so the varargs method was not being called. Instead, the va_list method was being called, and the parameter for the list was invalid!

There are many different ways to solve this problem:

  • Don’t overload the method name
  • Don’t use a helper method at all, but just put all the logic into Log itself
  • Don’t make the helper method visible to callers
  • Don’t have the methods share a common first parameter

This should not have taken me so long to solve the problem, but how I eventually noticed it was that my crash log only had one call to Log on the call stack when there should have been two. It just goes to show that debugging is not an automatic process, but really does require you to pay attention to all the information provided.

At least I’ve solved the issue and am no longer blocked. Unfortunately, I wasted more time than I care to admit solving it! Oh well, it’s a Monday somewhere in the world (namely, here!).

This entry was posted in C/C++. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *