Программирование на C: препроцессор, макросы как токены

Я пытаюсь сделать что-то концептуально похожее на это, но не могу заставить его работать (ошибка показана в конце) какие-либо идеи?

#include <stdio.h>

int main( int argc , char const *argv[] )
{
  int abc_def_ghi = 42;
  #define SUFFIX ghi
  #define VAR(prefix) prefix##_def_##SUFFIX
  printf( "%d\n" , VAR(abc) );
  return 0;
}

// untitled:8: error: ‘abc_def_SUFFIX’ undeclared (first use in this function)

person Joshua Cheek    schedule 20.11.2009    source источник
comment
const argv? Впервые вижу такое! Идея кажется хорошей, но я не уверен, что она мне нравится: она делает main несоответствующим и мешает мне делать то, чего я никогда не делал;)   -  person pmg    schedule 20.11.2009
comment
Это не несоответствие. argv можно объявить как нечто эквивалентное char* argv[] (C99 5.1.2.2.1), и добавление const не меняет ничего, кроме того, что main() может с ним делать (без приведения). Помните, что указатель на неконстантный можно без проблем преобразовать в указатель на константу - вплоть до того факта, что значения этих указателей будут сравниваться равными (6.3.2.3/2).   -  person Michael Burr    schedule 20.11.2009
comment
Хорошо, спасибо, Майкл. Это наоборот (удаление const), что сделало бы его несоответствующим. Мне это начинает нравиться!   -  person pmg    schedule 20.11.2009
comment
Привет, извини за отвлечение, мне следовало убрать это. При использовании TextMate ввод main, за которым следует табуляция, заполняет основу для основного метода, и это его часть.   -  person Joshua Cheek    schedule 21.11.2009


Ответы (3)


Вам просто нужно дополнительное косвенное обращение:

#include <stdio.h>

int main( int argc , char const *argv[] )
{
  int abc_def_ghi = 42;
  #define SUFFIX ghi
  #define VAR3(prefix, suffix) prefix##_def_##suffix
  #define VAR2(prefix, suffix) VAR3(prefix, suffix)
  #define VAR(prefix) VAR2(prefix, SUFFIX)
  printf( "%d\n" , VAR(abc) );
  return 0;
}

Хотя это выглядит избыточным, это не так.

person caf    schedule 20.11.2009
comment
Для меня уже слишком поздно пытаться понять Standardese для 6.10.3.3 The ## operator, но это объясняет, почему где-то там. (pdf @ open-std.org/JTC1/sc22 /wg14/www/docs/n1401.pdf) - person pmg; 20.11.2009
comment
Поскольку список замены макроса сам по себе не подлежит замене макроса до замены его параметров и применения операторов # и ##. Таким образом, в коде вопрошающего def_##SUFFIX заменяется на def_SUFFIX перед любой возможностью заменить SUFFIX на ghi. В коде caf, когда suffix заменяется на SUFFIX в VAR2, аргумент сначала подвергается расширению макроса (6.10.3.1/1). Результат - VAR3(abc,ghi), что дает abc_def_ghi. Обратите внимание, что VAR3(abc,SUFFIX) по-прежнему будет давать abc_def_SUFFIX, потому что параметры, следующие за ##, не раскрываются (также 6.10.2.1/1). - person Steve Jessop; 20.11.2009
comment
(тоже 6.10.2.1/1) - я имею в виду (тоже 6.10.3.1/1). - person Steve Jessop; 20.11.2009

Обычная идиома для правильного использования операторов предварительной обработки строкового преобразования (#) или вставки токена (##) заключается в использовании второго уровня косвенности. (Какие применения оператора препроцессора ## и какие ошибки следует учитывать?).

#define STRINGIFY2( x) #x
#define STRINGIFY(x) STRINGIFY2(x)

#define PASTE2( a, b) a##b
#define PASTE( a, b) PASTE2( a, b)

Потом:

int main( int argc , char const *argv[] )
{
  int abc_def_ghi = 42;
  #define SUFFIX ghi
  #define VAR(prefix) PASTE( prefix, PASTE( _def_, SUFFIX))
  printf( "%d\n" , VAR(abc) );
  return 0;
}

Должен дать вам результаты, которые вы ищете.

По сути, происходит то, что обработка операторов # и ## происходит до замены макроса. Затем происходит еще один раунд замены макроса. Поэтому, если вы хотите, чтобы макросы использовались вместе с этими операциями, вы должны использовать 1-й уровень, который просто выполняет замену - в противном случае сначала выполняется преобразование или вставка, и макросы больше не являются макросами - они соответствуют 1-му раунду нанизывание / оклейка производит.

Говоря более прямо - первый уровень макроса позволяет заменять параметры макроса, затем второй уровень замены макроса выполняет операцию строкового преобразования / вставки токена.

person Michael Burr    schedule 20.11.2009
comment
Вау, спасибо, STRINGIFY решает еще одну мою проблему. - person Joshua Cheek; 21.11.2009

Это работает с достаточным уровнем косвенности. Хотя другого ответа вполне достаточно, я хочу предложить этот фрагмент кода в качестве демонстрации:

#define SUFFIX ghi

#define VAR1(prefix) prefix##_def_##SUFFIX
VAR1(abc)

#define VAR2_(prefix, sfx) prefix##_def_##sfx
#define VAR2(prefix) VAR2_(prefix,SUFFIX)
VAR2(abc)

#define VAR3_(prefix, sfx) prefix##_def_##sfx
#define VAR3x(prefix,sfx) VAR3_(prefix,sfx)
#define VAR3(prefix) VAR3x(prefix,SUFFIX)
VAR3(abc)

Сохраните текстовый файл x.c и только предварительно обработайте его.

gcc -E x.c

Наблюдайте и размышляйте. Я сам не совсем понимаю. Просто потратьте два часа, пытаясь заставить работать макрос, использующий stringify. Интересно отметить, что иногда требуется двойное косвенное обращение.

person DarenW    schedule 16.01.2010
comment
Я получаю abc_def_SUFFIX abc_def_SUFFIX abc_def_ghi Судя по объяснению Стива, похоже, что это связано с порядком замены макросов. - person Joshua Cheek; 17.01.2010