]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | |
2 | [/ Copyright (C) 2006-2009, 2012 Alexander Nasonov ] | |
3 | [/ Copyright (C) 2012 Lorenzo Caminiti ] | |
4 | [/ Distributed under the Boost Software License, Version 1.0 ] | |
5 | [/ (see accompanying file LICENSE_1_0.txt or a copy at ] | |
6 | [/ http://www.boost.org/LICENSE_1_0.txt) ] | |
7 | [/ Home at http://www.boost.org/libs/scope_exit ] | |
8 | ||
9 | [library Boost.ScopeExit | |
10 | [quickbook 1.5] | |
11 | [version 1.1.0] | |
12 | [copyright 2006-2012 Alexander Nasonov, Lorenzo Caminiti] | |
13 | [purpose execute arbitrary code at scope exit] | |
14 | [license | |
15 | Distributed under the Boost Software License, Version 1.0 | |
16 | (see accompanying file LICENSE_1_0.txt or a copy at | |
17 | [@http://www.boost.org/LICENSE_1_0.txt]) | |
18 | ] | |
19 | [authors | |
20 | [Nasonov, Alexander] | |
21 | [Caminiti <email>lorcaminiti@gmail.com</email>, Lorenzo] | |
22 | ] | |
23 | [category utility] | |
24 | [id scope_exit] | |
25 | [dirname scope_exit] | |
26 | ] | |
27 | ||
28 | [def __Introduction__ [link scope_exit.introduction Introduction]] | |
29 | [def __Getting_Started__ [link scope_exit.getting_started Getting Started]] | |
30 | [def __Tutorial__ [link scope_exit.tutorial Tutorial]] | |
31 | [def __No_Variadic_Macros__ [link scope_exit.no_variadic_macros No Variadic Macros]] | |
32 | [def __Reference__ [@reference.html Reference]] | |
33 | [def __Boost_ScopeExit__ [link scope_exit Boost.ScopeExit]] | |
34 | [def __Boost_Lambda__ [@http://www.boost.org/libs/lambda Boost.Lambda]] | |
35 | [def __Boost_Phoenix__ [@http://www.boost.org/libs/phoenix Boost.Phoenix]] | |
36 | [def __Boost_Typeof__ [@http://www.boost.org/libs/typeof Boost.Typeof]] | |
37 | [def __typeof_emulation__ [@http://www.boost.org/libs/typeof type-of emulation]] | |
38 | [def __Boost_Preprocessor__ [@http://www.boost.org/libs/preprocessor Boost.Preprocessor]] | |
39 | [def __Boost_Config__ [@http://www.boost.org/libs/config Boost.Config]] | |
40 | [def __Boost_PointerContainer__ [@http://www.boost.org/libs/ptr_container Boost.PointerContainer]] | |
41 | [def __Boost_Multi_Index__ [@http://www.boost.org/libs/multi_index Boost.Multi-Index]] | |
42 | [def __ScopeGuard__ [@http://www.ddj.com/dept/cpp/184403758 ScopeGuard]] | |
43 | [def __D__ [@http://www.digitalmars.com/d/index.html D]] | |
44 | [def __D_scope_exit__ [@http://www.digitalmars.com/d/2.0/statement.html#ScopeGuardStatement scope(exit)]] | |
45 | [def __RAII__ [@http://www.research.att.com/~bs/glossary.html#Gresource-acquisition-is-initialization RAII]] | |
46 | [def __strong_guarantee__ [@http://www.research.att.com/~bs/glossary.html#Gstrong-guarantee strong guarantee]] | |
47 | ||
48 | [import ../test/world.cpp] | |
49 | [import ../test/world_seq.cpp] | |
50 | [import ../test/world_checkpoint.cpp] | |
51 | [import ../test/world_this.cpp] | |
52 | [import ../test/world_void.cpp] | |
53 | [import ../test/world_checkpoint_all.cpp] | |
54 | [import ../test/world_tpl.cpp] | |
55 | [import ../test/same_line.cpp] | |
56 | [import ../example/try_catch.cpp] | |
57 | [import ../example/scope_guard.cpp] | |
58 | [import ../example/world_cxx11_lambda.cpp] | |
59 | ||
60 | This library allows to execute arbitrary code when the enclosing scope exits. | |
61 | ||
62 | [section Introduction] | |
63 | ||
64 | Nowadays, every C++ developer is familiar with the Resource Acquisition Is Initialization (__RAII__) technique. | |
65 | It binds resource acquisition and release to initialization and destruction of a variable that holds the resource. | |
66 | There are times when writing a special class for such a variable is not worth the effort. | |
67 | This is when __Boost_ScopeExit__ comes into play. | |
68 | ||
69 | Programmers can put resource acquisition directly in their code and next to it, they can write code that releases the resource using this library. | |
70 | For example (see also [@../../test/world.cpp =world.cpp=]): | |
71 | [footnote | |
72 | Older versions of this library used a __Boost_Preprocessor__ sequence to specify the list of captured variables. | |
73 | While maintaining full backward compatibility, it is now possible to specify the captured variables also using a comma-separated list (which is the preferred syntax). | |
74 | See the __No_Variadic_Macros__ section for more information. | |
75 | ] | |
76 | ||
77 | [world] | |
78 | ||
79 | [endsect] | |
80 | ||
81 | [section Getting Started] | |
82 | ||
83 | This section explains how to setup a system to use this library. | |
84 | ||
85 | [section This Documentation] | |
86 | ||
87 | Programmers should have enough knowledge to use this library after reading the __Introduction__, __Getting_Started__, and __Tutorial__ sections. | |
88 | The __Reference__ section can be consulted at a later point for quick reference. | |
89 | All the other sections of this documentation can be considered optional. | |
90 | ||
91 | Some footnotes are marked by the word "*Rationale*". | |
92 | They explain reasons behind decisions made during the design and implementation of this library. | |
93 | ||
94 | In most of the examples presented in this documentation, the Boost.Detail/LightweightTest (=boost/detail/lightweight_test.hpp=) macro `BOOST_TEST` is used to check correctness conditions. | |
95 | The `BOOST_TEST` macro is conceptually similar to `assert` but a failure of the checked condition does not abort the program, instead it makes `boost::report_errors` return a non-zero program exit code. | |
96 | [footnote | |
97 | *Rationale.* | |
98 | Using Boost.Detail/LightweightTest allows to add the examples to the library regression tests so to make sure that they always compile and run correctly. | |
99 | ] | |
100 | ||
101 | [endsect] | |
102 | ||
103 | [section Compilers and Platforms] | |
104 | ||
105 | The authors originally developed and tested the library on GNU Compiler Collection (GCC) C++ 3.3, 3.4, 4.1, 4.2, 4.5.3 (with and without C++11 features [^-std=c++0x]), Microsoft Visual C++ (MSVC) 8.0, and Intel 10.1 under Linux, Cygwin, and Windows 7. | |
106 | However, this library should be usable on any compiler that supports __Boost_Typeof__ except: | |
107 | ||
108 | * MSVC 7.1 and 8.0 fail to link if a function with __Boost_ScopeExit__ is included by multiple translation units. | |
109 | * GCC 3.3 cannot compile __Boost_ScopeExit__ inside a template (see [@http://lists.boost.org/Archives/boost/2007/02/116235.php] for details). | |
110 | ||
111 | See the library [@http://www.boost.org/development/tests/release/developer/scope_exit.html regression test results] for detailed information on supported compilers and platforms. | |
112 | Check the library regression test [@../../test/Jamfile.v2 =Jamfile.v2=] for any special configuration that might be required for a specific compiler. | |
113 | ||
114 | [endsect] | |
115 | ||
116 | [section Installation] | |
117 | ||
118 | This library is composed of header files only. | |
119 | Therefore there is no pre-compiled object file which needs to be installed. | |
120 | Programmers can simply instruct the compiler where to find the library header files (`-I` option on GCC, `/I` option on MSVC, etc) and compile code using the library. | |
121 | ||
122 | The library implementation uses __Boost_Typeof__ to automatically deduce the types of the __Boost_ScopeExit__ captured variables (see the __Tutorial__ section). | |
123 | In order to compile code in __typeof_emulation__ mode, all types should be properly registered using `BOOST_TYPEOF_REGISTER_TYPE` and `BOOST_TYPEOF_REGISTER_TEMPLATE`, or appropriate __Boost_Typeof__ headers should be included (see the source code of most examples presented in this documentation). | |
124 | ||
125 | [endsect] | |
126 | ||
127 | [endsect] | |
128 | ||
129 | [section Tutorial] | |
130 | ||
131 | This section illustrates how to use this library. | |
132 | ||
133 | [section Capturing Variables] | |
134 | ||
135 | Imagine that we want to make many modifications to data members of some `world` class in its `world::add_person` member function. | |
136 | We start with adding a new `person` object to a vector of persons: | |
137 | ||
138 | void world::add_person(person const& a_person) { | |
139 | bool commit = false; | |
140 | ||
141 | persons_.push_back(a_person); // (1) direct action | |
142 | ... | |
143 | ||
144 | Some operations down the road may throw an exception and all changes to involved objects should be rolled back. | |
145 | This all-or-nothing semantic is also known as __strong_guarantee__. | |
146 | ||
147 | In particular, the last added person must be deleted from `persons_` if the function throws. | |
148 | All we need is to define a delayed action (release of a resource) right after the direct action (resource acquisition). | |
149 | For example (see also [@../../test/world.cpp =world.cpp=]): | |
150 | ||
151 | [world] | |
152 | ||
153 | The block below point =(1)= is a __Boost_ScopeExit__ declaration. | |
154 | Unlike point =(1)=, an execution of the __Boost_ScopeExit__ body will be delayed until the end of the current scope. In this case it will be executed either after point =(4)= or on any exception. | |
155 | (On various versions of the GCC compiler, it is necessary to use [macroref BOOST_SCOPE_EXIT_TPL] instead of [macroref BOOST_SCOPE_EXIT] within templates, see later in this section for details.) | |
156 | ||
157 | The __Boost_ScopeExit__ declaration starts with the [macroref BOOST_SCOPE_EXIT] macro invocation which accepts a comma-separated list of captured variables (a __Boost_Preprocessor__ sequence is also accepted for compilers that do not support variadic macros and for backward compatibility with older versions of this library, see the __No_Variadic_Macros__ section). | |
158 | If a capture starts with the ampersand sign `&`, a reference to the captured variable will be available inside the __Boost_ScopeExit__ body; otherwise, a copy of the variable will be made after the __Boost_ScopeExit__ declaration at point =(1)= and only the copy will be available inside the body (in this case, the captured variable's type must be [@http://www.boost.org/doc/libs/release/doc/html/CopyConstructible.html `CopyConstructible`]). | |
159 | ||
160 | In the example above, the variables `commit` and `persons_` are captured by reference because the final value of the `commit` variable should be used to determine whether to execute rollback actions or not, and the action should modify the `persons_` object, not its copy. | |
161 | This is the most common case but passing a variable by value is sometimes useful as well. | |
162 | ||
163 | Finally, the end of the __Boost_ScopeExit__ body must be marked by the [macroref BOOST_SCOPE_EXIT_END] macro which must follow the closing curly bracket `}` of the __Boost_ScopeExit__ body. | |
164 | ||
165 | [important | |
166 | In order to comply with the [@http://www.stlport.org/doc/exception_safety.html STL exception safety requirements], the __Boost_ScopeExit__ body must never throw (because the library implementation executes the body within a destructor call). | |
167 | This is true for all __Boost_ScopeExit__ macros (including [macroref BOOST_SCOPE_EXIT_TPL] and [macroref BOOST_SCOPE_EXIT_ALL] seen below) on both C++03 and C++11. | |
168 | ] | |
169 | ||
170 | Consider a more complex example where `world::add_person` can save intermediate states at some point and roll back to the last saved state. | |
171 | We use `person::evolution_` to store a version of the changes and increment it to cancel all rollback actions associated with those changes. | |
172 | If we pass a current value of `evolution_` stored in the `checkpoint` variable by value, it remains unchanged within the __Boost_ScopeExit__ body so we can compare it with the final value of `evolution_`. | |
173 | If the latter was not incremented since we saved it, the rollback action inside the __Boost_ScopeExit__ body should be executed. | |
174 | For example (see also [@../../test/world_checkpoint.cpp =world_checkpoint.cpp=]): | |
175 | ||
176 | [world_checkpoint] | |
177 | ||
178 | When multiple __Boost_ScopeExit__ blocks are declared within the same enclosing scope, the __Boost_ScopeExit__ bodies are executed in the reversed order of their declarations. | |
179 | ||
180 | [endsect] | |
181 | ||
182 | [section Capturing The Object `this`] | |
183 | ||
184 | Within a member function, it is also possible to capture the object `this`. | |
185 | However, the special symbol `this_` must be used instead of `this` in the __Boost_ScopeExit__ declaration and body to capture and access the object. | |
186 | For example (see also [@../../test/world_this.cpp =world_this.cpp=]): | |
187 | ||
188 | [world_this] | |
189 | ||
190 | It is not possible to capture the object `this_` by reference because C++ does not allow to take a reference to `this`. | |
191 | If the enclosing member function is constant then the captured object will also be constant, otherwise the captured object will be mutable. | |
192 | ||
193 | [endsect] | |
194 | ||
195 | [section Capturing No Variable] | |
196 | ||
197 | A __Boost_ScopeExit__ declaration can also capture no variable. | |
198 | In this case, the list of captured variables is replaced by the `void` keyword (similarly to the C++ syntax that allows to declare a function with no parameter using [^['result-type function-name]]`(void)`). | |
199 | [footnote | |
200 | *Rationale.* | |
201 | Unfortunately, it is not possible to simply invoke the __Boost_ScopeExit__ macro with no parameters as in `BOOST_SCOPE_EXIT()` because the C++ preprocessor cannot detect emptiness of a macro parameter when the parameter can start with a non-alphanumeric symbol (which is the case when capturing a variable by reference `&variable`). | |
202 | ] | |
203 | For example, this can be useful when the __Boost_ScopeExit__ body only needs to access global variables (see also [@../../test/world_void.cpp =world_void.cpp=]): | |
204 | ||
205 | [world_void] | |
206 | ||
207 | (Both compilers with and without variadic macros use this same syntax for capturing no variable, see the __No_Variadic_Macros__ section for more information.) | |
208 | ||
209 | [endsect] | |
210 | ||
211 | [section Capturing All Variables (C++11 Only)] | |
212 | ||
213 | On C++11 compliers, it is also possible to capture all the variables in scope without naming them one-by-one using the special macro [macroref BOOST_SCOPE_EXIT_ALL] instead of [macroref BOOST_SCOPE_EXIT]. | |
214 | [footnote | |
215 | *Rationale.* | |
216 | The [macroref BOOST_SCOPE_EXIT_ALL] macro is only defined on C++11 compilers for which the __Boost_Config__ macro `BOOST_NO_CXX11_LAMBDAS` is not defined. | |
217 | Using [macroref BOOST_SCOPE_EXIT_ALL] on C++03 compilers for which `BOOST_NO_CXX11_LAMBDAS` is defined will generate (possibly cryptic) compiler errors. | |
218 | Note that a new macro [macroref BOOST_SCOPE_EXIT_ALL] needed to be introduced instead of reusing [macroref BOOST_SCOPE_EXIT] because `BOOST_SCOPE_EXIT(&)` and `BOOST_SCOPE_EXIT(=)` cannot be distinguished from `BOOST_SCOPE_EXIT(void)` or `BOOST_SCOPE_EXIT(this_)` using the C++ preprocessor given that the symbols `&` and `=` are neither prefxied nor postfixed by alphanumeric tokens (this is not an issue for [macroref BOOST_SCOPE_EXIT_ALL] which always has the non-alphanumeric `&` or `=` as the first capture so the first capture tokens are simply never compared with neither `void` nor `this_` for this macro). | |
219 | ] | |
220 | ||
221 | Following the same syntax adopted by C++11 lambda functions, the [macroref BOOST_SCOPE_EXIT_ALL] macro accepts a comma-separated list of captures which must start with either `&` or `=` to capture all variables in scope respectively by reference or by value (note that no variable name is specified by these leading captures). | |
222 | Additional captures of specific variables can follow the leading `&` or `=` and they will override the default reference or value captures. | |
223 | For example (see also [@../../test/world_checkpoint_all.cpp =world_checkpoint_all.cpp=]): | |
224 | ||
225 | [world_checkpoint_all] | |
226 | ||
227 | The first __Boost_ScopeExit__ declaration captures all variables in scope by reference but the variable `checkpoint` and the object `this` which are explicitly captured by value (in particular, `p` and `persons_` are implicitly captured by reference here). | |
228 | The second __Boost_ScopeExit__ declaration instead captures all variables in scope by value but `p` which is explicitly captured by reference (in particular, `checkpoint`, `prev_id`, and `this` are implicitly captured by value here). | |
229 | ||
230 | Note that the [macroref BOOST_SCOPE_EXIT_ALL] macro follows the C++11 lambda function syntax which is unfortunately different from the [macroref BOOST_SCOPE_EXIT] macro syntax. | |
231 | In particular: | |
232 | ||
233 | # The [macroref BOOST_SCOPE_EXIT_ALL] macro cannot capture data members without capturing the object `this` while that is not the case for [macroref BOOST_SCOPE_EXIT]. | |
234 | [footnote | |
235 | At present, there seems to be some discussion to allow C++11 lambda functions to capture data members without capturing the object `this`. | |
236 | If the C++11 standard were changed to allow this, the [macroref BOOST_SCOPE_EXIT_ALL] macro syntax could be extended to be a superset of the [macroref BOOST_SCOPE_EXIT] macro while keeping full backward compatibility. | |
237 | ] | |
238 | # The [macroref BOOST_SCOPE_EXIT_ALL] macro captures the object in scope using `this` instead of `this_`. | |
239 | [footnote | |
240 | On compilers that support the use of the `typename` outside templates as allowed by the C++11 standard, [macroref BOOST_SCOPE_EXIT_ALL] can use both `this` and `this_` to capture the object in scope (notably, this is not the case for the MSVC 10.0 compiler). | |
241 | ] | |
242 | # The [macroref BOOST_SCOPE_EXIT_ALL] body is terminated by a semicolon `;` instead than by the [macroref BOOST_SCOPE_EXIT_END] macro. | |
243 | ||
244 | If programmers define the configuration macro [macroref BOOST_SCOPE_EXIT_CONFIG_USE_LAMBDAS] then the [macroref BOOST_SCOPE_EXIT] macro implementation will use C++11 lamda functions and the [macroref BOOST_SCOPE_EXIT] macro will follow the same syntax of [macroref BOOST_SCOPE_EXIT_ALL] macro, which is the C++11 lambda function syntax. | |
245 | However, [macroref BOOST_SCOPE_EXIT] will no longer be backward compatible and older code using [macroref BOOST_SCOPE_EXIT] might no longer compile (if data members were explicitly captured). | |
246 | ||
247 | [endsect] | |
248 | ||
249 | [section Template Workaround (GCC)] | |
250 | ||
251 | Various versions of the GCC compiler do not compile [macroref BOOST_SCOPE_EXIT] inside templates (see the __Reference__ section for more information). | |
252 | As a workaround, [macroref BOOST_SCOPE_EXIT_TPL] should be used instead of [macroref BOOST_SCOPE_EXIT] in these cases. | |
253 | [footnote | |
254 | *Rationale.* | |
255 | GCC versions compliant with C++11 do not present this issue and given that [macroref BOOST_SCOPE_EXIT_ALL] is only available on C++11 compilers, there is no need for a `BOOST_SCOPE_EXIT_ALL_TPL` macro. | |
256 | ] | |
257 | The [macroref BOOST_SCOPE_EXIT_TPL] macro has the exact same syntax of [macroref BOOST_SCOPE_EXIT]. | |
258 | For example (see also [@../../test/world_tpl.cpp =world_tpl.cpp=]): | |
259 | ||
260 | [world_tpl] | |
261 | ||
262 | It is recommended to always use [macroref BOOST_SCOPE_EXIT_TPL] within templates so to maximize portability among different compilers. | |
263 | ||
264 | [endsect] | |
265 | ||
266 | [section Same Line Expansions] | |
267 | ||
268 | In general, it is not possible to expand the [macroref BOOST_SCOPE_EXIT], [macroref BOOST_SCOPE_EXIT_TPL], [macroref BOOST_SCOPE_EXIT_END], and [macroref BOOST_SCOPE_EXIT_ALL] macros multiple times on the same line. | |
269 | [footnote | |
270 | *Rationale.* | |
271 | The library macros internally use `__LINE__` to generate unique identifiers. | |
272 | Therefore, if the same macro is expanded more than on time on the same line, the generated identifiers will no longer be unique and the code will not compile. | |
273 | (This restriction does not apply to MSVC and other compilers that provide the non-standard `__COUNTER__` macro.) | |
274 | ] | |
275 | ||
276 | Therefore, this library provides additional macros [macroref BOOST_SCOPE_EXIT_ID], [macroref BOOST_SCOPE_EXIT_ID_TPL], [macroref BOOST_SCOPE_EXIT_END_ID], and [macroref BOOST_SCOPE_EXIT_ALL_ID] which can be expanded multiple times on the same line as long as programmers specify a unique identifiers as the macros' first parameters. | |
277 | The unique identifier can be any token (not just numeric) that can be concatenated by the C++ preprocessor (e.g., `scope_exit_number_1_at_line_123`). | |
278 | [footnote | |
279 | Because there are restrictions on the set of tokens that the C++ preprocessor can concatenate and because not all compilers correctly implement these restrictions, it is in general recommended to specify unique identifiers as a combination of alphanumeric tokens. | |
280 | ] | |
281 | ||
282 | The [macroref BOOST_SCOPE_EXIT_ID], [macroref BOOST_SCOPE_EXIT_ID_TPL], and [macroref BOOST_SCOPE_EXIT_ALL_ID] macros accept a capture list using the exact same syntax as [macroref BOOST_SCOPE_EXIT] and [macroref BOOST_SCOPE_EXIT_ALL] respectively. | |
283 | For example (see also [@../../test/same_line.cpp =same_line.cpp=]): | |
284 | ||
285 | [same_line] | |
286 | ||
287 | As shown by the example above, the [macroref BOOST_SCOPE_EXIT_ID], [macroref BOOST_SCOPE_EXIT_ID_TPL], [macroref BOOST_SCOPE_EXIT_END_ID], and [macroref BOOST_SCOPE_EXIT_ALL_ID] macros are especially useful when it is necessary to invoke them multiple times within user-defined macros (because the C++ preprocessor expands all nested macros on the same line). | |
288 | ||
289 | [endsect] | |
290 | ||
291 | [endsect] | |
292 | ||
293 | [section:alternatives Annex: Alternatives] | |
294 | ||
295 | This section presents some alternative and related work to __Boost_ScopeExit__. | |
296 | ||
297 | [heading Try-Catch] | |
298 | ||
299 | This is an example of using a badly designed `file` class. | |
300 | An instance of `file` does not close the file in its destructor, a programmer is expected to call the `close` member function explicitly. | |
301 | For example (see also [@../../example/try_catch.cpp =try_catch.cpp=]): | |
302 | ||
303 | [try_catch_bad] | |
304 | ||
305 | Note the following issues with this approach: | |
306 | ||
307 | # The `passwd` object is defined outside of the `try` block because this object is required inside the `catch` block to close the file. | |
308 | # The `passwd` object is not fully constructed until after the `open` | |
309 | member function returns. | |
310 | # If opening throws, the `passwd.close()` should not be called, hence the call to `passwd.is_open()`. | |
311 | ||
312 | The __Boost_ScopeExit__ approach does not have any of these issues. | |
313 | For example (see also [@../../example/try_catch.cpp =try_catch.cpp=]): | |
314 | ||
315 | [try_catch_good] | |
316 | ||
317 | [heading RAII] | |
318 | ||
319 | __RAII__ is absolutely perfect for the `file` class introduced above. | |
320 | Use of a properly designed `file` class would look like: | |
321 | ||
322 | try { | |
323 | file passwd("/etc/passwd"); | |
324 | // ... | |
325 | } catch(...) { | |
326 | std::clog << "could not get user info" << std::endl; | |
327 | throw; | |
328 | } | |
329 | ||
330 | However, using __RAII__ to build up a __strong_guarantee__ could introduce a lot of non-reusable __RAII__ types. | |
331 | For example: | |
332 | ||
333 | persons_.push_back(a_person); | |
334 | pop_back_if_not_commit pop_back_if_not_commit_guard(commit, persons_); | |
335 | ||
336 | The `pop_back_if_not_commit` class is either defined out of the scope or as a local class: | |
337 | ||
338 | class pop_back_if_not_commit { | |
339 | bool commit_; | |
340 | std::vector<person>& vec_; | |
341 | // ... | |
342 | ~pop_back_if_not_commit() { | |
343 | if(!commit_) vec_.pop_back(); | |
344 | } | |
345 | }; | |
346 | ||
347 | In some cases __strong_guarantee__ can be accomplished with standard utilities: | |
348 | ||
349 | std::auto_ptr<Person> superman_ptr(new superman()); | |
350 | persons_.push_back(superman_ptr.get()); | |
351 | superman_ptr.release(); // persons_ successfully took ownership | |
352 | ||
353 | Or with specialized containers such as __Boost_PointerContainer__ or __Boost_Multi_Index__. | |
354 | ||
355 | [heading Scope Guards] | |
356 | ||
357 | Imagine that a new currency rate is introduced before performing a transaction (see also []): | |
358 | ||
359 | [scope_guard_decl] | |
360 | ||
361 | If the transaction does not complete, the currency must be erased from `rates`. | |
362 | This can be done with __ScopeGuard__ and __Boost_Lambda__ (or __Boost_Phoenix__): | |
363 | ||
364 | using namespace boost::lambda; | |
365 | ||
366 | ON_BLOCK_EXIT( | |
367 | if_(currency_rate_inserted && !_1) [ | |
368 | bind( | |
369 | static_cast< | |
370 | std::map<std::string, double>::size_type | |
371 | (std::map<std::string, double>::*)(std::string const&) | |
372 | >(&std::map<std::string, double>::erase) | |
373 | , &rates | |
374 | , currency | |
375 | ) | |
376 | ] | |
377 | , boost::cref(commit) | |
378 | ); | |
379 | ||
380 | // ... | |
381 | ||
382 | commit = true; | |
383 | ||
384 | Note the following issues with this approach: | |
385 | ||
386 | # __Boost_Lambda__ expressions are hard to write correctly (e.g., overloaded functions must be explicitly casted, as demonstrated in the example above). | |
387 | # The condition in the `if_` expression refers to `commit` variable indirectly through the `_1` placeholder reducing readability. | |
388 | # Setting a breakpoint inside `if_[...]` requires in-depth knowledge of __Boost_Lambda__ and debugging techniques. | |
389 | ||
390 | This code will look much better with C++11 lambdas: | |
391 | ||
392 | ON_BLOCK_EXIT( | |
393 | [currency_rate_inserted, &commit, &rates, ¤cy]() { | |
394 | if(currency_rate_inserted && !commit) rates.erase(currency); | |
395 | } | |
396 | ); | |
397 | ||
398 | // ... | |
399 | ||
400 | commit = true; | |
401 | ||
402 | With __Boost_ScopeExit__ we can simply do the following (see also [@../../example/scope_guard.cpp =scope_guard.cpp=]): | |
403 | ||
404 | [scope_guard_exit] | |
405 | ||
406 | [heading The D Programming Language] | |
407 | ||
408 | __Boost_ScopeExit__ is similar to __D_scope_exit__ feature built into the __D__ programming language. | |
409 | ||
410 | A curious reader may notice that the library does not implement `scope(success)` and `scope(failure)` of the __D__ language. | |
411 | Unfortunately, these are not possible in C++ because failure or success conditions cannot be determined by calling `std::uncaught_exception` (see [@http://www.gotw.ca/gotw/047.htm Guru of the Week #47] for details about `std::uncaught_exception` and if it has any good use at all). | |
412 | However, this is not a big problem because these two __D__'s constructs can be expressed in terms of __D_scope_exit__ and a `bool commit` variable (similarly to some examples presented in the __Tutorial__ section). | |
413 | ||
414 | [heading C++11 Lambdas] | |
415 | ||
416 | Using C++11 lambdas, it is relatively easy to implement the __Boost_ScopeExit__ construct. | |
417 | For example (see also [@../../example/world_cxx11_lambda.cpp =world_cxx11_lambda.cpp=]): | |
418 | ||
419 | [world_cxx11_lambda] | |
420 | ||
421 | However, this library allows to program the __Boost_ScopeExit__ construct in a way that is portable between C++03 and C++11 compilers. | |
422 | ||
423 | [endsect] | |
424 | ||
425 | [section:no_variadic_macros Annex: No Variadic Macros] | |
426 | ||
427 | This section presents an alternative syntax for compilers without variadic macro support. | |
428 | ||
429 | [heading Sequence Syntax] | |
430 | ||
431 | Most modern compilers support variadic macros (notably, these include GCC, MSVC, and all C++11 compilers). | |
432 | [footnote | |
433 | A C++ compiler does not support variadic macros if the __Boost_Config__ macro `BOOST_NO_CXX11_VARIADIC_MACROS` is defined for that compiler. | |
434 | ] | |
435 | However, in the rare case that programmers need to use this library on a complier without variaidc macros, this library also allows to specify the capture list using a __Boost_Preprocessor__ sequence where tokens are separated by round parenthesis `()`: | |
436 | ||
437 | (capture1) (capture2) ... // All compilers. | |
438 | ||
439 | Instead of the comma-separated list that we have seen so far which requires variadic macros: | |
440 | ||
441 | capture1, capture2, ... // Only compilers with variadic macros. | |
442 | ||
443 | For example, the following syntax is accepted on all compilers with and without variadic macros (see also [@../../test/world_seq.cpp =world_seq.cpp=] and [@../../test/world.cpp =world.cpp=]): | |
444 | ||
445 | [table | |
446 | [ [Boost.Preprocessor Sequence (All Compilers)] [Comma-Separated List (Variadic Macros Only)] ] | |
447 | [ [[world_seq]] [[world]] ] | |
448 | ] | |
449 | ||
450 | Note how the same macros accept both syntaxes on compilers with variadic macros and only the __Boost_Preprocessor__ sequence syntax on compilers without variadic macros. | |
451 | Older versions of this library used to only support the __Boost_Preprocessor__ sequence syntax so this syntax is supported also for backward compatibility. | |
452 | However, in the current version of this library and on compilers with variadic macros, the comma-separated syntax is preferred because it is more readable. | |
453 | ||
454 | Finally, an empty capture list is always specified using `void` on compilers with and without variaidc macros (see also [@../../test/world_void.cpp =world_void.cpp=]): | |
455 | ||
456 | [world_void] | |
457 | ||
458 | [heading Examples] | |
459 | ||
460 | For reference, the following is a list of most of the examples presented in this documentation reprogrammed using the __Boost_Preprocessor__ sequence syntax instead of comma-separated lists (in alphabetic order): | |
461 | ||
462 | [table | |
463 | [ [Files] ] | |
464 | [ [[@../../test/same_line_seq.cpp =same_line_seq.cpp=]] ] | |
465 | [ [[@../../example/scope_guard_seq.cpp =scope_guard_seq.cpp=]] ] | |
466 | [ [[@../../example/try_catch_seq.cpp =try_catch_seq.cpp=]] ] | |
467 | [ [[@../../test/world_checkpoint_all_seq.cpp =world_checkpoint_all_seq.cpp=]] ] | |
468 | [ [[@../../test/world_checkpoint_seq.cpp =world_checkpoint_seq.cpp=]] ] | |
469 | [ [[@../../test/world_this_seq.cpp =world_this_seq.cpp=]] ] | |
470 | [ [[@../../test/world_tpl_seq.cpp =world_tpl_seq.cpp=]] ] | |
471 | ] | |
472 | ||
473 | [endsect] | |
474 | ||
475 | [xinclude reference.xml] | |
476 | ||
477 | [section Acknowledgements] | |
478 | ||
479 | Alexander Nasonov is the original library author. | |
480 | ||
481 | Lorenzo Caminiti added variadic macro support, capture of the object `this_`, empty captures using `void`, and `BOOST_SCOPE_EXIT_ALL`. | |
482 | ||
483 | Thanks to the following people (in chronological order): | |
484 | ||
485 | Maxim Yegorushkin for sharing code where he used a local struct to clean up resources; | |
486 | ||
487 | Andrei Alexandrescu for pointing out the __D_scope_exit__ construct of the __D__ programming language; | |
488 | ||
489 | Pavel Vozenilek and Maxim Yanchenko for reviews of early drafts of the library; | |
490 | ||
491 | Steven Watanabe for his valuable ideas; | |
492 | ||
493 | Jody Hagins for good comments that helped to significantly improve the documentation; | |
494 | ||
495 | Richard Webb for testing the library on MSVC compiler; | |
496 | ||
497 | Adam Butcher for a workaround to error C2355 when deducing the type of `this` on some MSVC versions. | |
498 | ||
499 | [endsect] | |
500 |