Можно ли привести аргументы typecheck к макросу #define? Например:
typedef enum { REG16_A, REG16_B, REG16_C }REG16; #define read_16(reg16) read_register_16u(reg16); \ assert(typeof(reg16)==typeof(REG16));
Вышеприведенный код не работает. Что я делаю неправильно?
BTW, я использую gcc, и я могу гарантировать, что я всегда буду использовать gcc в этом проекте. Код не должен быть портативным.
gcc поддерживает typeof
например, миниатюрный макрос, полученный из ядра linux
#define min(x,y) ({ \ typeof(x) _x = (x); \ typeof(y) _y = (y); \ (void) (&_x == &_y); \ _x < _y ? _x : _y; })
но это не позволяет сравнивать два типа. Обратите внимание, хотя сравнение указателей, которое будет генерировать предупреждение, вы можете сделать typecheck, подобное этому (также из ядра linux)
#define typecheck(type,x) \ ({ type __dummy; \ typeof(x) __dummy2; \ (void)(&__dummy == &__dummy2); \ 1; \ })
Предположительно, вы могли бы сделать что-то подобное - например, сравнить указатели на аргументы.
Проверка типов в 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 }
Я уверен, что есть какой-то способ получить ошибку компилятора для этого.
Нет, macros не могут предоставить вам проверку типов. Но, в конце концов, почему макрос? Вы можете написать static
inline
функцию, которая (возможно) будет встроена в компилятор – и здесь вы будете иметь проверку типов.
static inline void read_16(REG16 reg16) { read_register_16u(reg16); }
Чтобы продолжить идею ulidtko, возьмите inline
функцию и верните что-нибудь:
inline bool isREG16(REG16 x) { return true; }
С помощью таких вещей вы можете сделать компиляцию утверждений времени:
typedef char testit[sizeof(isREG16(yourVariable))];
Нет. Макросы в C по своей природе являются небезопасными, и попытка проверки типов на C чревата проблемами.
Во-первых, macros расширяются путем текстовой подстановки в фазе компиляции, где информация типа отсутствует. По этой причине компилятору совершенно невозможно проверить тип аргументов при расширении макроса.
Во-вторых, когда вы пытаетесь выполнить проверку расширенного кода, например, assert
в вопросе, ваш чек откладывается на время выполнения, а также запускается на казалось бы безобидных конструкциях, таких как
a = read_16(REG16_A);
потому что REG16_A
( REG16_A
, REG16_B
и REG16_C
) имеют тип int
а не тип REG16
.
Если вам нужна безопасность типа, лучше всего использовать функцию. Если ваш компилятор поддерживает его, вы можете объявить функцию inline
, поэтому компилятор знает, что вы хотите избежать накладных расходов функции-функции, где это возможно.