![]() |
![]() ![]() ![]() ![]() |
![]() |
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 ::
реклама |