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

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
anubis1768
4 / 4 / 0
Регистрация: 23.02.2013
Сообщений: 151
#1

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

05.02.2014, 23:46. Просмотров 540. Ответов 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)
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
05.02.2014, 23:46
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Хак препроцессора, объясните как работает (C++):

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

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

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

Объясните, как работает программа - C++
программа по трем введеным числам определяет и выводит на экран число имеющее в составе наибольше едениц. Пожалуйста, объясните подробно...

Объясните как работает программа - C++
Задание звучит так: "Разработать функцию, в которую передаются в качестве аргументов массив типа float и его размер. Функция должна...

Объясните как работает рекурсия - C++
#include <iostream> #include <iomanip> using namespace std; void print(int a, int b); int main() { print(0,...

9
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
06.02.2014, 02:23 #2
Попробуй почитать это. Там, по идее, всё подробно расписано http://www.cyberforum.ru/blogs/18334/blog100.html
1
noname_club
103 / 94 / 10
Регистрация: 01.05.2013
Сообщений: 598
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;
}
0
#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))
0
anubis1768
4 / 4 / 0
Регистрация: 23.02.2013
Сообщений: 151
08.02.2014, 16:13  [ТС] #5
#pragma, читал эту статью когда то. Но все равно не понимаю во что развернется макрос при подстановке, например вектора.

Не по теме:

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



Добавлено через 1 минуту
noname_club, *сарказм* ваш код гениален и вполне годен в качестве ответа на мой вопрос *сарказм*
0
Jupiter
Каратель
Эксперт С++
6556 / 3977 / 227
Регистрация: 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_ ? что это?
0
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
0
0x10
2465 / 1637 / 241
Регистрация: 24.11.2012
Сообщений: 4,041
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__ - Текущая строка в файле). Не вижу смысла расписывать тут подробнее
1
anubis1768
4 / 4 / 0
Регистрация: 23.02.2013
Сообщений: 151
08.02.2014, 21:51  [ТС] #9
0x10, все понятно, спасибо большое за такой ответ. Эти BOOK макросы очень заплутали меня, никак не мог подумать, что они используются для разрешения конфликтов имен.
0
#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
1
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
09.02.2014, 02:24
Привет! Вот еще темы с ответами:

Объясните как работает программа - C++
Цель задачи была: Возведение X в 59 степень за самое малое количество шагов. 1. Ввод (X); 2. X2 = X*X; 3. X4 = X2*X2; ...

Объясните как работает программа - C++
Если символ: не пробел, не новая строка, не табуляция. То in присваивается no(все слова). Дальше я вообще не понял как программа работает....

Объясните, как работает программа - C++
объясните как работает программа #include &lt;iostream&gt; #include &lt;conio.h&gt; using namespace std; bool Р(int n) { for (int i =...

объясните как это работает - C++
#include &lt;iostream&gt; #include &lt;algorithm&gt; using namespace std; long func (long * arr, int length) { return...


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

Или воспользуйтесь поиском по форуму:
10
Yandex
Объявления
09.02.2014, 02:24
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru