![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
TutorialBoost , Chapter 1. Boost.ScopeExit 1.1.0 , Chapter 1. Boost.ScopeExit 1.1.0
|
![]() | Important |
---|---|
Чтобы соответствовать требованиям безопасностиSTL, корпусBoost.ScopeExitникогда не должен выбрасываться (потому что реализация библиотеки выполняет корпус в рамках вызова деструктора). Это верно для всехмакросов Boost.ScopeExit(включая< |
Рассмотрим более сложный пример, когда<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
>. Если функция ограждающего элемента постоянна, то захваченный объект также будет постоянным, в противном случае захваченный объект будет изменчивым.
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
>. В частности:
BOOST_SCOPE_EXIT_ALL
>не может захватывать элементы данных без захвата объекта<this
>, в то время как это не относится к<BOOST_SCOPE_EXIT
>.<persons_
>BOOST_SCOPE_EXIT_ALL
>макрос захватывает объект в масштабе, используя<this
>вместо<this_
>.<world.cpp
>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++, и поскольку не все компиляторы правильно реализуют эти ограничения, в целом рекомендуется указывать уникальные идентификаторы как комбинацию буквенно-цифровых токенов.
Статья Tutorial раздела Chapter 1. Boost.ScopeExit 1.1.0 Chapter 1. Boost.ScopeExit 1.1.0 может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
:: Главная :: Chapter 1. Boost.ScopeExit 1.1.0 ::
реклама |