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.
Related
It is very common for any medium-to-large project to replace printf with a custom log function. Here is a minimal C++ example and its usage:
#include <stdio.h>
#include <stdarg.h>
#include <string>
void log_printf(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap); // real code obviously does something fancier
va_end(ap);
}
int main() {
std::string x = "Hello";
// correct code
printf("String is %s\n", x.c_str());
log_printf("String is %s\n", x.c_str());
// incorrect code
printf("String is %s\n", x); // bad line 1
log_printf("String is %s\n", x); // bad line 2
}
The simple logger receives a variable amount of arguments and calls vprintf to output them to standard output. The lines under 'correct code' demonstrate correct usage of this logger. My question involves the 'bad' lines, where a string object is incorrectly passed instead of a pointer to the character buffer.
Under GCC 4.6 (tested under Linux) neither of the bad lines can compile, which is a good thing because I want to catch such incorrect usage. The error is:
error: cannot pass objects of non-trivially-copyable type ‘std::string {aka struct std::basic_string<char>}’ through ‘...’
However in GCC 5.1 it has apparently become possible to pass non-trivially-copyable objects, and the compilation succeeds. If I use -Wall then only 'bad line 1' raises a warning about an unexpected argument type, but 'bad line 2' with the log_printf compiles without issue in any case. Needless to say both lines produce garbage output.
I can catch 'bad line 1' with -Wall -Werror, but what about 'bad line 2'? How can I cause it to also generate a compilation error?
For your own functions you need to use a common function attribute call format:
void log_printf(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
void log_printf(const char* fmt, ...) {
...
}
Note that the attribute must be set on a function declaration, not the definition.
The first argument to the format attribute is the style, in this case printf (scanf is also possible, for functions that works like scanf), the second argument is the format string, and the third argument is where the ellipsis ... is. It will help GCC check the format strings like for the standard printf function.
This is a GCC extension, though some other compilers have adopted it to become GCC compatible, most notably the Intel C compiler ICC and Clang (the standard compiler used on OSX and some BSD variants). The Visual Studio compiler does not have this extension, and I know of no similar thing for Visual C++.
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)....
Is there a way that I can force a preprocessor macro in C++ to emit an error? What I would like to do is define a macro UNKNOWN. I'm writing some code for a robot, and I don't yet know where all of the electronics are being plugged in. I'd like to be able to define the ports in some header file, like
const int MOTOR_PORT = 1;
const int FAN_PORT = 2;
//etc.
However, when I reach a port that I don't yet know, I want to be able to write something like
const int LED_PORT = UNKNOWN;
In debug mode, UNKNOWN would just be defined to some arbitrary value, like 0. However, when compiling in release mode, I want it to throw an error when UNKNOWN is used, so that unassigned ports don't end up in the final release. I know I can use the #error directive to force an error, but can I do something similar in a macro?
I've seen a solution using static_assert, but I unfortunately can't use C++11 for this platform.
Since #error can't result from a macro expansion, you can ensure that the macro expands to something that must be diagnosed, like a syntax error.
For example:
#ifdef RELEASE
#define UNKNOWN #Invalid_use_of_UNKNOWN
#else
#define UNKNOWN 0
#endif
const int MOTOR_PORT = 1;
const int FAN_PORT = 2;
const int LED_PORT = UNKNOWN;
int main(void) {
int x = LED_PORT;
}
The # character isn't part of C's basic character set, so its appearance outside a comment, character constant, or string literal should always result in an error message. ($ would work, except that accepting $ in identifiers is a common extension. ` would probably also work, but # stands out better.)
I've defined the macro so it produces a reasonable error message with gcc:
c.c:9:1: error: stray ‘#’ in program
c.c:9:22: error: ‘Invalid_use_of_UNKNOWN’ undeclared here (not in a function)
and with clang:
c.c:9:22: error: expected expression
const int LED_PORT = UNKNOWN;
^
c.c:2:17: note: expanded from:
#define UNKNOWN #Invalid_use_of_UNKNOWN
^
1 error generated.
(There's a _Pragma operator corresponding to the #pragma directive. It would be nice if there were an _Error operator as well, but there isn't.)
Well, this does not produce a complier error message like #error, but would compile in debug and fail in release:
#ifdef _DEBUG
# define UNKNOWN 1
#else
# define UNKNOWN
#endif
const int port1 = UNKNOWN; // fail in release
You could make a division by zero which will throw a compiler error:
#define UNKNOWN 0/0
The sizeof operator cannot be applied to an incomplete type, so try this:
// Declared, but not defined anywhere.
struct illegal_use_of_unknown_macro;
#define UNKNOWN (sizeof (illegal_use_of_unknown_macro))
#define LOG(format,...) Logger::Log(format,__VA_ARGS__)
#define STRIP(netIp) GeneralUtils::inet_ntop_(netIp)
string GeneralUtils::inet_ntop_(unsigned int netIp){
char strIP[INET_ADDRSTRLEN];
in_addr sin_addr;
sin_addr.s_addr = netIp;
inet_ntop(AF_INET, &sin_addr.s_addr, strIP, sizeof strIP);
return string(strIP);
}
when calling to :
LOG("src %s dst %s" ,STRIP(src_ip_));
i get compilation error:
cannot pass objects of non-trivially-copyable type ‘std::string {aka struct std::basic_string<char>}’ through ‘...’
I understand that varargs is c compatible , so i cannot send string to it.
Is there a simple way to bypass it?
Will it be correct to fix it like this:
#define STRIP(netIp) GeneralUtils::inet_ntop_(netIp).data()
You can pass const char * instead of std::string. You can take it from std::string by calling c_str()
#define STRIP(netIp) GeneralUtils::inet_ntop_(netIp).data()
is wrong, it will invoke undefined behavior since it doesn't include a terminating zero. Use
#define STRIP(netIp) GeneralUtils::inet_ntop_(netIp).c_str()
instead.
I'm working with a library that redefines NULL. It causes some problems with other parts of my program. I'm not sure what I can do about it. Any idea? My program's in C++, the library's in C.
#ifdef NULL
#undef NULL
#endif
/**
* NULL define.
*/
#define NULL ((void *) 0)
Oh, and it produces these errors:
Generic.h:67: error: default argument for parameter of type 'LCD::LCDBase*' has type 'void*'
Generic.cpp: In constructor 'LCD::Generic::Generic(std::string, Json::Value*, int, LCD::LCDBase*)':
Generic.cpp:44: error: invalid conversion from 'void*' to 'QObject*'
Generic.cpp:44: error: initializing argument 2 of 'LCD::LCDWrapper::LCDWrapper(LCD::LCDInterface*, QObject*)'
Generic.cpp: In member function 'void LCD::Generic::BuildLayouts()':
Generic.cpp:202: error: invalid conversion from 'void*' to 'LCD::Widget*'
Generic.cpp: In member function 'void LCD::Generic::AddWidget(std::string, unsigned int, unsigned int, std::string)':
Generic.cpp:459: error: invalid conversion from 'void*' to 'LCD::Widget*'
scons: *** [Generic.o] Error 1
Here's the first one:
Generic(std::string name, Json::Value *config, int type, LCDBase *lcd = NULL);
Edit: Ok, casting explicitly works, but how do I cast for a function pointer?
Can you rebuild the library without that define? That's what I'd try first. NULL is a pretty standard macro, and should be assumed to be defined everywhere.
Right now, your problem is that C++ doesn't allow automatic casts from void * to other pointer types like C does.
From C++ Reference:
In C++, NULL expands either to 0 or 0L.
If that doesn't work, just do a global replace in the library: NULL to LIBDEFINEDNULL or something. That way you'll keep the library code intact and avoid the macro collision.
Do you have access to that libraries source? If so, I think a search and replace on their code is in order. (Replace their NULL with LIBNAME_NULL or something similar.) If that's simply not an option then I would recommend using 0 in your code instead of NULL.
I'm curious, though: What problems is that causing? They're not changing the value of null, only the default casting.
One of your comments says you've considered redefining it yourself, but you don't know what to redefine it to.
A lot of implementations will define NULL like this:
#undef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void*)0)
#endif
This is because in C, it makes sense to have it as a void pointer, because it's a pointer type that can be implicitly cast to other types.
C++ doesn't allow this (which is causing your problems), but using 0 instead of NULL works.
I think in all recent versions, GCC will actually define it to __null, which is a non-portable extension.
The most generic approach would be to wrap up the offending includes and store and restore the macros previous definition. This, however, is compiler-dependent.
This is how you could do it with VC:
#pragma push_macro("NULL")
#include <offendinglib.h>
#pragma pop_macro("NULL")
Alternatively, set the macro to what you need it to be afterwards:
#include <offendinglib.h>
#undef NULL
#define NULL 0
Yes, you do just need to cast appropriately:
Generic(std::string name, Json::Value *config, int type,
LCDBase *lcd = (LCDBase *)NULL);