Проверка типов аргументов макроса в C - PullRequest
4 голосов
/ 17 января 2011

Можно ли проверять аргументы макроса #define?Например:

typedef enum
{
    REG16_A,
    REG16_B,
    REG16_C
}REG16;

#define read_16(reg16)  read_register_16u(reg16); \
                        assert(typeof(reg16)==typeof(REG16));

Приведенный выше код не работает.Что я делаю не так?

Кстати, я использую gcc, и я могу гарантировать, что я всегда буду использовать gcc в этом проекте.Код не должен быть переносимым.

Ответы [ 5 ]

7 голосов
/ 16 ноября 2011

Проверка типов в C немного свободна для целочисленных типов;но вы можете обмануть компилятор, используя тот факт, что большинство типов указателей несовместимы.

Так что

#define CHECK_TYPE(var,type) { __typeof(var) *__tmp; __tmp = (type *)NULL; }

Это выдаст предупреждение "назначение из несовместимого типа указателя", если типы нене то же самое.Например,

typedef enum { A1,B1,C1 } my_enum_t;
int main (int argc, char *argv) {
    my_enum_t x;
    int y;

    CHECK_TYPE(x,my_enum_t);  // passes silently
    CHECK_TYPE(y,my_enum_t);  // assignment from incompatible pointer type
}

Я уверен, что есть какой-то способ получить ошибку компилятора для этого.

7 голосов
/ 17 января 2011

gcc поддерживает typeof

например. типизированный минимальный макрос, взятый из ядра Linux

#define min(x,y) ({ \
    typeof(x) _x = (x); \
    typeof(y) _y = (y); \
    (void) (&_x == &_y);        \
    _x < _y ? _x : _y; })

но это не позволяет сравнивать два типа. Обратите внимание, что при сравнении указателей, который выдаст предупреждение - вы можете выполнить такую ​​проверку типа (также из ядра linux)

#define typecheck(type,x) \
({  type __dummy; \
    typeof(x) __dummy2; \
    (void)(&__dummy == &__dummy2); \
    1; \
})

Предположительно, вы могли бы сделать что-то подобное - то есть сравнить указатели с аргументами.

3 голосов
/ 17 января 2011

Нет, макросы не могут обеспечить проверку типов. Но, в конце концов, почему макрос? Вы можете написать функцию static inline, которая (вероятно) будет встроена компилятором - и здесь у вас будет проверка типа.

static inline void read_16(REG16 reg16) {
    read_register_16u(reg16);
}
1 голос
/ 17 января 2011

Нет.Макросы в C по своей природе небезопасны, и попытка проверки типов в C сопряжена с проблемами.

Во-первых, макросы раскрываются путем текстовой подстановки в фазе компиляции, где информация о типах недоступна.По этой причине для компилятора совершенно невозможно проверить тип аргументов, когда он выполняет расширение макроса.

Во-вторых, когда вы пытаетесь выполнить проверку в расширенном коде, например assert ввопрос, ваша проверка откладывается до времени выполнения и также сработает на казалось бы безвредных конструкциях, таких как

a = read_16(REG16_A);

, потому что перечислители (REG16_A, REG16_B и REG16_C) имеют тип int ине типа REG16.

Если вы хотите безопасность типов, лучше всего использовать функцию.Если ваш компилятор поддерживает это, вы можете объявить функцию inline, чтобы компилятор знал, что вы хотите избежать возможного использования служебных вызовов везде, где это возможно.

1 голос
/ 17 января 2011

Чтобы продолжить идею ulidtko, возьмите функцию inline и пусть она вернет что-то:

inline
bool isREG16(REG16 x) {
  return true;
}

С помощью таких вещей, как вы можете сделать утверждения времени компиляции:

typedef char testit[sizeof(isREG16(yourVariable))];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...