Thursday, March 10, 2011

Overloading and varargs

I learned something important today: don't overload a variadic function.

It's very tempting to have functions like this in your C++ class:

class Foo {
public:
  void write(const char* format, ...);
  void write(const chat* format, va_list args);
}

The variadic function would be implemented like this:

void Foo::write(const char* format, ...) {
  va_list args;
  va_start(args, format);
  write(format, args);
  va_end(args);
}

It looks nice and clean -- a perfect application of overloading.

However there's a subtle problem! The C++ compiler will always try to resolve the method with the most specific signature when it encounters an overloaded call.

Calls like this are fine:

foo->write("%s\n", "Hello");
foo->write("...world");

But what if you have an argument which looks like a va_list? For example, if va_list is a pointer on your platform, what does this line do?

foo->write("How many chickens? Answer: %d\n", 0);

In C++ 0 isn't just a number. It's also the NULL pointer. The compiler will decide that you're actually calling the non-variadic function and will use NULL for the va_list argument. Then your program will crash when you try to read an int argument from a NULL va_list.

Lesson learned. Don't overload functions if one of the functions is variadic.

Now I have to go back and fix some code I wrote yesterday...

No comments:

Post a Comment