We have a huge set of functions exposed as an external API and we need to trace all argument values in each call. Currently we have a macro that accepts the list of all parameters:
void SomeApiFunction( bool parameter1, const wchar_t* parameter2 )
{
LOG_PARAMETERS( parameter1 << parameter2 )
//function payload
}
The macro expands as follows:
#define LOG_PARAMETERS( x ) GlobalLoggerObject << x;
and GlobalLoggerObject
is of type class CLogger
that has operator <<
overloaded for all possible parameter types - something like this:
template<class TypeToLog>
CLogger& operator << (const TypeToLog& parameter)
{
//logging code
return *this;
}
so that we can have a parameters list of any length and thanks to a chain of <<
they can be all passed into the macro and necessary concrete versions of operator <<
are instantiated depending on the type of the parameter.
Great except we have to maintain the list of parameters fully synchronous to that of the surrounding function.
Is there some way to just grab all the parameters at once - something like this:
TRACE_ALL_PARAMETERS( UNIVERSAL_MAGIC )
so that we don't have to list each parameter explicitly?
There is no way to do it in C/C++, but it can be accomplished with external code generation.
TRACE_ALL_PARAMETERS()
(with empty arguments list) when needed./^\s*TRACE_ALL_PARAMETERS\(/
, parses out the nearest list of parameter names, and replaces everything in parentheses following TRACE_ALL_PARAMETERS
with extracted parameter list.This script can be invoked as a custom build step or as an IDE macro.
That said, it doesn't seem to me a worthwhile investment of time and complication of the code management process. From my personal experience, it is rarely needed to just print all function parameters. Usually one needs to specify custom formatting for some parameters, access some parameters in a special way (e.g. it makes no sense to print a MyStruct*
as a pointer, usually I'd like to print p_my_struct->field_1
and p_my_struct->field_2
instead), etc.
So I'd keep the manual logging, and use an external tool for wholesale tracing if needed. E.g. on Windows, WinAPIOverride32 can be used, though with additional work to describe functions in the format it understands (I did it once, using GCC-XML to generate function descriptions).
If this is really a problem, one idea might be to add a runtime check for your unit tests.
As Nawaz mentions in one comment, there is the __FUNCSIG__
macro, that lists all paremeter types. For example for my main it expands to "int __cdecl main(int,char *[])"
.
Now, what you could do is to use this string to do a runtime check of the number (maybe even types) of parameters logged and if you have a mismatch use some mechanism of your test framework to signal that the function's logging is "broken".
As for the problem with not being able to split the parameters being passed to the logging macro: I'd try something like this:
#define LOG_WITH_CHECK(ValidationString, Params) \
cout << "Validate [" ValidationString << "] vs. [" << #Params << "]\n"; \
cout << "Now feed params to logging: {" << Params << "}"; \
/**/
int main(int argc, char* argv[])
{
LOG_WITH_CHECK(__FUNCSIG__, argc << argv[0]);
...
That is, you also validate the passed parameters as a <<
separated string.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With