Как получить запятую внутри фигурных скобок внутри аргумента макроса, если скобки вызывают синтаксическую ошибку? - PullRequest
14 голосов
/ 08 февраля 2012

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

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
(группа синтаксических ошибок, возникающих в результате его интерпретации как выражения оператора)
ошибка: выражение оператора недопустимо в области видимости файла

Как можно:

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

Ответы [ 2 ]

14 голосов
/ 08 февраля 2012

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

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

#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__.

Обновление: Необработанное решение, если вам не нравится __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;
}
1 голос
/ 08 февраля 2012

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

#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")
});
...