Форум программистов, компьютерный форум CyberForum.ru

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

Восстановить пароль Регистрация
 
anubis1768
4 / 4 / 0
Регистрация: 23.02.2013
Сообщений: 151
05.02.2014, 23:46     Хак препроцессора, объясните как работает #1
Уже кучу времени сижу как баран и питаюсь понять, как же это работает:
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....
Лучшие ответы (1)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
05.02.2014, 23:46     Хак препроцессора, объясните как работает
Посмотрите здесь:

C++ Директивы препроцессора.Ошибка #error не работает.
Объясните, как работает программа C++
C++ Объясните как работает this -> и ->
Объясните как работает программа C++
qsort объясните как работает C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
06.02.2014, 02:23     Хак препроцессора, объясните как работает #2
Попробуй почитать это. Там, по идее, всё подробно расписано http://www.cyberforum.ru/blogs/18334/blog100.html
noname_club
 Аватар для noname_club
100 / 88 / 9
Регистрация: 01.05.2013
Сообщений: 563
06.02.2014, 11:41     Хак препроцессора, объясните как работает #3
пример 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;
}
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
06.02.2014, 19:09     Хак препроцессора, объясните как работает #4
Цитата Сообщение от 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))
anubis1768
4 / 4 / 0
Регистрация: 23.02.2013
Сообщений: 151
08.02.2014, 16:13  [ТС]     Хак препроцессора, объясните как работает #5
#pragma, читал эту статью когда то. Но все равно не понимаю во что развернется макрос при подстановке, например вектора.

Не по теме:

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



Добавлено через 1 минуту
noname_club, *сарказм* ваш код гениален и вполне годен в качестве ответа на мой вопрос *сарказм*
Jupiter
Каратель
Эксперт C++
6542 / 3962 / 226
Регистрация: 26.03.2010
Сообщений: 9,273
Записей в блоге: 1
Завершенные тесты: 2
08.02.2014, 16:23     Хак препроцессора, объясните как работает #6

Не по теме:

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



Добавлено через 1 минуту
Цитата Сообщение от anubis1768 Посмотреть сообщение
#define BOOK_ID(identifier) BOOK_PP_CAT(auroraDetail_, identifier)
откуда тут взялся auroraDetail_ ? что это?
anubis1768
4 / 4 / 0
Регистрация: 23.02.2013
Сообщений: 151
08.02.2014, 17:16  [ТС]     Хак препроцессора, объясните как работает #7
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
0x10
2425 / 1597 / 232
Регистрация: 24.11.2012
Сообщений: 3,919
08.02.2014, 18:48     Хак препроцессора, объясните как работает #8
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Дисклеймер: пост на опечатки не проверял.

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

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__ - Текущая строка в файле). Не вижу смысла расписывать тут подробнее
anubis1768
4 / 4 / 0
Регистрация: 23.02.2013
Сообщений: 151
08.02.2014, 21:51  [ТС]     Хак препроцессора, объясните как работает #9
0x10, все понятно, спасибо большое за такой ответ. Эти BOOK макросы очень заплутали меня, никак не мог подумать, что они используются для разрешения конфликтов имен.
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
09.02.2014, 02:24     Хак препроцессора, объясните как работает
Еще ссылки по теме:

C++ Объясните как работает код
C++ Объясните, как работает программа
Объясните как работает программа C++

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

Или воспользуйтесь поиском по форуму:
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
09.02.2014, 02:24     Хак препроцессора, объясните как работает #10
Цитата Сообщение от anubis1768 Посмотреть сообщение
#pragma, читал эту статью когда то. Но все равно не понимаю во что развернется макрос при подстановке, например вектора.
Не по теме:
Думаю, что никто уже и не объяснит.
Значит, либо не внимательно читал, либо тогда это ещё не было написано. Вот, это из статьи
1.2.1 Компилятор gcc (он же mingw под windows)
В компиляторе gcc есть опция -E, которая печатает в терминале результат работы препроцессора и завершает компиляцию - т.е. код формироваться не будет. Если выдача оказывается слишком большой, то её можно перенаправить в файл через ">" в командной строке, либо подать опцию "-o <file>" в запуск gcc
Yandex
Объявления
09.02.2014, 02:24     Хак препроцессора, объясните как работает
Ответ Создать тему
Опции темы

Текущее время: 13:50. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru