Условные X-MACRO для выравнивания ENUM и String - PullRequest
0 голосов
/ 02 марта 2020

У меня есть список перечислений:

typedef enum {
     ENUM1,
     ENUM2,
     #if FLAG
     ENUM3,
     #endif
} enum_var_t;

И соответствующий список строк для выравнивания:

typedef struct { char[50] name; int val; } name_val_map_t
name_val_map_t name_val_map_table[] = {
    {.name="string1", .val=ENUM1},
    {.name="string2", .val=ENUM2},
    #if FLAG
    {.name="string3", .val=ENUM3},
    #endif
};

FLAG - это флаг сборки, который либо равен 0, либо 1 Я пытаюсь использовать X-макросы для выравнивания их в соответствии с ответом здесь :

#define IF(cond, foo) IF_IMPL(cond, foo)
#define IF_IMPL(cond, foo) IF_ ## cond (foo)
#define IF_0(foo)
#define IF_1(foo) foo

#define var_list \
X(ENUM1, "string1"), \
X(ENUM2, "string2"), \
IF(FLAG, X(ENUM3, "string3")), \

#define X(ENUMVAL, ...) ENUMVAL
typedef enum {
    var_list
}
#undef X
#define X(ENUMVAL, NAME) {.name = NAME, .val = ENUMVAL}
name_val_map_t name_val_map_table = {
var_list
}

Это приводит к ошибке, которая говорит о том, что я передаю больше аргументов макросу IF чем заявлено. Я предполагаю, что он обрабатывает запятую внутри X (ENUM3, "string3") как разделитель аргументов для IF. Я попытался инкапсулировать вызов X () в фигурные скобки и удалить фигурные скобки из IF_IMPL, но это тоже не сработало. Если я попытаюсь развернуть список аргументов в IF (), используя ... и VA_ARGS , я получу ожидаемые ошибки выражения. Я пытаюсь избежать использования файла def, так как это делает мой файл нечитаемым. Такое решение, как я пытался, было бы идеальным, чтобы избежать репликации кода и для удобства чтения. Любые указатели приветствуются, спасибо!

Ответы [ 2 ]

1 голос
/ 02 марта 2020

Использование variadi c макросов .

#define IF(cond, foo) IF_IMPL(cond, foo)
#define IF_IMPL(cond, ...) IF_ ## cond(__VA_ARGS__)
#define IF_0(foo, ...)
#define IF_1(foo, ...) foo, __VA_ARGS__

Тест:

//usr/local/bin/tcc -run "$0"; exit $?
#include <stdio.h>

#define FLAG3 1
#define FLAG4 0
#define FLAG5 1

typedef struct { char *name; int val; } name_val_map_t;

#define IF(cond, foo) IF_IMPL(cond, foo)
#define IF_IMPL(cond, ...) IF_ ## cond(__VA_ARGS__)
#define IF_0(foo, ...)
#define IF_1(foo, ...) foo, __VA_ARGS__

#define var_list               \
X(ENUM1, "string1")            \
X(ENUM2, "string2")            \
IF(FLAG3, X(ENUM3, "string3")) \
IF(FLAG4, X(ENUM4, "string4")) \
IF(FLAG5, X(ENUM5, "string5")) \

typedef enum {
#define X(ENUMVAL, str) ENUMVAL,
    var_list
#undef X
} enum_var_t;

name_val_map_t name_val_map_table[] = {
#define X(ENUMVAL, NAME) { NAME, ENUMVAL },
    var_list
#undef X
    { "sentinel value", 99 }
};

int main(void){
    int x =0;
    while(name_val_map_table[x].val != 99){
        printf("%i, %s\n", name_val_map_table[x].val, name_val_map_table[x].name);
    x++;}
    return 0;
}

/* output:
    0, string1
    1, string2
    2, string3
    3, string5
*/

Другой вариант - вручную создать IF_FLAGx (X (бла, бла) ) макросы для каждого случая ...

См. также: специализация макросов на основе аргумента в случае ошибки MSV C.

0 голосов
/ 02 марта 2020

Это кажется излишне сложным. Я бы просто сделал это вместо:

#if FLAG 
  #define var_list       \
  X(ENUM1, "string1")    \
  X(ENUM2, "string2")    \
  X(ENUM3, "string3")    
#else
  #define var_list       \
  X(ENUM1, "string1")    \
  X(ENUM2, "string2")    
#endif

Полный пример:

#include <stdio.h>

#define FLAG 1

#if FLAG 
  #define var_list       \
  X(ENUM1, "string1")    \
  X(ENUM2, "string2")    \
  X(ENUM3, "string3")    
#else
  #define var_list       \
  X(ENUM1, "string1")    \
  X(ENUM2, "string2")    
#endif


typedef enum
{
  #define X(enum_var, str) enum_var,
    var_list
  #undef X

  ENUM_N
} enum_var_t;

typedef struct
{
  char name[50];
  int  val;
} name_val_map_t;

const name_val_map_t name_val_map_table[] = 
{
  #define X(enum_var, str) { .name = str, .val = enum_var },
    var_list
  #undef X
};

int main (void)
{
  for(size_t i=0; i<ENUM_N; i++)
  {
    printf("%d %s\n", name_val_map_table[i].val, name_val_map_table[i].name);
  }
}
...