Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function overloading where parameters only differ by ellipses

I've got this logging system for which I'm looking to shortcut some of the string manipulation.

The logging system is used via functional macros which then forward to a single function call. E.g. #define Warning(...) LogMessage(eWarning, __VA_ARGS__);.

LogMessage then does a snprintf into a new buffer and then presents that message to whatever log targets happen to be installed; printf, OutputDebugString, etc.

Unfortunately, I've run into a problem where the buffer that we have isn't big enough, so the output gets truncated. I also realized that this method will fail if the output message has percent symbols in it, as snprintf will try to process the va_args. Finally, as the majority of our log messages do not use the va_args, it seems silly to copy the string just to present it to the loggers.

So- given my function prototype, should I be able to overload based on the presence of the ellipses? In other words, should I be able to assume that I can do something like:

LogMessage(LogLevel, const char* message, ...);
LogMessage(LogLevel, const char* message);

My google attempts haven't yielded anything particularly useful (just showing me that ellipses will match if nothing else does, varying from my requirements that nothing matches), and my initial stab at an implementation just gave me an ambiguous function call error.

With the error, I should just accept that I can't do this, but I'm wondering if it's just the compiler I'm using or if maybe I'm doing it wrong. I can achieve a similar effect with

// edited version of what I really have to remove our local APIs,
// please excuse minor errors
const char* message = NULL;
char buffer[512];

va_list args;
va_start(args, format);

if(strcmp(format, "%s") == 0) {
    message = va_arg(args, const char*);
}
else if (strchr(format, '%') == NULL) {
    message = format;
}
else {
    vsnprintf(buffer, 512, format, args);
    message = buffer;
}

va_end(args);

...but this seems wasteful in the typical case which can be known simply by the number of parameters being passed. E.g. if ellipses don't match anything, select the other function? If this doesn't work, is there another method I can try that doesn't require the user to decide with the macro name which function will be called? Honestly it's not even as much about the "waste" once I realized that if someone haphazardly said Error("Buffer not 100% full"); in their log message and got "Buffer not 1007.732873e10ull" as a result.

Edit: While my example has been answered by a "don't do that," can the question itself be answered?

like image 441
dash-tom-bang Avatar asked Sep 02 '10 00:09

dash-tom-bang


People also ask

Can two overloaded functions differ only in the return type?

Function overloading and return type in C++You cannot overload function declarations that differ only by return type. The function overloading is basically the compile time polymorphism. It checks the function signature.

What is ambiguity in function overloading?

When the compiler is unable to decide which function it should invoke first among the overloaded functions, this situation is known as function overloading ambiguity. The compiler does not run the program if it shows ambiguity error.

How are overloaded functions differentiated?

Overloaded functions differentiate between argument types that take different initializers. Therefore, an argument of a given type and a reference to that type are considered the same for the purposes of overloading. They're considered the same because they take the same initializers.

What are the different types of function overloading?

There are mainly two types of overloading, i.e. function overloading and operator overloading. Function overloading improves the code readability, thus keeping the same name for the same action.


2 Answers

I was inspired by the original answer to this question, but have come up with a slight improvement.

static void LogMessage(LogLevel level, const char* message);

template <typename T>
static void LogMessage(LogLevel level, const char* format, T t, ...)
{
    LogMessageVA(level, format, (va_list)&t);
}

static void LogMessageVA(LogLevel level, const char* format, va_list argptr);

This works without having to 'assume' that the second argument is const char*.

like image 90
Jesse Elliott Avatar answered Oct 12 '22 16:10

Jesse Elliott


I also realized that this method will fail if the output message has percent symbols in it, as snprintf will try to process the va_args.

Then caller beware. If your function is documented to take printf-style format strings then it is the caller's responsibility to escape any percent signs. It is not really your job to attempt to handle invalid format strings.

Honestly it's not even as much about the "waste" once I realized that if someone haphazardly said Error("Buffer not 100% full"); in their log message and got "Buffer not 1007.732873e10ull" as a result.

I think you're better off going along with the C++ ethos. In Java methods commonly check for valid arguments and throw exceptions when passed invalid values. In C++ you simply let callers shoot themselves in the foot. It's better to have them write 100%% than to jump through hoops to protect them from learning how to call your function properly.

like image 35
John Kugelman Avatar answered Oct 12 '22 15:10

John Kugelman