Как у меня есть запятая внутри фигурных скобок внутри аргумента макроса, когда скобки вызывают синтаксическую ошибку?

Я определил несколько макросов, которые упрощают определение массива структур, но я не могу найти способ использовать их без возникновения ошибок. Вот макросы (и несколько примеров структуры, чтобы показать, почему макросы могут быть использованы (фактические структуры, которые я заполняю, немного сложнее)):

struct string_holder {
    const char *string;
};
struct string_array_holder {
    struct string_holder *holders;
};
#define DEFINE_STRING_ARRAY_HOLDER(name, values) \
    static struct string_holder name##__array[] = values; \
    static struct string_array_holder name = { name##__array }
#define WRAP_STRING(string) { string }

Он отлично работает, когда вы используете его для объявления массива с одним элементом:

DEFINE_STRING_ARRAY_HOLDER(my_string_array_holder, {
    WRAP_STRING("my string")
});

Но когда я использую несколько элементов:

DEFINE_STRING_ARRAY_HOLDER(my_string_array_holder, {
    WRAP_STRING("hello"),
    WRAP_STRING("world")
});

Я получаю эту ошибку:

ошибка: слишком много аргументов, предоставляемых функционально подобранному вызову макросов

Поэтому он интерпретирует запятую в фигурных скобках как разделитель аргументов. Я следую советам этого вопроса и поставлю круглые скобки вокруг проблемного аргумента:

DEFINE_STRING_ARRAY_HOLDER(my_string_array_holder, ({
    WRAP_STRING("hello"),
    WRAP_STRING("world")
}));

Теперь, когда я пытаюсь скомпилировать его, он интерпретирует ({ ... }) как выражение выражения и жалуется:

предупреждение: использование выражения выражения выражения GNU
(куча синтаксических ошибок, возникающих в результате его интерпретации как выражение оператора)
ошибка: выражение оператора не допускается в области видимости файла

Как я могу:

  • Используйте макрос без ошибок (предпочтительно) или
  • Перепишите макрос [s], чтобы работать в этих обстоятельствах?

Ответы

Ответ 1

Дмитрий прав, переменные макросы - это путь.

Я помещаю некоторый пример кода, который я использую для проверки, если данный ключ является членом списка значений:

#define _IN(KEY, ...)                                             \
({                                                                \
  typedef __typeof (KEY) _t;                                      \
  const _t _key = (KEY);                                          \
  const _t _values[] = { __VA_ARGS__ };                           \
  _Bool _r = 0;                                                   \
  unsigned int _i;                                                \
  for (_i = 0; _i < sizeof (_values) / sizeof (_values[0]); ++_i) \
    if (_key == _values[_i])                                      \
      {                                                           \
        _r = 1;                                                   \
        break;                                                    \
      }                                                           \
  _r;                                                             \
})

Обратите внимание на использование __VA_ARGS__.

Update: Неочищенное решение, если вам не нравится __VA_ARGS__ в произвольных местах, будет макросом "развертки":

#define UNWRAP(...) __VA_ARGS__

Вы можете использовать его как префикс-оператор.; -)

#include <stdio.h>

/* "unwrapper": */
#define UNWRAP(...) __VA_ARGS__

/* your macros: */
#define WRAP(NAME, ELEMS) static const char *NAME[] = { UNWRAP ELEMS }

int
main (void)
{
  WRAP (some_test, ("a", "b", "c"));
  printf ("The second elem in some_test is: '%s'\n", some_test[1]);
  return 0;
}

Ответ 2

Да, используйте __VA_ARGS__, но Kay решение слишком сложно. Просто:

#define DEFINE_STRING_ARRAY_HOLDER(name, ...)                 \
    static struct string_holder name##_array[] = __VA_ARGS__; \
    static struct string_array_holder name = { name##_array }

достаточно. Затем вы можете использовать этот макрос, как вы планировали:

DEFINE_STRING_ARRAY_HOLDER(my_string_array_holder, {
    WRAP_STRING("hello"),
    WRAP_STRING("world")
});