c macro - как установить имя макроса как вывод другого макроса - PullRequest
0 голосов
/ 03 июля 2018

Я хочу создать макрос, имя которого является объединением двух вещей, т.е.

#define name ## _body(a) \
    a

но gcc -E выдает ошибку

macros.c: 9: 18: ошибка: «##» не может появляться ни в одном конце расширения макроса #define typename ## _body (body) \

Вопрос

Возможно ли сделать это , используя только препроцессор C ?

1 Ответ

0 голосов
/ 03 июля 2018

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

Рассмотрим следующие макросы:

#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, вы можете создавать «функции», которые вызывают различные реализации, основываясь на типе их аргумента.

...