I have the following function that I shouldn't change to not break dependencies
void Trace(
__in DbgInfo dbgInfo,
__in_z _Printf_format_string_ const WCHAR* pwszMsgFormat,
...) {
// some code I need
};
I need it to call a function that also has ... args:
#define TRACE_LINE( format, ... ) _TRACE( LogLevelInfo ,format, __VA_ARGS__ )
#define _TRACE( mask, format, ... ) \
((mask) & g_LogLevel) \
? TraceGlobal::TraceLine( mask, L"[" __FILEW__ L":" _BLOB_WIDE(_BLOB_STRINGIZE(__LINE__)) L"] " L"MyService!" __FUNCTIONW__ L": " format , __VA_ARGS__ ) \
: (void)0
void
ServiceTracing::TraceGlobal::TraceLine(
_In_ LogLevel level,
_In_z_ PCWSTR format,
_In_ ...)
/**
Routine Description:
Trace a formatted line with Level tracing
Arguments:
level: enum level of trace line
format: the string format
...: the variable arguments to be formatted
Return value:
None
**/
{
ASSERT(format);
va_list args;
va_start(args, format);
// code does tracing
va_end(args);
}
how can I do that?
Inside the main Trace function above I tried this:
TRACE_LINE( pwszMsgFormat, __VA_ARGS__ );
this didn't work however and I got error:
syntax error : missing ')' before identifier 'pwszMsgFormat'
error C2059: syntax error : ')'
I am assuming because it is macro the __VA_ARGS__ it gives this error.
On the other hand I tried calling the TraceGlobal directly
va_list args;
va_start(args, pwszMsgFormat);
TraceGlobal::TraceLine(LogLevelInfo, pwszMsgFormat , args );
va_end(args);
this builds fine but I get Segmentation Fault / Access Violation in the tracing function because I also initialize va_list and use va_start in TraceLine
Is there a simple way I can just pass along the variable arguments from function to another. I don't want to change Trace function to Macro.
Related
I try to embed a code block through the use of macro like this:
#define RUN_CODE_SNIPPET(c) do {\
c\
} while(0);
where 'c' is a code block enclosed inside '{ }'
Here is how to use it
#include <stdio.h>
#define RUN_CODE_SNIPPET(c) do {\
c\
} while(0);
int main(int argc, char *argv[]) {
RUN_CODE_SNIPPET({
//const char *message = "World";
const char message[] = {'w', 'o', 'r', 'l', 'd', '\0'};
printf("%s\r\n", message);
});
return 0;
}
You can run it here here
But I get compiler error when I use the initializer list format
test.c: In function ‘main’:
test.c:13:4: error: macro "RUN_CODE_SNIPPET" passed 6 arguments, but takes just 1
});
^
test.c:9:3: error: ‘RUN_CODE_SNIPPET’ undeclared (first use in this function)
RUN_CODE_SNIPPET({
^~~~~~~~~~~~~~~~
test.c:9:3: note: each undeclared identifier is reported only once for each
function it appears in
Seems the compiler is taking each element in the initializer list as the argument to the macro itself. The string initializer works fine.
What is wrong here?
The commas in what you pass inside the parentheses are interpreted as macro argument separators and the macro is expecting just one argument.
There are two ways around the problem:
parenthesize the commas-containing argument, i.e., pass (a,b,c) instead of a,b,c (not applicable in your case because your argument is not an expression)
use variadic macro arguments (... -> __VA_ARGS__)
In other words:
#define RUN_CODE_SNIPPET(...) do { __VA_ARGS__; }while(0)
will work (including the semicolon at the end of the macro is not advisable -- for a function-like macro, you should generally be able to do if(X) MACRO(something); else {} and the semicolon would mess that up).
I would like to turn on -Wdouble-promotion flag on my embedded project to avoid any accidental use of doubles. It was very useful however I can't fix my logger component because it uses __VA_ARGS__:
void log( const LogLevel _level, const char* const _file, const int _line, const char* const _format, ... )
{
va_list args;
va_start( args, _format );
Com< ILogger >()->Log( _level, _file, _line, _format, args );
va_end(args);
}
#define LOG_CRITICAL(...) log(LogLevel_Critical, __FILE__, __LINE__, __VA_ARGS__)
When I use stdarg.h or __VA_ARGS__ my floats are promoted into doubles (it is the standard behavior)...
base/logger.h, line 35, column 76: warning: implicit conversion from ‘float’ to ‘double’ when passing argument to function [-Wdouble-promotion]
#define LOG_WARN(...) log(LogLevel_Warning, __FILE__, __LINE__, __VA_ARGS__)
^
base/ntp.cpp, line 91, column 6: note: in expansion of macro ‘LOG_WARN’
LOG_WARN("System time is stepped by NTP daemon! (%f sec)", offset);
^~~~~~~~
It is an unfortunate situation, however it would be a really hard work to modify my logger component not to use printf in the background so I decided to suppress this error in this header:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdouble-promotion"
//(...) my logger component
#pragma GCC diagnostic pop
But it was unsuccessful because the warning is triggered when the compiler elaborates the #define LOG_CRITICAL(...) log(LogLevel_Critical, __FILE__, __LINE__, __VA_ARGS__) macro in the caller file.
How can I suppress the double-promotion warning of the variadic macro? GCC specific solutions are OK too.
Update 1:
I have checked alk's solution:
#define LOG_CRITICAL(...) \
_Pragma( "GCC diagnostic push" ) \
_Pragma( "GCC diagnostic ignored \"-Wdouble-promotion\"" ) \
log(LogLevel_Critical, __FILE__, __LINE__, __VA_ARGS__) \
_Pragma( "GCC diagnostic pop" )
After the modification there are many errors:
core/app_health.cpp, line 118, column 1: error: ‘#pragma’ is not allowed here
LOG_ERROR("[Main]");
I think this kind of pragma isn't allowed inside functions...
I have a warning in my code that is driving me crazy:
int vasprintf_wrapper(char** bufptr, const char* fmt, va_list ap)
{
// Do stuff...
// ...
return vasprintf(bufptr, fmt, ap);
}
Clang (3.6.0), complains with "format string is not a string literal", referring to the fmt argument that is being forwarded.
Naively, I tried to:
return vasprintf(bufptr, reinterpret_cast<const char[]>(fmt), ap);
Which of course doesn't compile.
What do I do? Disabling the warning altogether is not an option. I want to have the warning. But in this case, I would like to tell the compiler that I know what I'm doing ("famous last words" jokes aside...)
Indicate a parameter is a printf-style format using the __attribute__ flag. For example:
__attribute__((__format__ (__printf__, 2, 0)))
int vasprintf_wrapper(char** bufptr, const char* fmt, va_list ap)
{
...
}
The last parameter (0) disables checking for va_list.
From the documentation:
format (archetype, string-index, first-to-check)
The format attribute specifies that a function takes printf-, scanf-, strftime-, or strfmon-style arguments that should be type-checked against a format string.
The parameter archetype determines how the format string is interpreted.
The parameter string-index specifies which argument is the format string argument (starting from 1).
The parameter first-to-check is the number of the first argument to check against the format string. For functions where the arguments are not available to be checked (such as vprintf), specify the third parameter as zero.
See also:
http://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
http://clang.llvm.org/docs/AttributeReference.html#format
The warning flag that enables this type of warning is -Wformat-nonliteral. Since you don't want to turn that warning off completely, you can locally disable this warning using the following code:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
...
#pragma clang diagnostic pop
So your function would look like this:
int vasprintf_wrapper(char** bufptr, const char* fmt, va_list ap)
{
// Do stuff...
// ...
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
return vasprintf(bufptr, fmt, ap);
#pragma clang diagnostic pop
}
I'm trying to build the following code in Eclipse CDT on Linux with GNU:
log_defs.h
#pragma once
#define dbg_log(fmt, ...) debug_logger::debug(__FILE__, __FUNCTION__, __LINE__, fmt, __VA_ARGS__, 0)
ErrorMessage.cpp
ErrorMessage::ErrorMessage( void ){ dbg_log( L"ErrorMessage::ErrorMessage _in" ); }
ErrorMessage::~ErrorMessage( void ){ dbg_log( L"ErrorMessage::~ErrorMessage _in" ); }
I'm getting the following errors:
../sources/ErrorMessage.cpp: In constructor ‘ErrorMessage::ErrorMessage()’:
/include/log_defs.h:27:97: error: expected primary-expression before ‘,’ token
#define dbg_log(fmt, ...) debug_logger::debug(__FILE__, __FUNCTION__, __LINE__, fmt, __VA_ARGS__, 0)
^
../sources/ErrorMessage.cpp:150:37: note: in expansion of macro ‘dbg_log’
ErrorMessage::ErrorMessage( void ){ dbg_log( L"ErrorMessage::ErrorMessage _in" ); }
^
../sources/ErrorMessage.cpp: In destructor ‘ErrorMessage::~ErrorMessage()’:
/include/log_defs.h:27:97: error: expected primary-expression before ‘,’ token
#define dbg_log(fmt, ...) debug_logger::debug(__FILE__, __FUNCTION__, __LINE__, fmt, __VA_ARGS__, 0)
^
../sources/ErrorMessage.cpp:152:38: note: in expansion of macro ‘dbg_log’
ErrorMessage::~ErrorMessage( void ){ dbg_log( L"ErrorMessage::~ErrorMessage _in" ); }
The reason is that when you don't pass any additional arguments to the macro, __VA_ARGS__ expands to nothing. You therefore end up with the following code like this after macro expansion:
debug_logger::debug("SomeFile", "SomeFunction", 42, L"TheFormatString", , 0)
If the code you've posted faithfully captures your real scenario, the way out is easy: subsume fmt into the variadic part:
#define dbg_log(...) debug_logger::debug(__FILE__, __FUNCTION__, __LINE__, __VA_ARGS__, 0)
That way, you'll always pass at least one argument (the fmt string) to the macro, and the commas will work out nicely.
I'm trying to create LOGDEBUG macro:
#ifdef DEBUG
#define DEBUG_TEST 1
#else
#define DEBUG_TEST 0
#endif
#define LOGDEBUG(...) do { if (DEBUG_TEST) syslog(LOG_MAKEPRI(LOG_SYSLOG, LOG_DEBUG), __VA_ARGS__); } while (0)
...
size_t haystack_len = fminl(max_haystack_len, strlen(haystack_start));
LOGDEBUG(("haystack_len %ld\n", haystack_len));
I am not using # or ## parameters to stringify the arguments, and yet g++ apparently tries to stringify them:
numexpr/interpreter.cpp:534:5: error: invalid conversion from ‘size_t {aka long unsigned int}’ to ‘const char*’ [-fpermissive]
Note that haystack_len is size_t and I do not convert it to char* in the macro, yet compiler sees it as such. Does g++ implicitly tries to convert macro arguments to strings?
How to fix that? I mean, I'm using gnu LOG_MAKEPRI macro for syslogging, is it this macro that may be causing trouble? Also, is there some way to see the macro-expanded code?
How to fix that?
LOGDEBUG(("haystack_len %ld\n", haystack_len)); call the macro with one unique argument.
So it will produce:
do { if (DEBUG_TEST) syslog(LOG_MAKEPRI(LOG_SYSLOG, LOG_DEBUG), ("haystack_len %ld\n", haystack_len)); } while (0);
And ("haystack_len %ld\n", haystack_len) use comma operator and result in haystack_len
So you have to call it that way: LOGDEBUG("haystack_len %ld\n", haystack_len);
Also, is there some way to see the macro-expanded code?
gcc -E may help.