Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Tutorial

Boost , Chapter 1. Boost.ScopeExit 1.1.0 , Chapter 1. Boost.ScopeExit 1.1.0

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

В этом разделе показано, как пользоваться библиотекой.

Представьте, что мы хотим внести много изменений в элементы данных некоторого класса<world>в его функции<world::add_person>. Начнем с добавления нового<person>объекта к вектору лиц:

void world::add_person(person const& a_person) {
    bool commit = false;
    persons_.push_back(a_person);           // (1) direct action
    ...

Некоторые операции в будущем могут привести к исключению, и все изменения должны быть отменены. Эта семантика «все или ничего» также известна каксильная гарантия.

В частности, последний добавленный человек должен быть удален из<persons_>, если функция бросает. Все, что нам нужно, это определить отсроченное действие (выпуск ресурса) сразу после прямого действия (приобретение ресурсов). Например (см. также<world.cpp>):

void world::add_person(person const& a_person) {
    bool commit = false;
    persons_.push_back(a_person);           // (1) direct action
    // Following block is executed when the enclosing scope exits.
    BOOST_SCOPE_EXIT(&commit, &persons_) {
        if(!commit) persons_.pop_back();    // (2) rollback action
    } BOOST_SCOPE_EXIT_END
    // ...                                  // (3) other operations
    commit = true;                          // (4) disable rollback actions
}

Блок ниже точки<(1)>являетсяBoost.ScopeExitзаявление. В отличие от точки<(1)>, выполнение корпусаBoost.ScopeExitбудет отложено до конца текущего объема. В этом случае он будет выполнен либо после пункта<(4)>, либо по любому исключению. (На различных версиях компилятора GCC необходимо использовать<BOOST_SCOPE_EXIT_TPL>вместо<BOOST_SCOPE_EXIT>в шаблонах, см. далее в этом разделе для подробностей.)

ДекларацияBoost.ScopeExitначинается с<BOOST_SCOPE_EXIT>макровызова, который принимает разделённый на запятую список захваченных переменных (последовательностьBoost.Preprocessorтакже принята для компиляторов, которые не поддерживают вариадные макросы, и для обратной совместимости с более старыми версиями этой библиотеки см. разделNo Variadic Macros). Если захват начинается со знака амперсанда<&>, ссылка на захваченную переменную будет доступна внутри телаBoost.ScopeExit; в противном случае копия переменной будет сделана после объявленияBoost.ScopeExitв точке<(1)>и только копия будет доступна внутри тела (в этом случае тип захваченной переменной должен быть<CopyConstructible>).

В приведенном выше примере переменные<commit>и<persons_>фиксируются посредством ссылки, поскольку конечное значение переменной<commit>должно использоваться для определения того, следует ли выполнять действия отката или нет, и действие должно модифицировать объект<persons_>, а не его копию. Это наиболее распространенный случай, но иногда полезно также передавать переменную по стоимости.

Наконец, конец корпусаBoost.ScopeExitдолжен быть отмечен макросом<BOOST_SCOPE_EXIT_END>, который должен следовать за закрывающейся вьющейся скобкой<}>корпусаBoost.ScopeExit.

[Important]Important

Чтобы соответствовать требованиям безопасностиSTL, корпусBoost.ScopeExitникогда не должен выбрасываться (потому что реализация библиотеки выполняет корпус в рамках вызова деструктора). Это верно для всехмакросов Boost.ScopeExit(включая<BOOST_SCOPE_EXIT_TPL>и<BOOST_SCOPE_EXIT_ALL>, показанных ниже) как на C++03, так и на C++11.

Рассмотрим более сложный пример, когда<world::add_person>может в какой-то момент сохранить промежуточные состояния и откатиться к последнему сохраненному состоянию. Мы используем<person::evolution_>для хранения версии изменений и увеличения ее, чтобы отменить все действия отката, связанные с этими изменениями. Если мы пройдем текущее значение<evolution_>, хранящееся в переменной<checkpoint>по значению, оно останется неизменным в телеBoost.ScopeExit, чтобы мы могли сравнить его с конечным значением<evolution_>. Если последнее не было увеличено с тех пор, как мы его сохранили, действие отката внутри телаBoost.ScopeExitдолжно быть выполнено. Например (см. также<world_checkpoint.cpp>):

void world::add_person(person const& a_person) {
    persons_.push_back(a_person);
    // This block must be no-throw.
    person& p = persons_.back();
    person::evolution_t checkpoint = p.evolution;
    BOOST_SCOPE_EXIT(checkpoint, &p, &persons_) {
        if(checkpoint == p.evolution) persons_.pop_back();
    } BOOST_SCOPE_EXIT_END
    // ...
    checkpoint = ++p.evolution;
    // Assign new identifier to the person.
    person::id_t const prev_id = p.id;
    p.id = next_id_++;
    BOOST_SCOPE_EXIT(checkpoint, &p, &next_id_, prev_id) {
        if(checkpoint == p.evolution) {
            next_id_ = p.id;
            p.id = prev_id;
        }
    } BOOST_SCOPE_EXIT_END
    // ...
    checkpoint = ++p.evolution;
}

Когда несколькоблоков Boost.ScopeExitобъявляются в одной и той же области охвата, органыBoost.ScopeExitвыполняются в обратном порядке своих деклараций.

В пределах функции члена также можно захватить объект<this>. Однако специальный символ<this_>должен использоваться вместо<this>в декларацииBoost.ScopeExitи корпусе для захвата и доступа к объекту. Например (см. также<world_this.cpp>):

BOOST_SCOPE_EXIT(&commit, this_) { // Capture object `this_`.
    if(!commit) this_->persons_.pop_back();
} BOOST_SCOPE_EXIT_END

Невозможно захватить объект<this_>посредством ссылки, поскольку C++ не позволяет взять ссылку на<this>. Если функция ограждающего элемента постоянна, то захваченный объект также будет постоянным, в противном случае захваченный объект будет изменчивым.

world_void.cpp:

struct world_t {
    std::vector<person> persons;
    bool commit;
} world; // Global variable.
void add_person(person const& a_person) {
    world.commit = false;
    world.persons.push_back(a_person);
    BOOST_SCOPE_EXIT(void) { // No captures.
        if(!world.commit) world.persons.pop_back();
    } BOOST_SCOPE_EXIT_END
    // ...
    world.commit = true;
}

(Оба компилятора с вариадными макросами и без них используют этот же синтаксис для захвата переменной, см. разделNo Variadic Macrosдля получения дополнительной информации.)

На компиляторах C++11 также можно захватывать все переменные в объеме, не называя их один за другим, используя специальный макрос<BOOST_SCOPE_EXIT_ALL>вместо<BOOST_SCOPE_EXIT>.

Следуя тому же синтаксису, принятому лямбда-функциями C++11, макрос<BOOST_SCOPE_EXIT_ALL>принимает список захватов, разделенный запятой, который должен начинаться либо с<&>, либо с<=>, чтобы захватывать все переменные в объеме соответственно по ссылке или по значению (обратите внимание, что эти ведущие захваты не указывают имя переменной). Дополнительные захваты конкретных переменных могут следовать за ведущими<&>или<=>и они будут переопределять стандартные ссылки или захваты значений. Например (см. также<world_checkpoint_all.cpp>):

void world::add_person(person const& a_person) {
    persons_.push_back(a_person);
    // This block must be no-throw.
    person& p = persons_.back();
    person::evolution_t checkpoint = p.evolution;
    // Capture all by reference `&`, but `checkpoint` and `this` (C++11 only).
    BOOST_SCOPE_EXIT_ALL(&, checkpoint, this) { // Use `this` (not `this_`).
        if(checkpoint == p.evolution) this->persons_.pop_back();
    }; // Use `;` (not `SCOPE_EXIT_END`).
    // ...
    checkpoint = ++p.evolution;
    // Assign new identifier to the person.
    person::id_t const prev_id = p.id;
    p.id = next_id_++;
    // Capture all by value `=`, but `p` (C++11 only).
    BOOST_SCOPE_EXIT_ALL(=, &p) {
        if(checkpoint == p.evolution) {
            this->next_id_ = p.id;
            p.id = prev_id;
        }
    };
    // ...
    checkpoint = ++p.evolution;
}

Первоезаявление Boost.ScopeExitзахватывает все переменные в объеме посредством ссылки, но переменная<checkpoint>и объект<this>, которые явно захвачены значением (в частности,<p>и<persons_>, неявно захвачены посредством ссылки здесь). Второезаявление Boost.ScopeExitвместо этого захватывает все переменные в объеме по значению, но<p>, которые явно захвачены ссылкой (в частности,<checkpoint>,<prev_id>и<this>неявно захвачены значением здесь).

Обратите внимание, что макрос<BOOST_SCOPE_EXIT_ALL>следует синтаксису функции лямбда C++11, который, к сожалению, отличается от макросинтаксиса<BOOST_SCOPE_EXIT>. В частности:

  1. Макро<BOOST_SCOPE_EXIT_ALL>не может захватывать элементы данных без захвата объекта<this>, в то время как это не относится к<BOOST_SCOPE_EXIT>.<persons_>
  2. <BOOST_SCOPE_EXIT_ALL>макрос захватывает объект в масштабе, используя<this>вместо<this_>.<world.cpp>
  3. Тело<BOOST_SCOPE_EXIT_ALL>заканчивается полуколоном<;>, а не макро<BOOST_SCOPE_EXIT_END>.

Если программисты определяют макрос конфигурации<BOOST_SCOPE_EXIT_CONFIG_USE_LAMBDAS>, то макрореализация<BOOST_SCOPE_EXIT>будет использовать функции C++11 lamda, а макрос<BOOST_SCOPE_EXIT>будет следовать тому же синтаксису<BOOST_SCOPE_EXIT_ALL>макроса, который является синтаксисом функции лямбды C++11. Однако<BOOST_SCOPE_EXIT>больше не будет обратно совместимым, и более старый код, использующий<BOOST_SCOPE_EXIT>, может больше не компилироваться (если члены данных были явно захвачены).

Различные версии компилятора GCC не компилируют<BOOST_SCOPE_EXIT>внутри шаблонов (см. разделСсылкадля получения дополнительной информации). В этих случаях вместо<BOOST_SCOPE_EXIT>следует использовать<BOOST_SCOPE_EXIT_TPL>.Макро<BOOST_SCOPE_EXIT_TPL>имеет точно такой же синтаксис<BOOST_SCOPE_EXIT>. Например (см. также<world_tpl.cpp>):

template<typename Person>
void world<Person>::add_person(Person const& a_person) {
    bool commit = false;
    persons_.push_back(a_person);
    BOOST_SCOPE_EXIT_TPL(&commit, this_) { // Use `_TPL` postfix.
        if(!commit) this_->persons_.pop_back();
    } BOOST_SCOPE_EXIT_END
    // ...
    commit = true;
}

Рекомендуется всегда использовать<BOOST_SCOPE_EXIT_TPL>в шаблонах, чтобы максимизировать переносимость между различными компиляторами.

Поэтому эта библиотека предоставляет дополнительные макросы<BOOST_SCOPE_EXIT_ID>,<BOOST_SCOPE_EXIT_ID_TPL>,<BOOST_SCOPE_EXIT_END_ID>и<BOOST_SCOPE_EXIT_ALL_ID>, которые могут быть расширены несколько раз на одной строке, если программисты указывают уникальные идентификаторы в качестве первых параметров макросов. Уникальным идентификатором может быть любой токен (не только числовой), который может быть сцеплен с препроцессором C++ (например,<scope_exit_number_1_at_line_123>).<(1)>

Макросы<BOOST_SCOPE_EXIT_ID>,<BOOST_SCOPE_EXIT_ID_TPL>и<BOOST_SCOPE_EXIT_ALL_ID>принимают список захвата, используя тот же синтаксис, что и<BOOST_SCOPE_EXIT>и<BOOST_SCOPE_EXIT_ALL>соответственно. Например (см. также<same_line.cpp>):

#define SCOPE_EXIT_INC_DEC(variable, offset) \
    BOOST_SCOPE_EXIT_ID(BOOST_PP_CAT(inc, __LINE__), /* unique ID */ \
            &variable, offset) { \
        variable += offset; \
    } BOOST_SCOPE_EXIT_END_ID(BOOST_PP_CAT(inc, __LINE__)) \
    \
    BOOST_SCOPE_EXIT_ID(BOOST_PP_CAT(dec, __LINE__), \
            &variable, offset) { \
        variable -= offset; \
    } BOOST_SCOPE_EXIT_END_ID(BOOST_PP_CAT(dec, __LINE__))
#define SCOPE_EXIT_INC_DEC_TPL(variable, offset) \
    BOOST_SCOPE_EXIT_ID_TPL(BOOST_PP_CAT(inc, __LINE__), \
            &variable, offset) { \
        variable += offset; \
    } BOOST_SCOPE_EXIT_END_ID(BOOST_PP_CAT(inc, __LINE__)) \
    \
    BOOST_SCOPE_EXIT_ID_TPL(BOOST_PP_CAT(dec, __LINE__), \
            &variable, offset) { \
        variable -= offset; \
    } BOOST_SCOPE_EXIT_END_ID(BOOST_PP_CAT(dec, __LINE__))
#define SCOPE_EXIT_ALL_INC_DEC(variable, offset) \
    BOOST_SCOPE_EXIT_ALL_ID(BOOST_PP_CAT(inc, __LINE__), \
            =, &variable) { \
        variable += offset; \
    }; \
    BOOST_SCOPE_EXIT_ALL_ID(BOOST_PP_CAT(dec, __LINE__), \
            =, &variable) { \
        variable -= offset; \
    };
template<typename T>
void f(T& x, T& delta) {
    SCOPE_EXIT_INC_DEC_TPL(x, delta) // Multiple scope exits on same line.
    BOOST_TEST(x == 0);
}
int main(void) {
    int x = 0, delta = 10;
    {
        SCOPE_EXIT_INC_DEC(x, delta) // Multiple scope exits on same line.
    }
    BOOST_TEST(x == 0);
    f(x, delta);
#ifndef BOOST_NO_CXX11_LAMBDAS
    {
        SCOPE_EXIT_ALL_INC_DEC(x, delta) // Multiple scope exits on same line.
    }
    BOOST_TEST(x == 0);
#endif // LAMBDAS
    return boost::report_errors();
}

Как показано в приведенном выше примере, макросы<BOOST_SCOPE_EXIT_ID>,<BOOST_SCOPE_EXIT_ID_TPL>,<BOOST_SCOPE_EXIT_END_ID>и<BOOST_SCOPE_EXIT_ALL_ID>особенно полезны, когда необходимо вызывать их несколько раз в пределах определяемых пользователем макросов (потому что препроцессор C++ расширяет все вложенные макросы на одной линии).



Rationale. К сожалению, это невозможно просто вызвать Boost.ScopeExit макрос без параметров, как в BOOST_SCOPE_EXIT(), потому что препроцессор C++ не может обнаружить пустота макро параметра, когда параметр может начаться с не-альфинумерки символ (что происходит при захвате переменной по ссылке &variable). .

Обоснование.Макрос<BOOST_SCOPE_EXIT_ALL>определяется только на компиляторах C++11, для которых макрос<BOOST_NO_CXX11_LAMBDAS>Boost.Configне определен. Использование<BOOST_SCOPE_EXIT_ALL>на компиляторах C++03, для которых определено<BOOST_NO_CXX11_LAMBDAS>, будет генерировать (возможно, загадочные) ошибки компилятора. Обратите внимание, что новый макрос<BOOST_SCOPE_EXIT_ALL>должен быть введен вместо повторного использования<BOOST_SCOPE_EXIT>, потому что<BOOST_SCOPE_EXIT(&)>и<BOOST_SCOPE_EXIT(=)>не могут быть отличимы от<BOOST_SCOPE_EXIT(void)>или<BOOST_SCOPE_EXIT(this_)>с использованием препроцессора C++, учитывая, что символы<&>и<=>не являются ни префиксированными, ни постфиксированными буквенно-цифровыми токенами (это не проблема для<BOOST_SCOPE_EXIT_ALL>, который всегда имеет не буквенно-цифровой<&>или<=>в качестве первого захвата, поэтому первые токены захвата просто никогда не сравниваются ни с<void>, ни с<this_>для этого макроса).

<persons_>В настоящее время, по-видимому, существует некоторое обсуждение, позволяющее функциям лямбды C++11 захватывать элементы данных без захвата объекта<this>. Если бы стандарт C++11 был изменен, макросинтаксис<BOOST_SCOPE_EXIT_ALL>можно было бы расширить до супернабора<BOOST_SCOPE_EXIT>макроса, сохраняя при этом полную обратную совместимость.

<world.cpp>На компиляторах, поддерживающих использование внешних шаблонов<typename>в соответствии со стандартом C++11,<BOOST_SCOPE_EXIT_ALL>может использовать как<this>, так и<this_>для захвата объекта в объеме (в частности, это не относится к компилятору MSVC 10.0).

Rationale. GCC версии соответствуют C++11 не представляет эту проблему и учитывая, что BOOST_SCOPE_EXIT_ALL доступен только на компиляторах C++11, нет необходимости в макросе BOOST_SCOPE_EXIT_ALL_TPL. .

Обоснование.Макросы библиотеки используют<__LINE__>для создания уникальных идентификаторов. Поэтому, если один и тот же макрос будет расширен более чем в срок на одной и той же строке, сгенерированные идентификаторы больше не будут уникальными и код не будет компилироваться. (Это ограничение не распространяется на MSVC и другие компиляторы, которые предоставляют нестандартный макрос<__COUNTER__>.)

<(1)>Поскольку существуют ограничения на набор токенов, которые может сопрягать препроцессор C++, и поскольку не все компиляторы правильно реализуют эти ограничения, в целом рекомендуется указывать уникальные идентификаторы как комбинацию буквенно-цифровых токенов.


PrevUpHomeNext

Статья Tutorial раздела Chapter 1. Boost.ScopeExit 1.1.0 Chapter 1. Boost.ScopeExit 1.1.0 может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Chapter 1. Boost.ScopeExit 1.1.0 ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 18:42:33/0.010761022567749/0