Случай вариационного макроса в C - PullRequest
0 голосов
/ 09 марта 2019

У меня есть 2 макроса-обертки для утверждения входных параметров функции:

/**
 * @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_;                                                        \

Первый используется в функциях, возвращающих void, а второй - в функциях, возвращающих void. Мне было интересно, либо в C11 (или ранее) существует механизм, позволяющий использовать только один макрос с количеством переменных параметров. В зависимости от того, сколько параметров предоставлено макросу (1 или 2), return или return ret_ будут скомпилированы.

Ответы [ 2 ]

3 голосов
/ 09 марта 2019

Вы можете сделать это следующим образом:

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

Но помните, вы не можете гарантировать только один параметр в этом вариационном макросе, поэтому вам нужно использовать его правильно.

Обновление: Благодаря этой теме я пришел к лучшему подходу:

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);
}

Этот вариант намного лучше предыдущего, потому чтоэто как-то проверяет количество параметров.

2 голосов
/ 09 марта 2019

Есть способ сделать такие вещи, которые являются стандартными C. Сначала у вас есть основной макрос, который выполняет работу в обоих случаях

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

Как вы можете видеть, он получает три аргумента или более.И вам нужно договориться, что RET - это просто пустой токен для случая, когда вам это нужно.

Теперь вы можете обернуть вокруг него

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

Это гарантирует, чтозапятые, которые могут быть внутри отдельных аргументов, будут рассматриваться как разделители аргументов для ASSERT0.

В этом случае макрос уровня пользователя может быть

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

. Это гарантирует, что если вы используете его только с однимАргумент ASSERT0 внизу увидит пустой второй аргумент.Если вы вызываете его с двумя аргументами, ASSERT0 просто увидит их.

Также вам следует подумать об обёртывании вашего внутреннего макроса в do { ... } while(0).В противном случае вы можете столкнуться с проблемой "зависания else" и спутать пользователей с другими синтаксическими эффектами.

...