Как реализовать универсальный макрос в C? - PullRequest
9 голосов
/ 31 августа 2011
FUNC(param);

Если param равно char *, отправьте func_string.

, когда int, отправить на func_int

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

Ответы [ 6 ]

12 голосов
/ 31 августа 2011

Это будет возможно с C1X , но не в текущем стандарте.

Это будет выглядеть так:

#define cbrt(X) _Generic((X), long double: cbrtl, \
                          default: cbrt, \
                          float: cbrtf)(X)
11 голосов
/ 31 августа 2011

Типы переменных известны компилятору, но не препроцессору (который видит код просто как неструктурированный текст поток токенов и выполняет над ним только простые операции замены). Поэтому я боюсь, что вы не сможете достичь этого с помощью макросов C.

В C ++ они изобрели шаблоны для решения таких проблем (и не только).

5 голосов
/ 31 августа 2011

Вы можете проверить характеристики типов.

Например, int может содержать отрицательное значение, а char* - нет.Так что если ((typeof(param))-1) < 0, param не подписано:

if (((typeof(param))-1) < 0) {
    do_something_with_int();
} else {
    do_something_with_char_p();
}

Компилятор, очевидно, оптимизирует это.

Попробуйте это здесь: http://ideone.com/et0v1

Это было быдаже проще, если типы были разных размеров.Например, если вы хотите написать универсальный макрос, который может обрабатывать символы разных размеров:

if (sizeof(param) == sizeof(char)) {
    /* ... */
} else if (sizeof(param) == sizeof(char16_t)) {
    /* ... */
} else if (sizeof(param) == sizeof(char32_t)) {
    /* ... */
} else {
   assert("incompatible type" && 0);
}

GCC имеет встроенную функцию __builtin_types_compatible_p(), которая может проверять совместимость типов:

if (__builtin_types_compatible_p(typeof(param), int)) {
    func_int(param);
} else if (__builtin_types_compatible_p(typeof(param), char*)) {
    func_string(param);
}

Попробуйте здесь: http://ideone.com/lEmYE

Вы можете поместить это в макрос, чтобы достичь того, что вы пытаетесь сделать:

#define FUNC(param) ({                                                \
    if (__builtin_types_compatible_p(typeof(param), int)) {           \
        func_int(param);                                              \
    } else if (__builtin_types_compatible_p(typeof(param), char*)) {  \
        func_string(param);                                           \
    }                                                                 \
})

(({...}) является выражением оператора GCC , оно позволяет группе операторов быть значением.

Встроенное __builtin_choose_expr() может выбрать выражение для компиляции.С __builtin_types_compatible_p это позволяет вызвать ошибку во время компиляции, если тип параметра несовместим как с int, так и с char*: (компилируя что-то недопустимое в этом случае)

#define FUNC(param)                                                        \ 
    __builtin_choose_expr(__builtin_types_compatible_p(typeof(param), int) \ 
        , func_int(param)                                                  \ 
        , __builtin_choose_expr(__builtin_types_compatible_p(typeof(param), char*) \ 
            , func_string(param)                                           \ 
            , /* The void expression results in a compile-time error       \ 
                 when assigning the result to something.  */               \ 
            ((void)0)                                                      \ 
        )                                                                  \ 
    )

Этофактически слегка измененный пример из __buildin_choose_expr docs .

4 голосов
/ 31 августа 2011

Нет возможности запускать типы проверки времени в C89 / ANSI C, но есть расширение для gcc, которое позволяет это.typeof или что-то в этом роде, если я помню.Я видел это в Linux Kernel один раз.

В kernel.h :

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

Взгляните на эту статью: GCC-хаки в Linuxkernel

Когда я впервые увидел это, я действительно задал вопрос здесь на SO о:

мин макрос в kernel.h

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

1 голос
/ 31 августа 2011

Типы переменных действительно известны во время компиляции, однако расширение макроса происходит перед компиляцией.Я предлагаю вам реализовать 2 перегруженные функции вместо макроса.

1 голос
/ 31 августа 2011

Вы не можете сделать это с помощью макроса.Значение макроса подставляется во время компиляции и не интерпретируется.Они просто замены.

...