В определении макроса частью имени всегда является один токен препроцессора. Однако вы можете использовать функциональные макросы с параметрами, которые расширяются до одного токена.
Рассмотрим следующие макросы:
#define JOIN(a,b) a ## b
#define FOO(suffix) JOIN(FOO_, suffix)
#define FOOBAZ JOIN(FOO_, BAZ)
#define FOOBAAZ JOIN(foo_, baaz)
#define FOO_BAR first
#define FOO_BAZ second
С вышеприведенными определениями, объявив
int FOO(BAR) = 1;
int FOOBAZ = 2;
int FOOBAAZ = 3;
эквивалентно (т. Е. Подвергается предварительной обработке)
int first = 1;
int second = 2;
int foo_baaz = 3;
В некоторых случаях (особенно при изучении различных алгоритмов) полезны шаблоны или полиморфный код. Рассмотрим, например, следующее ops.h :
#if defined(TYPE) && defined(PREFIX)
#undef JOIN2
#define JOIN2_(a,b) a ## b
#define JOIN2(a,b) JOIN2_(a, b)
#define NAME(end) JOIN2(PREFIX, end)
static inline TYPE NAME(_add)(const TYPE val1, const TYPE val2)
{
return val1 + val2;
}
static inline TYPE NAME(_sub)(const TYPE val1, const TYPE val2)
{
return val1 - val2;
}
static inline TYPE NAME(_neg)(const TYPE val)
{
return -val;
}
static inline TYPE NAME(_mul)(const TYPE val1, const TYPE val2)
{
return val1 * val2;
}
static inline TYPE NAME(_div)(const TYPE val1, const TYPE val2)
{
return val1 / val2;
}
#endif
#undef NAME
#undef JOIN2
#undef PREFIX
#undef TYPE
Макрос NAME(suffix)
расширяется до одного токена, состоящего из расширения PREFIX
, за которым сразу следует расширение suffix
. (Если они не являются макросами препроцессора, они используются как есть.) Это позволяет многократно включать один и тот же заголовочный файл, предполагая, что PREFIX
определяется для нового значения каждый раз.
Обратите внимание, что обычно между пробелами, например, NAME(_add)
и следующие (const TYPE val1, const TYPE val2)
. Я опустил его в надежде, что это сделает определения функций более знакомыми.
Давайте рассмотрим пример программы, использующей такой заголовочный файл:
#include <stdlib.h>
#include <inttypes.h>
#include <stdio.h>
#define TYPE uint32_t
#define PREFIX u32
#include "ops.h"
#define TYPE float
#define PREFIX float
#include "ops.h"
#define TYPE double
#define PREFIX dbl
#include "ops.h"
int main(void)
{
printf("dbl_div(217.0, 31.0) = %.1f\n", dbl_div(217.0, 31.0));
printf("u32_sub(4, 2) = %" PRIu32 "\n", u32_sub(4, 2));
printf("float_mul(1.25f, 72.00f) = %.2ff\n", float_mul(1.25f, 72.00f));
return EXIT_SUCCESS;
}
Первый #include "ops.h"
определяет функции u32_add()
, u32_sub()
, u32_neg()
, u32_mul()
и u32_div()
. Второй определяет функции float_add()
и т. Д., А третий dbl_add()
и т. Д.
Указанные выше файлы являются действительными C99, и при компиляции и запуске выдает
dbl_div(217.0, 31.0) = 7.0
u32_sub(4, 2) = 2
float_mul(1.25f, 72.00f) = 90.00f
Если вы комбинируете вышеупомянутое с подходящими макросами, используя C11 _Generic
, вы можете создавать «функции», которые вызывают различные реализации, основываясь на типе их аргумента.