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

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

Восстановить пароль Регистрация
Другие темы раздела
C++ Задано массив строк произвольной длины. Отформатировать данный массив по ширине поля. http://www.cyberforum.ru/cpp-beginners/thread1090322.html
Задано массив строк произвольной длины. Отформатировать данный массив по ширине поля.
C++ Умные указатели Пишу класс дерево поиска:template<typename T, typename Compare> class AvlTree { private: struct Node { T key; size_t height; std::unique_ptr<Node> leftChild; std::unique_ptr<Node> rightChild; explicit Node(const T &key) : key(key), height(1), leftChild(nullptr), http://www.cyberforum.ru/cpp-beginners/thread1090290.html
Определите по данным, сколько банок не прострелил Гарри и сколько банок не прострелил Ларри. C++
Бандиты Гарри и Ларри отдыхали на природе. Решив пострелять, они выставили на бревно несколько банок из-под кока-колы (не больше 10). Гарри начал простреливать банки по порядку, начиная с самой левой, Ларри — с самой правой. В какой-то момент получилось так, что они одновременно прострелили одну и ту же последнюю банку. Гарри возмутился и сказал, что Ларри должен ему кучу денег за то, что тот...
C++ Перевести код из языка Pascal в C++
Помогите пожалуйста перевести код программы из языка Pascal в C++. А то у меня не совсем нормально получается. program pas; const N = 10; var i, t, k, G: integer;
C++ Написать функцию вычисления знака числа http://www.cyberforum.ru/cpp-beginners/thread1090266.html
Здравствуйте! Прошу прощения за глупый вопрос, мог бы спросить и у препода, но ждать долго, а сдать хочу досрочно) Задание прикрепил ниже. Насколько я понял, речь идет о первом знаке после запятой. Но что это за условие под фигурной скобкой?
C++ Изменить программу таким образом, чтобы ввод исходных данных осуществлялся из файла Изменить программу,я её закинул архивом , таким образом чтобы ввод исходных данных осуществлялся из файла, результат также должен выводиться в файл. #include <stdio.h> #include <conio.h> #include <stdio.h> #include <conio.h> #include <vcl.h> #include <iostream.h> #pragma hdrstop //--------------------------------------------------------------------------- подробнее

Показать сообщение отдельно
0x10
2425 / 1597 / 232
Регистрация: 24.11.2012
Сообщений: 3,919
08.02.2014, 18:48     Хак препроцессора, объясните как работает
Дисклеймер: пост на опечатки не проверял.

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

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__ - Текущая строка в файле). Не вижу смысла расписывать тут подробнее
 
Текущее время: 07:38. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru