Что значит ## в #define? - PullRequest
30 голосов
/ 28 июня 2011

Что означает эта строка? Особенно, что означает ##? 1002 *

#define ANALYZE(variable, flag)     ((Something.##variable) & (flag))

Edit:

Немного растерялся. Каким будет результат без ##?

Ответы [ 6 ]

17 голосов
/ 28 июня 2011

Немного смущен.Каков будет результат без ##?

Обычно вы не заметите никакой разницы.Но есть разница .Предположим, что Something имеет тип:

struct X { int x; };
X Something;

И посмотрите на:

int X::*p = &X::x;
ANALYZE(x, flag)
ANALYZE(*p, flag)

Без оператора конкатенации токенов ## он расширяется до:

#define ANALYZE(variable, flag)     ((Something.variable) & (flag))

((Something. x) & (flag))
((Something. *p) & (flag)) // . and * are not concatenated to one token. syntax error!

С конкатенацией токенов она расширяется до:

#define ANALYZE(variable, flag)     ((Something.##variable) & (flag))

((Something.x) & (flag))
((Something.*p) & (flag)) // .* is a newly generated token, now it works!

Важно помнить, что препроцессор работает с токенами препроцессора, не с текстом.Поэтому, если вы хотите объединить два токена, вы должны явно сказать это.

12 голосов
/ 28 июня 2011

## называется конкатенацией токенов, используемой для конкатенации двух токенов при вызове макроса.

Смотрите это:

9 голосов
/ 28 июня 2011

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

например. IBM doc:

  • Конкатенация происходит перед любым макросы в аргументах раскрыты.
  • Если результатом конкатенации является допустимое имя макроса , оно доступно для дальнейшая замена, даже если это появляется в контексте, в котором это обычно не будет доступен.
  • Если больше чем один ## оператор и / или # оператор появляется в замене список макроопределения, порядок оценки операторов не определены.

Примеры также очень самоочевидны

#define ArgArg(x, y)          x##y
#define ArgText(x)            x##TEXT
#define TextArg(x)            TEXT##x
#define TextText              TEXT##text
#define Jitter                1
#define bug                   2
#define Jitterbug             3

С выводом:

ArgArg(lady, bug)   "ladybug"
ArgText(con)    "conTEXT"
TextArg(book)   "TEXTbook"
TextText    "TEXTtext"
ArgArg(Jitter, bug)     3

Источник - документация IBM. Может отличаться в зависимости от других компиляторов.

На вашу линию:

Он объединяет атрибут переменной с «Кое-что». и обращается к переменной, которая логически аннулируется, что дает результат, если у Something.variable установлен флаг.

Вот пример моего последнего комментария и вашего вопроса (компилируется с g ++):

// this one fails with a compiler error
// #define ANALYZE1(variable, flag)     ((Something.##variable) & (flag))
// this one will address Something.a (struct)
#define ANALYZE2(variable, flag)     ((Something.variable) & (flag))
// this one will be Somethinga (global)
#define ANALYZE3(variable, flag)     ((Something##variable) & (flag))
#include <iostream>
using namespace std;

struct something{
int a;
};

int Somethinga = 0;

int main()
{
something Something;
Something.a = 1;

if (ANALYZE2(a,1))
    cout << "Something.a is 1" << endl;
if (!ANALYZE3(a,1))
    cout << "Somethinga is 0" << endl;
        return 1;
};
3 голосов
/ 28 июня 2011

Это не ответ на ваш вопрос, просто сообщение CW с некоторыми советами, которые помогут вам самостоятельно исследовать препроцессор.

Этап предварительной обработки фактически выполняется перед компиляцией любого фактического кода. Другими словами, когда компилятор начинает собирать ваш код, никаких # определения операторов или чего-либо подобного не остается.

Хороший способ понять, что препроцессор делает с вашим кодом, - это получить предварительно обработанный вывод и посмотреть на него.

Вот как это сделать для Windows:

Создайте простой файл с именем test.cpp и поместите его в папку, скажем, c: \ temp. Моя выглядит так:

#define dog_suffix( variable_name ) variable_name##dog

int main()
{
  int dog_suffix( my_int ) = 0;
  char dog_suffix( my_char ) = 'a';

  return 0;
}

Не очень полезно, но просто. Откройте командную строку Visual Studio, перейдите к папке и выполните следующую командную строку:

c:\temp>cl test.cpp /P

Итак, ваш компилятор (cl.exe) работает с вашим файлом, а параметр / P указывает компилятору сохранить предварительно обработанный вывод в файл.

Теперь в папке рядом с test.cpp вы найдете test.i, который для меня выглядит так:

#line 1 "test.cpp"


int main()
{
  int my_intdog = 0;
  char my_chardog = 'a';

  return 0;
}

Как видите, # define не осталось, только код, в который он был расширен.

3 голосов
/ 28 июня 2011

давайте рассмотрим другой пример:

рассмотрим

#define MYMACRO(x,y) x##y

без ##, ясно, что препроцессор не может видеть x и y как отдельные токены, не так ли?

В вашем примере

#define ANALYZE(variable, flag)     ((Something.##variable) & (flag))

## просто не требуется, так как вы не создаете новый идентификатор.Фактически, компилятор выдает «error: paste». », А« variable »не дает действительный токен предварительной обработки"

3 голосов
/ 28 июня 2011

Согласно Википедии

Конкатенация токенов, также называемая вставкой токенов, является одной из самых тонких и простых в использовании функций препроцессора макроса C.Два аргумента могут быть «склеены» вместе с помощью оператора препроцессора ##;это позволяет объединить два токена в предварительно обработанном коде.Это может использоваться для создания сложных макросов, которые действуют как сырая версия шаблонов C ++.

Проверка Конкатенация токенов

...