Специальные операторы '#' и '##' внутри определений #define

Внутри определений макросов можно использовать два специальных оператора:

  • Одиночный символ решетки '#', стоящий перед именем параметра макроса, превращает содержимое этого параметра в строку. Разрешен только в макросах-функциях.
  • Сдвоенный символ решетки '##', стоящий между двумя словами (токенами) объединяет их. Причем если токен является параметром макроса, то подставляется его значение, а если токен — имя макроса, оно подставляется как есть, без раскрытия макроса. Если в результате "склейки" получается другое имя макроса, он раскрывается.

В примерах данной книги мы часто использовали следующий макрос:

#define PRT(APrint(#A"=", (A))

Он вызывает функцию Print, в которой переданное выражение отображается в виде строки благодаря #A, а после знака равно выводится актуальное значение A.

Для демонстрации '##' рассмотрим другой макрос:

#define COMBINE(A,B,XA##B(X)

С помощью него мы можем фактически сгенерировать вызов определенного выше макроса SQN:

Print(COMBINE(SQ,N,2)); // 4

Литералы SQ и N объединяются, после чего макрос SQN раскрывается в ((2)*(2)) и дает результат 4.

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

#define VAR(TYPE,NTYPE var##N = N

Тогда строка кода:

VAR(int3);

эквивалентна записи:

int var3 = 3;

Сцепка токенов позволяет реализовать с помощью макроса краткую форму записи цикла по элементам массива.

#define for_each(IAfor(int I = 0max_##I = ArraySize(A); I < max_##I; ++I)
  
// описываем и как-то заполняем массив x
double x[];
// ...
// выполняем цикл по массиву
for_each(ix)
{
   x[i] = i * i;
}