I'm trying to clean up some legacy code that contains hundreds of functions with bodies that look like this:
void functionWithSideEffect(Foo* foo)
{
if (foo)
{
// Use `foo' to warm the planet
...
}
}
Obviously, silently failing if a precondition check fails isn't the best idea, so I'd like to refactor this to:
int functionWithSideEffect(Foo* foo)
{
RETURN_IF_FALSE(foo != NULL, "foo is NULL in functionWithSideEffect!");
// Use `foo' to warm the planet
...
}
The following macro seems to work fine for functions that don't return a value:
#define RETURN_IF_FALSE(cond, msg) \
do { \
if (!(cond)) { \
LOG("%s\n", (msg)); \
assert((cond)); \
} \
return; \
} while(0)
And it has the following desirable properties:
It is clear and succinct in usage
It does not silently fail
It will crash in debug builds, but attempt to soldier on in release builds
(Admittedly, for functions returning void, soldiering on in release builds may not always be 'desirable'.)
For functions that do return a value, this macro does the trick:
#define RETURN_VALUE_IF_FALSE(cond, msg, retval ) \
do { \
if (!(cond)) { \
LOG("%s\n", (msg)); \
assert((cond)); \
} \
return(retval); \
} while(0)
My question is: is it possible to write a single RETURN_IF_FALSE macro to handle both void functions and functions returning a value? I sat down to attempt something using a varargs macro and quickly discovered I'm not very good at writing complex macros. I started out with this test program:
#include <stdio.h>
#include <assert.h>
#define RETURN_IF_FALSE(cond, msg, ... ) \
do { \
if (!(cond)) { \
fprintf(stderr, "%s\n", (msg)); \
assert((cond)); \
} \
return (##__VA_ARGS__); \
} while(0)
int main()
{
RETURN_IF_FALSE(1 < 0, "1 is not less than 0!", -1);
return 0;
}
Perhaps not surprisingly, it generated the following compile error:
g++ macro_test.cpp -o macro_test
macro_test.cpp:10:14: error: pasting "(" and "-" does not give a valid preprocessing token
return (##__VA_ARGS__); \
^
macro_test.cpp:16:5: note: in expansion of macro ‘RETURN_IF_FALSE’
RETURN_IF_FALSE(1 < 0, "1 is not less than 0!", -1);
^
Is it even possible to cover both cases with a single macro? I'm using gcc 4.8.1 on Linux. (I can compile with -std=c++11, if it helps...)
UPDATE: To bring this full-circle, here's the implementation I ultimately wound up with based on #Turix's answer and a suggestion from #Deduplicator to move the assert() call up to avoid a double evaluation of the conditional in the 'sunny day' case:
#define RETURN_IF_FALSE(cond, ... ) \
do { \
if (!(cond)) { \
const char* msg = \
"Pre-condition '" #cond "' not met, returning " #__VA_ARGS__ "..."; \
LOG("%s\n", msg); \
assert((cond)); \
return __VA_ARGS__; \
} \
} while(0)
(I decided it wasn't really all that necessary/useful to allow setting of a 'free form' message string, so I just generated a canned one from the condition...)
Just replace this part of your macro return (##__VA_ARGS__); with return __VA_ARGS__ ; and I think it should do what you want (assuming that what you would pass for the return value isn't a complex expression -- if it is, you would need to pre-wrap the parameter with parentheses).
I got this to work.
#include <stdio.h>
#define RET_IF_FALSE(x, y, z) if (!x) { printf(y); return z; }
int a(int *p)
{
RET_IF_FALSE(p, __FUNCTION__, 0);
return *p;
}
void b(int *p)
{
RET_IF_FALSE(p, __FUNCTION__, );
}
int main()
{
int x;
x = a(&x);
b(&x);
x = a(NULL);
b(NULL);
return 0;
}
It may not be the prettiest solution with a trailing comma, and it isn't compliant with standards according to gcc's -pedantic option.
Using:
#define RET_IF_FALSE(x, y, ...) if (!x) { printf(y); return __VA_ARGS__; }
with the rest of the code the same works for gcc with pedantic and -std=c99, and with -std=c++11 in clang++ and g++. Not sure what MS compilers do, as their support for standards is a little less stellar at times (and I haven't got a Windows setup to test on at present).
Related
What's the way to implement a standard-compliant assert macro with an optional formatted message?
What I have works in clang, but (correctly) triggers the -Wgnu-zero-variadic-macro-arguments warning if it is turned on (e.g. via -Wpedantic) when the macro is used without the optional message. Wandbox
#define MyAssert(expression, ...) \
do { \
if(!(expression)) \
{ \
printf("Assertion error: " #expression " | " __VA_ARGS__); \
abort(); \
} \
} while(0)
One needs to really use the preprocessor to the max in order to differentiate no additional arguments from the case where they are present. But with Boost.PP one can do this:
#include <boost/preprocessor/variadic/size.hpp>
#include <boost/preprocessor/arithmetic/sub.hpp>
#include <boost/preprocessor/logical/bool.hpp>
#include <boost/preprocessor/cat.hpp>
#define MyAssert(...) BOOST_PP_CAT(MY_ASSERT,BOOST_PP_BOOL(BOOST_PP_SUB(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1)))(__VA_ARGS__)
#define MY_ASSERT0(expr) MY_ASSERT1(expr,)
#define MY_ASSERT1(expression, ...) \
do { \
if(!(expression)) \
{ \
std::printf("Assertion error: " #expression " | " __VA_ARGS__); \
std::abort(); \
} \
} while(0)
MyAssert must accept at least one argument (standard). Then we count the arguments, subtract one, and turn to a boolean (0 or 1). This 0 or 1 is concatenated to the token MY_ASSERT to form a macro name, to which we proceed to forward the arguments.
MY_ASSERT1 (with args), is your original macro. MY_ASSERT0 substitutes itself with MY_ASSERT1(expr,), the trailing comma means we pass another argument (thus fulfilling the requirement for the one extra argument), but it is an empty token sequence, so it does nothing.
You can see it live.
Since we already went down this rabbit hole, if one doesn't want to pull in Boost.PP the above can be accomplished with the usual argument counting trick, slightly adapted. First, we must decide on a maximum limit for the arguments we allow. I chose 20, you can choose more. We'll need the typical CONCAT macro, and this macro here:
#define HAS_ARGS(...) HAS_ARGS_(__VA_ARGS__,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,)
#define HAS_ARGS_(a1,a2,a3,a4,a5,b1,b2,b3,b4,b5,c1,c2,c3,c4,c5,d1,d2,d3,d4,d5,e, N, ...) N
It's argument counting, but with a twist. When __VA_ARGS__ is a single argument (no extra ones), the N resolved as 0. Otherwise, it is resolved as 1. There can be up to 20 extra arguments after the expression, any number of which will resolve to the same 1. Now we just plug it into the same place we used boost before:
#define MyAssert(...) CONCAT(MY_ASSERT, HAS_ARGS(__VA_ARGS__))(__VA_ARGS__)
You can tinker with it here
I have a solution which I'm not particularly proud of..
We can obtain the first argument in plain form and as a string using:
#define VA_ARGS_HEAD(N, ...) N
#define VA_ARGS_HEAD_STR(N, ...) #N
Note that in usage, in order to not get warnings, you should do VA_ARGS_HEAD(__VA_ARGS__, ) (with the extra ,) so that VA_ARGS_HEAD is never used with a single parameter (trick taken from StoryTeller's answer).
We define the following helper function:
#include <stdarg.h>
#include <stdio.h>
inline int assertionMessage(bool, const char *fmt, ...)
{
int r;
va_list ap;
va_start(ap, fmt);
r = vprintf(fmt, ap);
va_end(ap);
return r;
}
When the assertion has a format string, the function would work with __VA_ARGS__ as is, however when the bool is the only argument, we're missing a format string. That's why we'll add another empty string after __VA_ARGS__ when invoking it:
#define MyAssert(...) \
do { \
if(!(VA_ARGS_HEAD(__VA_ARGS__, ))) \
{ \
printf("Assertion error: %s | ", VA_ARGS_HEAD_STR(__VA_ARGS__, )); \
assertionMessage(__VA_ARGS__, ""); \
abort(); \
} \
} while(0)
Note that assertionMessage doesn't have printf in its name. This is deliberate and intended to avoid the compiler giving format-string related warnings for its invocations with the extra "" argument. The down-side for this is that we don't get the format-string related warnings when they are helpful.
The basic solution is to use << on cerr:
#define MyAssert(expression, msg) \
do { \
if(!(expression)) \
{ \
std::cerr << msg; \
abort(); \
} \
} while(0)
This solution uses C++ streams, so you can format the output as you see fit. Actually this is a simplification of a C++17 solution that I'm using to avoid temporaries (people tend to use + instead of << with this solution, triggering some efficiency warnings).
Use it then like this:
MyAssert(true, "message " << variable << " units");
I think the optionality is bogus here, as you are outputting "Assertion error:" meaning that you expect a message.
For example, I saw source code like the following. Can we use #define in a function? How does it work? (more information: this code is what I copied from openvswitch source code):
void *
ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
{
switch (code) {
case OFPUTIL_ACTION_INVALID:
#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
#include "ofp-util.def"
OVS_NOT_REACHED();
#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \
case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
#include "ofp-util.def"
}
OVS_NOT_REACHED();
}
#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \
void \
ofputil_init_##ENUM(struct STRUCT *s) \
{ \
memset(s, 0, sizeof *s); \
s->type = htons(ENUM); \
s->len = htons(sizeof *s); \
} \
\
struct STRUCT * \
ofputil_put_##ENUM(struct ofpbuf *buf) \
{ \
struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \
ofputil_init_##ENUM(s); \
return s; \
}
#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
OFPAT10_ACTION(ENUM, STRUCT, NAME)
#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
OFPAT10_ACTION(ENUM, STRUCT, NAME)
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
void \
ofputil_init_##ENUM(struct STRUCT *s) \
{ \
memset(s, 0, sizeof *s); \
s->type = htons(OFPAT10_VENDOR); \
s->len = htons(sizeof *s); \
s->vendor = htonl(NX_VENDOR_ID); \
s->subtype = htons(ENUM); \
} \
\
struct STRUCT * \
ofputil_put_##ENUM(struct ofpbuf *buf) \
{ \
struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \
ofputil_init_##ENUM(s); \
return s; \
}
#include "ofp-util.def"
#define is a preprocessor directive: it is used to generate the eventual C++ code before it is handled to the compiler that will generate an executable. Therefore code like:
for(int i = 0; i < 54; i++) {
#define BUFFER_SIZE 1024
}
is not executed 54 times (at the preprocessor level): the preprocessor simply runs over the for loop (not knowing what a for loop is), sees a define statement, associates 1024 with BUFFER_SIZE and continues. Until it reaches the bottom of the file.
You can write #define everywhere since the preprocessor is not really aware of the program itself.
Sure this is possible. The #define is processed by the preprocessor before the compiler does anything. It is a simple text replacement. The preprocessor doesn't even know if the line of code is inside or outside a function, class or whatever.
By the way, it is generally considered bad style to define preprocessor macros in C++. Most of the things they are used for can be better achieved with templates.
You can use it inside a function, but it is not scoped to the function. So, in your example, the second definitions of a macro will be a redefinition and generate an error. You need to use #undef to clear them first.
You can use #define anywhere you want. It has no knowledge of functions and is not bound by their scope. As the preprocessor scans the file from top-to-bottom it processes #defines as it sees them. Do not be misled (by silly code like this!) into thinking that the #define is somehow processed only when the function is called; it's not.
How does it work? All C/C++ files are first processed by... the preprocessor.
It doesn't know anything about C nor C++ syntax. It simply replaces THIS_THING with ANOTHER THING. That's why you can place a #define in functions as well.
Sure. #define is handled by the preprocessor which occurs well before the compiler has any sense of lines of code being inside functions, inside parameters lists, inside data structures, etc.
Since the preprocessor has no concept of C++ functions, it also means that there is no natural scope to macro definitions. So if you want to reuse a macro name, you have to #undef NAME to avoid warnings.
I have the following code:
#include <iostream>
#include <stdexcept>
#define TRACE_MACRO(EnterText) \
class CTrace \
{ \
public: \
CTrace() \
{ \
std::cout << EnterText; \
} \
private:\
};
#define DO_TRACE TRACE_MACRO("[ENTER] " __FUNCTION__ "\r\n") CTrace trace
static void test()
{
DO_TRACE;
}
int main(int, char**)
{
DO_TRACE;
test();
return 0;
}
Which outputs:
[ENTER] main::CTrace::CTrace
[ENTER] test::CTrace::CTrace
How can I write the macro such that __FUNCTION__ is expanded first so that the output becomes:
[ENTER] main
[ENTER] test
I attempted to create a sub macro called DO_TRACE2 that forwards the arguments, but this results in the same output.
If this isn't possible then what about a macro that will compile time substring the text to strip off the CTrace::CTrace part of the string?
Edit: Note that I don't want to pass a pointer to a compile time string to this class, I want the call to std::cout to appear as if I had actually manually wrote std::cout << "main";
Untested, but here's a simple rearrangement that might work:
#define TRACE_MACRO \
class CTrace \
{ \
public: \
CTrace(const char* text) \
{ \
std::cout << text; \
} \
private:\
};
#define DO_TRACE TRACE_MACRO CTrace trace("[ENTER] " __FUNCTION__ "\r\n")
Jimmy asked
is it necessary to define a whole new class in the macro def?
And you replied:
#Jimmy no, I just wanted the call to std::cout to be using const
static compile time strings rather than a pointer to a compile time
string, if that makes sense.
So why not just drop the class:
#define DO_TRACE std::cout << "[ENTER] " << __FUNCTION__ << "\r\n"
This will output:
[ENTER] main
[ENTER] test
and uses the const static compile time string....
I probably missed something, just tell me, I'll delete this post....
By the way, I compiled the code from your post using GNU GCC version 4.8.1 from http://www.compileonline.com/compile_cpp_online.php. It outputs
[ENTER] CTrace
[ENTER] CTrace
So looks like FUNCTION macro is resolved differenetly by compilers...
I would like to do something like the following:
#define CODE_BLOCK( id) \
do { \
someNameSpace::##id foo; \
foo.ParseFromString( ...); \
print_##id( foo);
} while(0)
[...]
CODE_BLOCK( BlahStream);
So basically, I want foo to be declared as type someNameSpace::id where id macro argument. print_id is defined elsewhere. Anyways, g++ complains with the message:
error: pasting "::" and "BlahStream" does not give a valid preprocessing token
Is there anyway around this?
If it helps, foo is from some userdefined googleprotocol buffer. My g++ version is 4.7.2 on debian linux.
Thanks.
Just remove the ## operator and it'll work just fine.
e.g.
#define stuff(x) \
std:: x
#include <iostream>
int main() {
stuff(cout) << "Hello World\n";
}
The preprocessor is already aware that x::y is three separate tokens: x, :: and y.
Therefore, you do not need any concatenation and, in fact, as you've found out, trying to use it will result in an error.
When you write this:
someNameSpace::##id
you are writing someNamespace, ::y … and ::y is not a single token.
You should simply write:
someNameSpace::id
Now you have someNamespace, ::, y … as desired.
Here's your finished code:
#define CODE_BLOCK(id) \
do { \
someNameSpace::id foo; \
foo.ParseFromString(...); \
print_##id(foo);
} while(0)
// [...]
CODE_BLOCK(BlahStream);
N.B. I've removed the spaces that you keep writing after the ( symbol, because ew. :)
I have a macro to do a version of default arguments:
#define Log_getMacro4(_1, _2, _3, _4, NAME, ...) NAME
#define Log_logWarning4(...) Log_log__("warning", __VA_ARGS__)
#define Log_logWarning3(...) Log_log__("warning", __VA_ARGS__, __LINE__)
#define Log_logWarning2(...) Log_log__("warning", __VA_ARGS__, __FILE__, __LINE__)
#define Log_logWarning1(...) Log_log__("warning", __VA_ARGS__, __PRETTY_FUNCTION__, __FILE__, __LINE__)
#define Log_logWarning(...) Log_getMacro4(__VA_ARGS__, Log_logWarning4, Log_logWarning3, Log_logWarning2, Log_logWarning1)(__VA_ARGS__)
The problem is that I now want to provide a variant on that function which frees the first argument to Log_logWarning after:
#define Log_logWarning_free(str, ...) Log_logWarning(str, __VA_ARGS__); if (str) free(str);
The problem is that this cannot be used with the return value of a function. For example:
char *foo(){
char *ret = (char*)malloc(30*sizeof(char));
strcpy(ret, "Hello World");
return ret;
}
void bar(){
Log_logWarning_free(foo());
}
Therefore, I was wondering if there is a way to create a local variable first to which I will assign the first argument of the macro and then use that variable in the test and subsequent free.
I would use an inline function instead, if at all possible.
If you have to use a macro, use a do { ... } while (0) construct:
#define Log_logWarning_free(str, ...) \
do { \
char * logtmp_ = str; \
Log_logWarning(logtmp_, __VA_ARGS__); \
if (logtmp_) free(logtmp_); \
} while (0)
The do-while-0 trick allows you to have a code block, while it prevents accidentally attaching the block to an another flow control construct incorrectly.
This full test program compiles with 4.7.2:
#include <stdlib.h>
#define Log_logWarning_free(str, ...) \
do { \
char * logtmp_ = str; \
Log_logWarning(logtmp_, __VA_ARGS__); \
if (logtmp_) free(logtmp_); \
} while (0)
void Log_logWarning(char* fmt, ...);
char * get_log_str(void);
int main()
{
Log_logWarning_free(get_log_str(), 1, 2, 3);
return 0;
}
Firstly, wrap your functions in do { } while(0) so you can add a semicolon to your usage of the functions and avoid weird errors.
Secondly, yes, you can use a local variable. See http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html for examples.
An simple example would be
#define swap(a,b) do {int _c=b; b=a; a=_c;} while(0)
That can then be safely invoked as:
int x = 5;
int y = 7;
swap(x, y);
See also: Can a C macro contain temporary variables?
The problem is knowing the type of the variable (except in
C++11). For the rest, you can use the usual trick for scoping:
#define X(y) do { auto CONCAT(_log_tmp_,__LINE__) = (y); ... } while(false);
In C and in C++ pre-C++11, you'll probably have to pass the type
of the variable in as an argument to the macro.