Подтвердить что ты не робот

Что означает ## в #define?

Что означает эта линия? В частности, что означает ##?

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

Edit:

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

4b9b3361

Ответ 1

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

Обычно вы не заметите никакой разницы. Но есть разница. Предположим, что 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!

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

Ответ 3

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

например. 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;
};

Ответ 4

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

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

Отметьте Конкатенация токена

Ответ 5

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

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

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

Вот как это сделать для 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, а только код, в который он был добавлен.

Ответ 6

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

рассмотрим

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

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

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

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

## просто не требуется, поскольку вы не создаете новый идентификатор. На самом деле компилятор выпускает "error: inserting". "И" variable "не дает допустимого токена предварительной обработки"