2
votes

I have 2 wrapper macros for asserting function input parameters:

/**
 * @brief   An assert wrapper with no value return in case assert fails.
 * @param   x_: value to test for being non zero.
 */
#define UTIL_ASSERT_VOID(x_)                                                \
    assert_param(x_);                                                       \
    if (!x_)                                                                \
        return;                                                             \

/**
 * @brief   An assert wrapper with a value return in case assert fails.
 * @param   x_: value to test for being non zero.
 */
#define UTIL_ASSERT_VAL(x_, ret_)                                           \
    assert_param(x_);                                                       \
    if (!x_)                                                                \
        return ret_;                                                        \

The former is used in functions returning void, while the latter in functions returning non-void. I was wondering either in C11 (or earlier) there is a mechanism allowing one to use only a single macro with variadic parameters amount. Depending on how many parameters are provided to the macro (1 or 2), a return or return ret_ would be compiled.

2
Smells like an "XY problem". What you probably should do is simply assert(param1); assert(param2); .... How does it make sense to return from the function when an assert has triggered? - Lundin
@Lundin in a generic situation it doesnt make sense. But first- you should always handle all cases, so you always return after assert, no matter what. Second, this doesnt make sense only if the program is run in an environment controlled by an operating system, that will halt it at assert. In an embedded system, with no OS and many threads, bad things can happen when segmentation faults occur. Also depends on assert implementation, it doesnt always halt the thread. - Łukasz Przeniosło
In a real system you would always return an error code upon error; you wouldn't just silently crash in case of errors in a void function. So your scenario isn't realistic. You wouldn't have one function returning void and another returning int, but a single function type returning err_t or whatever. Also, bare metal systems should avoid assert() in the first place. - Lundin
@Lundin assert is a debugging tool. Its not even compiled in a release build. Its existence makes sense only for development purposes and is not taken under consideration in a release build. If it feels like it should, the functionalities are not build in a correct way. You are describing different mechanisms. - Łukasz Przeniosło
That's what I mean, in case of release build the assert wouldn't be there and so your void function would just silently return and the program have no way to tell that an error occurred. That's simply bad design. In addition, embedded systems would most often leave parameter validation to the caller, since it can be expensive. - Lundin

2 Answers

5
votes

You can do it like this:

#define UTIL_ASSERT(x_, ...)                                                \
    assert_param(x_);                                                       \
    if (!x_)                                                                \
        return __VA_ARGS__;

But remember, you cannot guarantee just 1 parameter in this variadic macro, so you need to use it correctly.

Update: Thanks to this thread, I came to better approach:

void assert_param(int x);

#define UTIL_ASSERT_1(x_)   do { assert_param(x_); if (!x_) return; } while(0)

#define UTIL_ASSERT_2(x_, ret_)   do { assert_param(x_); if (!x_) return ret_; } while(0)     

#define GET_MACRO(_1,_2,NAME,...) NAME
#define UTIL_ASSERT(...) GET_MACRO(__VA_ARGS__, UTIL_ASSERT_2, UTIL_ASSERT_1)(__VA_ARGS__)


int foo() {
     UTIL_ASSERT(0,1);
}

void doo() {
     UTIL_ASSERT(0);
}

This one is much better than previous one, because it somehow validates number of parameters.

3
votes

There is a way to do such things that are standard C. First you have a core macro that does the job for both cases

#define ASSERT0(X, RET, ...) 
   /* put your stuff here that only uses X and RET, and ignores the ... */

As you can see this receives three arguments or more. And you'd have to arrange that RET is just the empty token for the case that you need this.

Now you can put a wrapper around that

#define ASSERT1(...) ASSERT0(__VA_ARGS__)

This assures that commas that may be inside individual arguments will be seen as argument separators for ASSERT0.

The user level macro can then be

#define MY_ASSERT(...) ASSERT1(__VA_ARGS__, ,)

This ensures that if you use it with only one argument ASSERT0 down below will see an empty second argument. If you call it with two arguments, ASSERT0 will just see these.

Also you should consider to wrap your inner macro in do { ... } while(0). Otherwise you can have a "dangling else" problem and confuse users with other syntactic effects.