Cannot use macro in C++ - c++

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.

Related

avr-libc and stdio.h - Function-like macro leads to error: expected unqualified-id before 'do'

I'm working with avr-libc, and need to use a function-like macro defined in the stdio.h header of the library:
#define fdev_setup_stream(stream, p, g, f) \
do { \
(stream)->put = p; \
(stream)->get = g; \
(stream)->flags = f; \
(stream)->udata = 0; \
} while(0)
in my code, I create a FILE buffer, and try to prepare it with the macro:
void uart_putchar(char c, FILE *stream) {
if (c == '\n') {
uart_putchar('\r', stream);
}
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = c;
}
FILE uart_output;
fdev_setup_stream(uart_output, uart_putchar, NULL, _FDEV_SETUP_WRITE);
When compiling, I get the following error:
<path_to_file>/foo.cpp:80:1: error: expected unqualified-id before 'do'
fdev_setup_stream(uart_output, uart_putchar, NULL, _FDEV_SETUP_WRITE);
^
<path_to_file>/foo.cpp:80:1: error: expected unqualified-id before 'while'
fdev_setup_stream(uart_output, uart_putchar, NULL, _FDEV_SETUP_WRITE);
^
Why doesn't the compiler allow the macro definition? Why wouldn't the syntax be correct?
I'm compiling with avr-g++ version 4.9.2, and using the -std=gnu++11 flag. In Ubuntu 16.04.

C++ logging: __VA_ARGS__ causes double promotion [-Wdouble-promotion]

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...

Pass Variable arguments from function to another with variable arguments

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.

GCC: __attribute__ ((format (printf, x, y)) does not seem to work when function is called using a variadic macro

GCC version ntoarm-gcc (GCC) 4.4.2
I've added 'printf' format attributes to all my functions that wrap printf() and co. They work perfectly fine except when calling the functions using a variadic macro.
class Log { [...]
void log_fmt(LogLevel level, const std::string& funcName, const char_t * const logFormatStr, ...) __attribute__ ((format (printf, 4, 5)));
[...] };
An incorrect direct call like
log.log_fmt(Info, "test", "wrong %u", "type");
yields warning:
format '%u' expects type 'unsigned int', but argument 5 has type 'const char*'
The same incorrect call using a macro yields no warning, however:
#define LOGI(MSG, ...) log.log_fmt(Info, __func__, (MSG), __VA_ARGS__)
LOGI("wrong %u", "type");
Can I get the warnings to show up in this case too? Have I made a mistake or is this intended behaviour?
This:
#include <iostream>
#include <cstdio>
struct log {
static void logf(std::string, std::string, const char*, ...) __attribute__((format (printf, 3, 4))) {}
};
#define L(m, ...) log::logf("no", __func__, (m), __VA_ARGS__)
int main() {
//log::logf("hi", "hi", "test %u", "hi");
L("test %u", "hi");
}
works perfectly, in the sense that it gives the correct warning, like below:
main.cpp: In function 'int main()':
main.cpp:8:61: warning: format '%u' expects argument of type 'unsigned int', but argument 4 has type 'const char*' [-Wformat=]
#define L(m, ...) log::logf("no", __func__, (m), __VA_ARGS__)
^
main.cpp:12:5: note: in expansion of macro 'L'
L("test %u", "hi");
^
At global scope:
cc1plus: warning: unrecognized command line option "-Wno-undefined-internal" [enabled by default]
So, I would guess that the problem is on the position parameters (you have put 4, 5 on your format attribute, when it seems that you should have put 3, 4)....

Macro expansion within a macro

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.