1

Тема: ## оператор препроцесора в C і в C++

Не часто таке бачу, оператор ## знайшов в файлі <stdint.h>

#define UINT32_C(x)  (x ## U)
#define UINT64_C(x)  (x ## ULL)

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

Пошук в мережі дуже скупий, але якісь результати таки знайшов: "c macros ##" --> "# і ## в макросах" (en).
Пишуть що ## конкатенує літерали, і там приклад :

#include <stdio.h>
#define f(a,b) a##b
#define g(a) #a
#define h(a) g(a)

int main(int argc, char **argv) {
    printf("%s\n", h(f(1,2)));  // 12
    printf("%s\n", g(f(1,2)));  // f(1,2)
    return 0;
}

Якщо маєте коменти, то кидайте.

2

Re: ## оператор препроцесора в C і в C++

The # operator, ## operator.
З власних архівів:

#define _JOIN(A, B) A##B
#define  JOIN(A, B) _JOIN(A, B)

#define CREATE_UNIQUE_NAME(BASE) JOIN(BASE, __COUNTER__)

#define _STRINGIFY(X) #X
#define  STRINGIFY(X) _STRINGIFY(X)

Допомагає з кодогенерацією, використовувати можна всюди, де дуже необхідно :)

Подякували: leofun01, koala2

3 Востаннє редагувалося leofun01 (12.02.2024 22:59:54)

Re: ## оператор препроцесора в C і в C++

wander, а чому кожний з них по 2 рази ? (JOIN->_JOIN, STRINGIFY->_STRINGIFY)
Це якийсь хак щоб змусити користувача не напхати зайвого ? чи щоб простіше було ловити синтаксні помилки ?

4

Re: ## оператор препроцесора в C і в C++

Хороше запитання, оскільки я це зберігав з якогось давнього тест-пет-проєкту і вже й не пам'ятаю для чого саме, то можу припустити, що це використовувалось в поєднанні з чимось ще (що, однак, у мене не збереглось)..
У наведеному прикладі без контексту сенсу у цьому, вочевидь, ніякого немає.

UPD. Пригадав, макроси що починаються з _ - виконують операцію над препроцесорними токенами без розкриття визначень макросів (without expanding macro definitions). Ті, що без - виконують операцію над препроцесорними токенами після розкриття макросів (after macro-expanding).

Наприклад:

int _JOIN(s, 1); // expands to -> int s1;
int  JOIN(s, 2); // expands to -> int s2;

#define s z
int _JOIN(s, 1); // expands to -> int s1;
int  JOIN(s, 2); // expands to -> int z2;
// JOIN - розкриватиме макрос у `<value of s>2`
Подякували: leofun01, koala2

5

Re: ## оператор препроцесора в C і в C++

Всі макроси працюють однаково. Але розкриття відбувається по черзі.
Проміжний макрос потрібен для того, щоб під #, ## потрапили вже розкриті аргументи.
Тобто на першому етапі робиться розкриття самого макроса, а його аргументи підставляються як є.
Хоча б для того, щоб з таким:

#if defined(NDEBUG)
#  define assert(e)    ((void)0)
#else /* !NDEBUG */
#  define assert(e)    ((e) ? (void)0 : __assert(__func__, __FILE__, __LINE__, #e))
#endif /* NDEBUG */
  assert(MAX(aa,bb) <= SIZE_LIMIT);

рядок повідомлення був "MAX(aa,bb) <= SIZE_LIMIT",
а не містив розгорнуті MAX і SIZE_LIMIT (Ну, наприклад, 512U - (sizeof(ll_header_t) + sizeof(rt_header_t)))

Якщо є проміжний макрос, то спочатку буде підставлене його ім'я і розкриті аргументи, потім він буде виконаний.

Подякували: leofun011

6

Re: ## оператор препроцесора в C і в C++

wander написав:

Допомагає з кодогенерацією, використовувати можна всюди, де дуже необхідно :)

Егеж (тиць)

Подякували: wander, leofun012

7

Re: ## оператор препроцесора в C і в C++

До речі, «трохи історії».
Колись давно оцього ## не існувало.
Чесно не пам'ятаю коли — «до С99» чи «до С89», тобто лише у до-ансішному С.
А препроцесор тоді коментарі просто прибирав. У буквальному, тобто політерному сенсі.
От тоді це виглядало отак:

#define _JOIN(a,b) a/**/b

(зараз він такий коментар заміняє на пробіл)

Подякували: leofun011

8

Re: ## оператор препроцесора в C і в C++

Мій локальний компілятор в цей час не вміє працювати з __VA_ARGS__ і __VA_OPT__. Мікромякі пропонують пхати костилі і ломати код повністю.

9 Востаннє редагувалося Droid 77 (29.03.2024 18:20:39)

Re: ## оператор препроцесора в C і в C++

leofun01 написав:

Мій локальний компілятор в цей час не вміє працювати з __VA_ARGS__ і __VA_OPT__. Мікромякі пропонують пхати костилі і ломати код повністю.

На фіга пхати костилі коли то все форматований ввід\вивід ?

10 Востаннє редагувалося Kizyak (30.03.2024 01:19:00)

Re: ## оператор препроцесора в C і в C++

leofun01 написав:

Мій локальний компілятор в цей час не вміє працювати з __VA_ARGS__ і __VA_OPT__. Мікромякі пропонують пхати костилі і ломати код повністю.

Наскільки я розумію, Variable Arguments працює не для всіх Call Convension.
Якщо код кладе параметри функції в стек, ними можна пройтись через вказівник на адресу першого аргументу функції (як воно за першочерговим задумом повинно було б бути).
Але, до прикладу, GCC по замовчуванню для amd64 використовує fastcall, де перші N аргументів в залежності від типу передаються через регістри процесора (відповідно вказівиком як по спику по ним не пройтись).
Тоді треба явно вказати Call Convention, що використовує стек для передачі аргументів функції. До прикладу:

void __stdcall foo(const char* format, ...);

PS. перепрошую, мова не про це в темі  *FACEPALM*

11

Re: ## оператор препроцесора в C і в C++

Значить так, Droid 77 і Kizyak йдуть читати про variadic macros, вивчайуть шо воно таке, і тільки після того можуть шось тут написати.