Reputation: 4944
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:
(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...)
Upvotes: 1
Views: 310
Reputation: 129524
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).
Upvotes: 1
Reputation: 4490
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).
Upvotes: 2