Can I return early from a variable-argument function?
Suppose I have two C++ functions for debug output:
void Trace( const wchar_t* format, ... )
{
va_list args;
va_start( args, format );
VarArgTrace( format, args );
va_end( args );
}
void VarArgTrace( const wchar_t* format, va_list args )
{
WCHAR buffer[1024];
//use ::_vsnwprintf_s to format the string
::OutputDebugS开发者_运维技巧tringW( buffer );
}
the above uses Win32 OutputDebugStringW()
, but it doesn't really matter. Now I want to optimize the formatting so that when there's no debugger attached formatting is not done (I measured - speedup is significant):
void Trace( const wchar_t* format, ... )
{
if( !IsDebuggerPresent() ) {
return;
}
//proceed as previously
va_list args;
.....
}
will the fact that I return early once IsDebuggerPresent()
returns null affect anything except that formatting will be skipped?
I mean I no longer call va_start
and va_end
- will this matter? Will skipping va_start
and va_end
cause any unexpected behavior changes?
No, there is no obligation to use va_start in a varargs function.
If you don't use va_start you cannot use va_end; if you use va_start you should use va_end, no matter how the function returns.
The only requirement on an early return is that if you have used (executed) va_start()
, you must use va_end()
before you return.
If you flout this rule, you'll get away with it on most systems, but some system somewhere needs the va_end()
, so don't risk omitting it. It is undefined behaviour to omit it.
Other than that rule, it is up to you how you handle your return. Your proposed early return is not a problem.
All those macros do (on Windows at least) are pointer manipulation for a bookmark in the arg list. Returning early will be fine. This is compiler-specific, though I can't imagine why early return would be a problem on other platforms.
From x86 vadefs.h
:
#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 )
That's quite safe, because in C languages, the job of 'cleaning' the passed parameters from the stack is always done by the calling function, not the called one.
Some languages (Pascal springs to mind) may do this the other way round. This is potentially more efficient, but only works because there's no concept of a variable number of arguments.
There's not much overhead in the varargs stuff, usually just a few pointer manipulations. There's probably slightly more overhead in setting up the stack to each varargs call, so instead of
void Trace( const wchar_t* format, ... ) { if( !IsDebuggerPresent() ) { return; } //proceed as previously va_list args; ..... }
you're probably better off using the preprocessor to prevent calls to trace stuff in non debug builds.
Littering your code with
if( IsDebuggerPresent() ) Trace( stuff... );
seems quite distateful too.
Did VC2010 get variadic macros yet? :)
精彩评论