Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.57/7: Рейтинг темы: голосов - 7, средняя оценка - 4.57
4 / 4 / 0
Регистрация: 23.02.2013
Сообщений: 151

Хак препроцессора, объясните как работает

05.02.2014, 23:46. Показов 1553. Ответов 9
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Уже кучу времени сижу как баран и питаюсь понять, как же это работает:
C++
1
2
3
4
5
6
7
8
9
10
11
#define BOOK_PP_CAT_IMPL(a, b) a ## b
#define BOOK_PP_CAT(a, b) BOOK_PP_CAT_IMPL(a, b)
#define BOOK_ID(identifier) BOOK_PP_CAT(auroraDetail_, identifier)
#define BOOK_LINE_ID(identifier) BOOK_PP_CAT(BOOK_ID(identifier), __LINE__)
 
#define FOREACH(declaration, container)                                                                                                         \
    if (bool BOOK_LINE_ID(broken) = false) {} else                                                                                              \
    for (auto BOOK_LINE_ID(itr) = (container).begin(); BOOK_LINE_ID(itr) != (container).end() && !BOOK_LINE_ID(broken); ++BOOK_LINE_ID(itr))    \
    if (bool BOOK_LINE_ID(passed) = false) {} else                                                                                              \
    if (BOOK_LINE_ID(broken) = true, false) {} else                                                                                             \
    for (declaration = *BOOK_LINE_ID(itr); !BOOK_LINE_ID(passed); BOOK_LINE_ID(passed) = true, BOOK_LINE_ID(broken) = false)
Объясните, во что будет разворачиваться такой макрос и как он работает

Добавлено через 28 минут
uP....
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
05.02.2014, 23:46
Ответы с готовыми решениями:

Объясните вывод препроцессора
Обьясните, толковые люди, но подробно, почему вывод будет HelloWorldXY ? #include <iostream> #define X Hello #define Y World...

Директивы препроцессора.Ошибка #error не работает.
Ребят программа должна выдавать сообщение об ошибке и прекращать компиляцию.,если,X или Y неопределены Делаю #error,но почему-то...

Объясните как работает this -> и ->
Как написано в книжке this - это указатель на экземпляр класса. Что происходит в данных выражениях: 1. i = this -> a -> b ->...

9
Временно недоступен
 Аватар для #pragma
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
06.02.2014, 02:23
Попробуй почитать это. Там, по идее, всё подробно расписано https://www.cyberforum.ru/blogs/18334/blog100.html
1
 Аватар для noname_club
112 / 103 / 12
Регистрация: 01.05.2013
Сообщений: 603
06.02.2014, 11:41
пример foreach цикла для векторов

C++
1
2
3
#define VECTOR_FOREACH(__vector__)  \
if (__vector__.size()>0) \
for (int FOREACH_index = 0; FOREACH_index < __vector__.size(); FOREACH_index++)
применение

C++
1
2
3
4
5
vector<int> z;
// ... bla bla
VECTOR_FOREACH(z) {
   cout << z[FOREACH_index] << endl;
}
0
Временно недоступен
 Аватар для #pragma
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
06.02.2014, 19:09
Цитата Сообщение от noname_club Посмотреть сообщение
пример foreach цикла для векторов
C++
1
2
3
#define VECTOR_FOREACH(__vector__)  \
if (__vector__.size()>0) \
for (int FOREACH_index = 0; FOREACH_index < __vector__.size(); FOREACH_index++)
Хотя больше для C подходит
C
1
2
3
4
#define VECTOR_FOREACH(type, iterator, vector) \
    for(type iterator = vectorGetFirst(vector) ; \
        iterator ;\
        iterator = listGetNext(vector))
0
4 / 4 / 0
Регистрация: 23.02.2013
Сообщений: 151
08.02.2014, 16:13  [ТС]
#pragma, читал эту статью когда то. Но все равно не понимаю во что развернется макрос при подстановке, например вектора.

Не по теме:

Думаю, что никто уже и не объяснит.



Добавлено через 1 минуту
noname_club, *сарказм* ваш код гениален и вполне годен в качестве ответа на мой вопрос *сарказм*
0
Каратель
Эксперт С++
6610 / 4029 / 401
Регистрация: 26.03.2010
Сообщений: 9,273
Записей в блоге: 1
08.02.2014, 16:23

Не по теме:

Цитата Сообщение от anubis1768 Посмотреть сообщение
Уже кучу времени сижу как баран и питаюсь понять, как же это работает:
Объясните, во что будет разворачиваться такой макрос и как он работает
ага щаз, вывалил чью-то какашку
Цитата Сообщение от anubis1768 Посмотреть сообщение
#define FOREACH(declaration, container)
а вы мне объясните...к автору кода и обращайся, а нет так используй for



Добавлено через 1 минуту
Цитата Сообщение от anubis1768 Посмотреть сообщение
#define BOOK_ID(identifier) BOOK_PP_CAT(auroraDetail_, identifier)
откуда тут взялся auroraDetail_ ? что это?
0
4 / 4 / 0
Регистрация: 23.02.2013
Сообщений: 151
08.02.2014, 17:16  [ТС]
Jupiter, вот и я не знаю что это. Но как то компилируется и даже работает. Поэтому я и хочу услышать объяснение. Вот полный листинг файла
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef BOOK_FOREACH_HPP
#define BOOK_FOREACH_HPP
 
// Preprocessor trick to allow nested loops
#define BOOK_PP_CAT_IMPL(a, b) a ## b
#define BOOK_PP_CAT(a, b) BOOK_PP_CAT_IMPL(a, b)
#define BOOK_ID(identifier) BOOK_PP_CAT(auroraDetail_, identifier)
#define BOOK_LINE_ID(identifier) BOOK_PP_CAT(BOOK_ID(identifier), __LINE__)
 
 
// Macro to emulate C++11 range-based for loop
// Instead of for (decl : range) you write FOREACH(decl, range) as in the following example
//
// std::vector<int> v = ...;
// FOREACH(int& i, v)
// {
//     i += 2;
// }
#define FOREACH(declaration, container)                                                                                                         \
    if (bool BOOK_LINE_ID(broken) = false) {} else                                                                                              \
    for (auto BOOK_LINE_ID(itr) = (container).begin(); BOOK_LINE_ID(itr) != (container).end() && !BOOK_LINE_ID(broken); ++BOOK_LINE_ID(itr))    \
    if (bool BOOK_LINE_ID(passed) = false) {} else                                                                                              \
    if (BOOK_LINE_ID(broken) = true, false) {} else                                                                                             \
    for (declaration = *BOOK_LINE_ID(itr); !BOOK_LINE_ID(passed); BOOK_LINE_ID(passed) = true, BOOK_LINE_ID(broken) = false)
 
#endif // BOOK_FOREACH_HPP
Добавлено через 13 секунд
Jupiter, вот и я не знаю что это. Но как то компилируется и даже работает. Поэтому я и хочу услышать объяснение. Вот полный листинг файла
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef BOOK_FOREACH_HPP
#define BOOK_FOREACH_HPP
 
// Preprocessor trick to allow nested loops
#define BOOK_PP_CAT_IMPL(a, b) a ## b
#define BOOK_PP_CAT(a, b) BOOK_PP_CAT_IMPL(a, b)
#define BOOK_ID(identifier) BOOK_PP_CAT(auroraDetail_, identifier)
#define BOOK_LINE_ID(identifier) BOOK_PP_CAT(BOOK_ID(identifier), __LINE__)
 
 
// Macro to emulate C++11 range-based for loop
// Instead of for (decl : range) you write FOREACH(decl, range) as in the following example
//
// std::vector<int> v = ...;
// FOREACH(int& i, v)
// {
//     i += 2;
// }
#define FOREACH(declaration, container)                                                                                                         \
    if (bool BOOK_LINE_ID(broken) = false) {} else                                                                                              \
    for (auto BOOK_LINE_ID(itr) = (container).begin(); BOOK_LINE_ID(itr) != (container).end() && !BOOK_LINE_ID(broken); ++BOOK_LINE_ID(itr))    \
    if (bool BOOK_LINE_ID(passed) = false) {} else                                                                                              \
    if (BOOK_LINE_ID(broken) = true, false) {} else                                                                                             \
    for (declaration = *BOOK_LINE_ID(itr); !BOOK_LINE_ID(passed); BOOK_LINE_ID(passed) = true, BOOK_LINE_ID(broken) = false)
 
#endif // BOOK_FOREACH_HPP
0
3258 / 2060 / 351
Регистрация: 24.11.2012
Сообщений: 4,909
08.02.2014, 18:48
Лучший ответ Сообщение было отмечено anubis1768 как решение

Решение

Дисклеймер: пост на опечатки не проверял.

Давайте разбираться.
Просто раскручивая эту магию, наверное, сложно будет понять почему автор написал именно так. Попробуем для начала сами написать простой макрос, который будет работать. Или хотя бы делать вид, что работает.

C++
1
2
3
4
5
6
7
#ifndef BOOK_FOREACH_HPP
#define BOOK_FOREACH_HPP
 
#define FOREACH(declaration, container) \
    for (auto itr = (container).begin(); itr != (container).end(); ++itr)
 
#endif //BOOK_FOREACH_HPP
Тут, я так полагаю, все предельно ясно? Одна строчка, которая заменяется на привычный цикл с использованием итераторов.
Вот только работать не будет, поскольку мы забыли о переменной, которую будем использовать для доступа к текущему элементу.
Возникает вопрос: где ее разместить? Отметим, что просто так влепить ее абы куда нельзя - важно, чтобы переменная после отработки препроцессора сохранила свою область видимости.
Очень хочется сделать нечто такое:

C++
1
2
3
4
#define FOREACH(declaration, container) \
    for (auto itr = (container).begin(); itr != (container).end(); ++itr) { \
        declaration = *itr; \
    }
Но представьте что будет после раскрытия такого макроса. Код, который будет располагаться после него, окажется за пределами блока, который мы только что написали. Воспользуемся таким приемом:

C++
1
2
3
#define FOREACH(declaration, container) \
    for (auto itr = (container).begin(); itr != (container).end(); ++itr) \
        for (declaration = *itr; ;)
Т.е. переменную поместили в первый блок оператора for - который выполняется в начале цикла (обычно там и располагают объявления счетчиков). Это отличное место, поскольку другие операторы (if, while) стали бы проверять значение, возвращаемое оператором присваивания, и цикл мог бы тут же завершиться, например, на элементе, равном нулю.

Проблема заключается в том, что работать этот код все равно не будет. Вложенный цикл, который мы добавили для объявления переменной, бесконечен. Следовательно, нужно предусмотреть условие выхода. Ответим на вопрос: когда из него нужно выйти? Очевидно, нужна всего одна итерация, поскольку вложенных циклов мы не хотим. Нам просто нужно было место для объявления переменной. Так давайте добавим переменную, которая сначала будет true, а после первого прохода изменится на false.

Возникает проблема: где объявить этот флаг, чтобы его область видимости ограничивалась циклом?
Конструкция

C++
1
for (int a = 1, bool passed = false;;)
некорректна.

Но мы можем обернуть внутренний цикл в условный оператор, в котором уже объявить нужный флаг. Тогда код примет вид:
C++
1
2
3
4
5
#define FOREACH(declaration, container) \
    for (auto itr = container.begin(); itr != container.end(); ++itr) \
        if (bool passed = false) {} \
        else \
            for (declaration = *itr, bool passed = false; !passed; passed = true)
Вот так хитро мы сначала объявили флаг passed = false и тут же перешли к выполнению цикла. Условие false не срабатывает, поэтому для начала выполнения цикла требуется еще и блок else. И уже после первой итерации цикла мы меняем значение флага на true, чтобы выйти из внутреннего вспомогательного цикла.

Попробуем последний вариант в бою:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "foreach.h"
#include <iostream>
#include <vector>
 
int main()
{
    std::vector<int> v {1, 2, 3, 4, 5};
 
    FOREACH(int& i, v)
    {
        std::cout << i << " ";
    }
    std::cout << std::endl;
}
Bash
1
2
3
$ g++ -std=c++11 -Wall main.cpp 
$ ./a.out 
1 2 3 4 5
Работает. Точнее, делает вид, что работает.

Вспомним, что существует оператор break, который позволяет выйти из цикла.
Если мы подставим его: к какому циклу он будет относиться? Правильно, к нашему внутреннему вспомогательному циклу.
Попробуем:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "foreach.h"
#include <iostream>
#include <vector>
 
int main()
{
    std::vector<int> v {1, 2, 3, 4, 5};
 
    FOREACH(int& i, v)
    {
        std::cout << i << " ";
        break;
    }
    std::cout << std::endl;
}
Bash
1
2
3
$ g++ -std=c++11 -Wall main.cpp 
$ ./a.out 
1 2 3 4 5
Итак, мы хотели прервать цикл, но из-на вспомогательного цикла сделать это не удалось. Следовательно, нам нужна еще дополнительная переменная, которая поможет определить когда нужно выйти из цикла по break.

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

C++
1
2
3
4
5
6
7
8
9
#define FOREACH(declaration, container) \
    if (bool broken = false) {} \
    else \
        for (auto itr = container.begin(); itr != container.end() && !broken; ++itr) \
            if (bool passed = false) {} \
            else \
                if (broken = true, false) {} \
                else \
                    for (declaration = *itr; !passed; passed = true, broken = false)
Bash
1
2
$ ./a.out 
1
Что добавили? Немного: в самом начала объявление broken = false. Затем во внешнем цикле проверку: не прерван ли он. Перед внутренним циклом присваиваем broken = true, а после первой же итерации если не было встречено оператора break - снова broken = false, что означает - внешний цикл по коллекции нужно продолжать.

Вот мы и восстановили в полном виде сам цикл.
Как видим, в коде еще присутствует пачка макросов BOOK_. Тут все совсем просто. Вспомним, что циклы могут быть вложенными. Следовательно, наши итераторы, которых мы наобъявляли, будут конфликтовать. Так вот задача этих макросов - предотвращение конфликта имен путем добавления своего префикса (auroraDetail_) и номера строки, в котором был объявлен итератор (## - оператор конкатенации, __LINE__ - Текущая строка в файле). Не вижу смысла расписывать тут подробнее
1
4 / 4 / 0
Регистрация: 23.02.2013
Сообщений: 151
08.02.2014, 21:51  [ТС]
0x10, все понятно, спасибо большое за такой ответ. Эти BOOK макросы очень заплутали меня, никак не мог подумать, что они используются для разрешения конфликтов имен.
0
Временно недоступен
 Аватар для #pragma
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
09.02.2014, 02:24
Цитата Сообщение от anubis1768 Посмотреть сообщение
#pragma, читал эту статью когда то. Но все равно не понимаю во что развернется макрос при подстановке, например вектора.
Не по теме:
Думаю, что никто уже и не объяснит.
Значит, либо не внимательно читал, либо тогда это ещё не было написано. Вот, это из статьи
1.2.1 Компилятор gcc (он же mingw под windows)
В компиляторе gcc есть опция -E, которая печатает в терминале результат работы препроцессора и завершает компиляцию - т.е. код формироваться не будет. Если выдача оказывается слишком большой, то её можно перенаправить в файл через ">" в командной строке, либо подать опцию "-o <file>" в запуск gcc
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
09.02.2014, 02:24
Помогаю со студенческими работами здесь

Объясните как работает %
Искал в интернете, но про % очень скудно написано. Знаю только,что используется для деления с остатком. Если можно,объясните на примере.

Объясните, как работает программа возведения числа в степень
A в степени K, как работает эта программа, за что отвечают m и s ? #include &quot;stdafx.h&quot; #include &quot;iostream&quot; using namespace...

Объясните, как работает программа
Поясните пожалуйста чайнику! Объясните плиз как работает программа, то есть что значит &quot;**&quot; и остальные строки плиз очень...

Объясните, как работает программа
Підскажіть будьласка як працює програма. #include &quot;stdafx.h&quot; #include&lt;iostream&gt; #include&lt;conio.h&gt; #include&lt;math.h&gt; ...

Объясните как работает код
можете подробно объяснить как работают программы. 1 #include&lt;iostream&gt; #include&lt;cmath&gt; using namespace std; int main() ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
10
Ответ Создать тему
Новые блоги и статьи
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка.
Programma_Boinc 23.12.2025
Рецензия / Мнение/ Перевод Нашел на реддите интересную статью под названием The Thinkpad X220 Tablet is the best budget school laptop period . Ниже её машинный перевод. Thinkpad X220 Tablet —. . .
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru