2
0
mirror of https://github.com/boostorg/scope.git synced 2026-01-19 04:42:10 +00:00

Added scope guards docs.

This commit is contained in:
Andrey Semashev
2023-03-12 00:43:47 +03:00
parent b0b11e3d55
commit e0fc6d6626
9 changed files with 657 additions and 0 deletions

1
doc/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
html

113
doc/Jamfile Normal file
View File

@@ -0,0 +1,113 @@
# Copyright 2023 Andrey Semashev
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# https://www.boost.org/LICENSE_1_0.txt)
using quickbook ;
using boostbook ;
using doxygen ;
using xsltproc ;
import set ;
import doxygen ;
import xsltproc ;
import notfile ;
import path ;
import project ;
project boost/libs/scope/doc ;
local doxygen_params =
<doxygen:param>RECURSIVE=YES
<doxygen:param>ALPHABETICAL_INDEX=YES
<doxygen:param>REPEAT_BRIEF=YES
<doxygen:param>ALWAYS_DETAILED_SEC=YES
<doxygen:param>BRIEF_MEMBER_DESC=NO
<doxygen:param>ABBREVIATE_BRIEF=YES
<doxygen:param>INHERIT_DOCS=YES
<doxygen:param>HIDE_UNDOC_MEMBERS=YES
<doxygen:param>HIDE_UNDOC_CLASSES=YES
<doxygen:param>HIDE_SCOPE_NAMES=YES
<doxygen:param>EXTRACT_ALL=NO
<doxygen:param>EXTRACT_PRIVATE=NO
<doxygen:param>BUILTIN_STL_SUPPORT=YES
<doxygen:param>ENABLE_PREPROCESSING=YES
<doxygen:param>MACRO_EXPANSION=YES
<doxygen:param>TAB_SIZE=4
<doxygen:param>SOURCE_BROWSER=YES
<doxygen:param>VERBATIM_HEADERS=NO
# <doxygen:param>SEARCH_INCLUDES=YES
# <doxygen:param>"INCLUDE_PATH=../../.."
# <doxygen:param>EXCLUDE_SYMBOLS="detail detail::*"
<doxygen:param>"PREDEFINED=BOOST_SCOPE_DOXYGEN \\
BOOST_SCOPE_DETAIL_DOC_HIDDEN(...)=... \\
BOOST_NO_CXX17_DEDUCTION_GUIDES=1 \\
BOOST_SYMBOL_VISIBLE= \\
BOOST_FORCEINLINE=inline \\
BOOST_INLINE_VARIABLE=inline \\
BOOST_CXX14_CONSTEXPR=constexpr"
<xsl:param>boost.doxygen.detailns=detail
# <xsl:param>boost.doxygen.detail=implementation_
;
local top_level_includes =
[ glob
../../../boost/scope/*.hpp
] ;
# This rule generates *.qbk files with macros with references to files, classes, etc. from the doxygen resulting *.xml files.
rule gen-references ( target : source : properties * )
{
DEPENDS target : source ;
local source-path = [ path.make [ on $(source) return $(LOCATE) ] ] ;
STYLESHEET on $(target) = [ path.native [ path.join [ path.parent $(source-path) ] gen_references.xsl ] ] ;
local target-name = $(source:B) ;
TARGET on $(target) = [ path.native [ path.join $(source-path) $(target-name:S=.qbk) ] ] ;
}
actions gen-references
{
$(NAME:E=xsltproc) -o "$(TARGET)" "$(STYLESHEET)" "$(>)"
}
doxygen top_level_reference
:
$(top_level_includes)
:
$(doxygen_params)
<xsl:param>"boost.doxygen.reftitle=Reference"
<location>tmp
;
notfile top_level_refs : @gen-references : top_level_reference.xml ;
xml scope_doc
:
scope.qbk
:
<dependency>top_level_refs
;
boostbook scope
:
scope_doc
:
<xsl:param>"boost.root=../../../.."
<xsl:param>"boost.libraries=../../../libs/libraries.htm"
<xsl:param>"nav.layout=none"
<xsl:param>"boost.image=Boost"
<xsl:param>"navig.graphics=1"
<xsl:param>"chunk.section.depth=1"
<xsl:param>"boost.compact.function=0"
<format>pdf:<xsl:param>"boost.url.prefix=https://www.boost.org/doc/libs/release/libs/scope/doc/html"
;
###############################################################################
alias boostdoc ;
explicit boostdoc ;
alias boostrelease : scope ;
explicit boostrelease ;

17
doc/changelog.qbk Normal file
View File

@@ -0,0 +1,17 @@
[/
/ Copyright 2023 Andrey Semashev
/
/ Distributed under the Boost Software License, Version 1.0.
/ (See accompanying file LICENSE_1_0.txt or copy at
/ https://www.boost.org/LICENSE_1_0.txt)
/
/ This document is a part of Boost.Scope library documentation.
/]
[section:changelog Changelog]
[heading 0.1]
Initial release for Boost review.
[endsect]

81
doc/gen_references.xsl Normal file
View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
Copyright 2023 Andrey Semashev
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
https://www.boost.org/LICENSE_1_0.txt)
This stylesheet extracts information about headers, classes, etc.
from the Doxygen-generated reference documentation and writes
it as QuickBook templates that refer to the according Reference sections.
-->
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/library-reference">
<xsl:text disable-output-escaping="yes">[/
/ Copyright 2023 Andrey Semashev
/
/ Distributed under the Boost Software License, Version 1.0.
/ (See accompanying file LICENSE_1_0.txt or copy at
/ https://www.boost.org/LICENSE_1_0.txt)
/
/ This document is a part of Boost.Scope library documentation.
/
/ This document was automatically generated, DO NOT EDIT!
/]
</xsl:text>
<xsl:apply-templates>
<xsl:with-param name="namespace"/>
<xsl:with-param name="enclosing_namespace"/>
</xsl:apply-templates>
</xsl:template>
<!-- Skip any text nodes -->
<xsl:template match="text()"/>
<!-- Headers -->
<xsl:template match="header">
<xsl:param name="namespace"/>
<xsl:param name="enclosing_namespace"/>
[template <xsl:value-of select="translate(@name, '/.', '__')"/>[][headerref <xsl:value-of select="@name"/>]]
<xsl:apply-templates>
<xsl:with-param name="namespace" select="$namespace"/>
<xsl:with-param name="enclosing_namespace" select="$enclosing_namespace"/>
</xsl:apply-templates>
</xsl:template>
<!-- Namespaces - only needed to construct fully qualified class names -->
<xsl:template match="namespace">
<xsl:param name="namespace"/>
<xsl:param name="enclosing_namespace"/>
<xsl:variable name="namespace_prefix">
<xsl:value-of select="$namespace"/><xsl:if test="string-length($namespace) &gt; 0"><xsl:text>::</xsl:text></xsl:if>
</xsl:variable>
<xsl:apply-templates>
<xsl:with-param name="namespace" select="concat($namespace_prefix, @name)"/>
<xsl:with-param name="enclosing_namespace" select="@name"/>
</xsl:apply-templates>
</xsl:template>
<!-- Classses -->
<xsl:template match="class|struct">
<xsl:param name="namespace"/>
<xsl:param name="enclosing_namespace"/>
[template <xsl:value-of select="concat('class_', $enclosing_namespace, '_', @name)"/>[][classref <xsl:value-of select="concat($namespace, '::', @name)"/><xsl:text> </xsl:text><xsl:value-of select="@name"/>]]
<xsl:apply-templates>
<xsl:with-param name="namespace" select="concat($namespace, '::', @name)"/>
<xsl:with-param name="enclosing_namespace" select="concat($enclosing_namespace, '_', @name)"/>
</xsl:apply-templates>
</xsl:template>
<!-- Free functions - currently disabled because multiple overloads generate duplicate QuickBook templates -->
<!--
<xsl:template match="function">
<xsl:param name="namespace"/>
<xsl:param name="enclosing_namespace"/>
[template <xsl:value-of select="concat('func_', $enclosing_namespace, '_', @name)"/>[][funcref <xsl:value-of select="concat($namespace, '::', @name)"/><xsl:text> </xsl:text><xsl:value-of select="@name"/>]]
</xsl:template>
-->
</xsl:transform>

221
doc/scope.qbk Normal file
View File

@@ -0,0 +1,221 @@
[library Boost.Scope
[quickbook 1.7]
[authors [Semashev, Andrey]]
[copyright 2022-2023 Andrey Semashev]
[license
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
[@https://www.boost.org/LICENSE_1_0.txt]).
]
[id scope]
[source-mode c++]
]
[c++]
[/ Links to external resources /]
[def __boost_config__ [@http://www.boost.org/doc/libs/release/libs/config/doc/html/index.html Boost.Config]]
[def __boost_function__ [@https://www.boost.org/doc/libs/release/doc/html/function.html Boost.Function]]
[def __boost_scope_exit__ [@http://www.boost.org/doc/libs/release/libs/scope_exit/doc/html/index.html Boost.ScopeExit]]
[def __boost_smart_ptr__ [@http://www.boost.org/doc/libs/release/libs/smart_ptr/doc/html/smart_ptr.html Boost.SmartPtr]]
[template github_issue[key][@https://github.com/boostorg/scope/issues/[key] #[key]]]
[template github_pr[key][@https://github.com/boostorg/scope/pull/[key] PR#[key]]]
[/ Auto-generated macros that refer to Reference sections /]
[import tmp/top_level_reference.qbk]
[section:intro Introduction]
The Boost.Scope library is a collection of utilities helping code execution upon leaving a scope and automatic resource management. The library contains
components that were defined in the [@https://cplusplus.github.io/fundamentals-ts/v3.html C++ Extensions for Library Fundamentals, Version 3] technical specification,
in the [@https://cplusplus.github.io/fundamentals-ts/v3.html#scope.syn `<experimental/scope>`] standard library
header. The library also contains extensions to the Fundamentals TS that improve usability or efficiency of the components.
The components provided by the library can be divided into two categories:
* A set of scope guards that allow executing arbitrary code when the scope guard is destroyed,
* A generic resource handle that automatically frees the owned resource upon destruction.
There is some overlap in terms of functionality with __boost_scope_exit__, __boost_smart_ptr__ as well as C++ standard library smart-pointers. __boost_scope_exit__
defines a set of macros for defining code blocks to be executed at scope exit. Scope guards provided by Boost.Scope provide similar functionality, but with
simpler syntax and new features. You can see the syntax differences in the table below:
[table __boost_scope_exit__ and Boost.Scope comparison
[[__boost_scope_exit__ (C++03)] [Boost.Scope (C++11)] [Boost.Scope (C++17)]]
[[
```
class adder
{
int x, y;
public:
int compute()
{
// Reset variables on return
BOOST_SCOPE_EXIT(this_)
{
this_->x = 0;
this_->y = 0;
}
BOOST_SCOPE_EXIT_END;
return x + y;
}
};
```
]
[
```
class adder
{
int x, y;
public:
int compute()
{
// Reset variables on return
auto cleanup = boost::scope::make_scope_exit([this]
{
x = 0;
y = 0;
});
return x + y;
}
};
```
]
[
```
class adder
{
int x, y;
public:
int compute()
{
// Reset variables on return
BOOST_SCOPE_FINAL [this]
{
x = 0;
y = 0;
};
return x + y;
}
};
```
]]
[[
```
template< typename Object >
class collection
{
std::set< Object > objects;
public:
template< typename T >
void add_object(T const& arg)
{
typename std::set< Object >::iterator it =
objects.insert(Object());
// Remove the object on failure
unsigned int uncaught_count =
boost::core::uncaught_exceptions();
BOOST_SCOPE_EXIT_TPL(this_, it, uncaught_count)
{
if (uncaught_count != boost::core::uncaught_exceptions())
this_->objects.erase(it);
}
BOOST_SCOPE_EXIT_END;
// Throws on error
it->activate(arg);
}
};
```
]
[
```
template< typename Object >
class collection
{
std::set< Object > objects;
public:
template< typename T >
void add_object(T&& arg)
{
auto it = objects.emplace();
// Remove the object on failure
auto cleanup = boost::scope::make_scope_fail([this, it]
{
objects.erase(it);
});
// Throws on error
it->activate(std::forward< T >(arg));
}
};
```
]
[
```
template< typename Object >
class collection
{
std::set< Object > objects;
public:
template< typename T >
void add_object(T&& arg)
{
auto it = objects.emplace();
// Remove the object on failure
boost::scope::scope_fail cleanup{[this, it]
{
objects.erase(it);
}};
// Throws on error
it->activate(std::forward< T >(arg));
}
};
```
]]
]
Unique resource wrapper provided by Boost.Scope is a generalization of smart pointers like `std::unique_ptr` and `boost::scoped_ptr` from __boost_smart_ptr__. While
smart pointers are suitable for managing resources represented by pointers (e.g. objects in dynamically allocated memory), unique resource wrapper can be used
with any kind of resource types, such as integers (e.g. POSIX file descriptors) and user-defined types.
The library defines its components in namespace `boost::scope`. For brevity, the namespace qualification may be omitted in this documentation; readers should assume
that unqualified names like `scope_exit` or `unique_resource` are defined in `boost::scope`.
[endsect]
[section:install_compat Installation and compatibility]
This library is header-only, it does not require separate compilation. You can add the path to Boost headers to your project, include the required Boost.Scope header
and then use the component provided by that header in your code.
The library requires a C++11 compiler at the minimum. For certain fetures, C++17 support is required. The following compilers were tested with the library:
* gcc 4.8 and newer
* clang 3.5 and newer
* MSVC 14.0 and newer
Except for the POSIX file descriptor support, which is only available on POSIX systems, the library components are agnostic to the operating system.
[endsect]
[include scope_guards.qbk]
[include unique_resource.qbk]
[xinclude tmp/top_level_reference.xml]
[include changelog.qbk]

207
doc/scope_guards.qbk Normal file
View File

@@ -0,0 +1,207 @@
[/
/ Copyright 2023 Andrey Semashev
/
/ Distributed under the Boost Software License, Version 1.0.
/ (See accompanying file LICENSE_1_0.txt or copy at
/ https://www.boost.org/LICENSE_1_0.txt)
/
/ This document is a part of Boost.Scope library documentation.
/]
[section:scope_guards Scope guards]
A scope guard is an object that invokes an arbitrary function object on destruction. Scope guards are useful for implementing actions
that need to be reliably performed upon control leaving an execution scope, which is especially helpful for handling exceptions.
The wrapped function object is specified on the scope guard construction and cannot be changed afterwards. It must be one of:
* a user-defined class with a public `operator()` taking no arguments, or
* an lvalue reference to such class, or
* an lvalue reference to a function taking no arguments.
Boost.Scope provides four kinds of scope guards, differing in their features and conditions upon which the function object is called,
summarised in the table below.
[table Scope guard comparison
[[Feature] [[class_scope_scope_exit]] [[class_scope_scope_success]] [[class_scope_scope_fail]] [[class_scope_scope_final]]]
[[Invokes function on normal scope exit?] [Yes] [Yes] [No] [Yes]]
[[Invokes function on scope exit due to an exception?] [Yes] [No] [Yes] [Yes]]
[[Function may throw?] [Yes] [Yes] [No] [Yes]]
[[Can be (de)activated?] [Yes] [Yes] [Yes] [No]]
[[Move-constructible? (requires function to be move-constructible)] [Yes] [Yes] [Yes] [No]]
[[Has factory function? (C++11-friendly)] [Yes] [Yes] [Yes] [No]]
]
[note Except for [class_scope_scope_fail], function objects wrapped in scope guards are allowed to throw exceptions upon execution.
However, users should remember that [class_scope_scope_exit] and [class_scope_scope_final] invoke the wrapped function upon exceptions,
and throwing an exception from the scope guard in this case will terminate the program. It is user's responsibility to ensure this
doesn't happen. This problem doesn't exist for [class_scope_scope_success] because it doesn't invoke the wrapped function object on
exceptions. However, it is generally recommended to use scope guards to implement actions that cannot throw and move all operations
that may fail to the normal code flow.]
[section:conditional `scope_exit`, `scope_success` and `scope_fail`]
#include <``[boost_scope_scope_exit_hpp]``>
#include <``[boost_scope_scope_success_hpp]``>
#include <``[boost_scope_scope_fail_hpp]``>
The [class_scope_scope_exit], [class_scope_scope_success] and [class_scope_scope_fail] scope guards provide similar interfaces and
capabilities and only differ in conditions when they invoke the wrapped function object. As shown in the table above,
[class_scope_scope_success] only invokes the function object if it is destroyed normally, [class_scope_scope_fail] - if it is
destroyed due to an exception being thrown, and [class_scope_scope_exit] acts regardless of the exception status.
In addition to the exception status, each of the scope guards supports active and inactive state. The wrapped function object will
only be called if the scope guard is in active state while being destroyed. By default, scope guards are created in active state,
but this can be changed by passing `false` as the second argument for the constructor. Scope guards can also be deactivated or
re-activated during their lifetime, which can be useful if the scope guard needs to be activated based on some condition.
class collection
{
std::set< std::shared_ptr< object > > objects;
public:
void add_object(std::shared_ptr< object > const& obj)
{
// Create a deactivated scope guard initially
std::set< std::shared_ptr< object > >::iterator it;
boost::scope::scope_fail rollback_guard{[&, this]
{
objects.erase(it);
},
false};
bool inserted;
std::tie(it, inserted) = objects.insert(obj);
if (inserted)
{
// Activate rollback guard
rollback_guard.set_active(true);
}
obj->on_added_to_collection(*this);
}
};
The code sample above relies on C++17 [@https://en.cppreference.com/w/cpp/language/class_template_argument_deduction class template
argument deduction (CTAD)] for `scope_fail` to deduce the function object type (which is the lambda). If this feature is not available,
the scope guard construction can be rewritten using a factory function, like this:
auto rollback_guard = boost::scope::make_scope_fail([&, this]
{
objects.erase(it);
},
false);
Factory functions are provided for each of the three scope guards described in this section and are compatible with C++11.
Scope guards described in this section are moveable, which requires the wrapped function objects to be moveable or copyable as well. After
moving, the moved-from scope guard becomes inactive.
[endsect]
[section:unconditional `scope_final`]
#include <``[boost_scope_scope_final_hpp]``>
The [class_scope_scope_final] scope guard is similar to [class_scope_scope_exit] in terms of when it invokes the wrapped function. But it
lacks support for moveability and activation/deactivation - this scope guard is always active upon construction. This allows to implement
it more efficiently when these features are not needed.
[note [class_scope_scope_final] is a more lightweight version of [class_scope_scope_exit], similar to how `std::lock_guard` is a more
lightweight version of `std::unique_lock`.]
Since [class_scope_scope_final] effectively provides no interface to interact with after construction, it is better suited for anonymous
"set up and forget" kind of scope guards. To reinforce this affinity, the library provides a `BOOST_SCOPE_FINAL` macro, which acts as
a keyword defining a uniquely named [class_scope_scope_final] scope guard. The macro should be followed by the function object to be
invoked on scope exit.
BOOST_SCOPE_FINAL []
{
std::cout << "Hello world!" << std::endl;
};
[note `BOOST_SCOPE_FINAL` requires support for C++17 [@https://en.cppreference.com/w/cpp/language/class_template_argument_deduction CTAD].
The [class_scope_scope_final] class itself is compatible with C++11, but given that there is no factory function for it, C++17 support is
very much desired.]
[endsect]
[section:runtime_defined Setting up scope exit actions at run time]
It is possible to use scope guard classes to implement scope exit actions that are initialized at run time. To implement this, one could use
a function object wrapper such as `std::function` together with the scope guard to schedule the function call. For example:
using cleanup_func_t = std::function< void() >;
// Create an inactive scope guard first, since the cleanup function is not set yet
boost::scope::scope_exit< cleanup_func_t > cleanup(cleanup_func_t(), false);
// Later in the program, initialize the scope guard with the function selected at run time
if (cond)
{
cleanup = boost::scope::scope_exit< cleanup_func_t >([]
{
std::cout << "cond is true" << std::endl;
});
}
else
{
cleanup = boost::scope::scope_exit< cleanup_func_t >([]
{
std::cout << "cond is false" << std::endl;
});
}
It is also possible to do this with `BOOST_SCOPE_FINAL`, although it eliminates one of the advantages provided by this macro, namely not
having to invent a variable name. Also note that the function wrapper must be valid at all times once the scope guard is constructed.
// Create a non-empty function wrapper that does nothing
std::function< void() > cleanup_func = [] {};
// Create a scope guard that refers to the function wrapper
BOOST_SCOPE_FINAL std::ref(cleanup_func);
// Later in the program, initialize the function wrapper
if (cond)
{
cleanup_func = []
{
std::cout << "cond is true" << std::endl;
};
}
else
{
cleanup_func = []
{
std::cout << "cond is false" << std::endl;
};
}
However, when setting up scope exit actions at run time like that, users should be aware that function wrappers typically use dynamic
memory allocation internally and copy the function object data, which may involve calling copy constructors that may also fail with an
exception. Although many standard library implementations use small object optimization for `std::function`, and this technique is also
used in other implementations like __boost_function__, it is generally not guaranteed that initializing the function wrapper with a given
function object will not throw. If setting up the scope exit action needs to be a non-throwing operation (for example, if the scope guard
is supposed to revert the effects of the immediately preceding operation), it is recommended to initialize inactive scope guards beforehand
and only activate one of them at a later point in the program.
// Create inactive scope guards for both branches
boost::scope::scope_exit cleanup_true([]
{
std::cout << "cond is true" << std::endl;
},
false);
boost::scope::scope_exit cleanup_false([]
{
std::cout << "cond is false" << std::endl;
},
false);
// Later in the program, activate one of the scope guards.
// This won't throw.
if (cond)
cleanup_true.set_active(true);
else
cleanup_false.set_active(true);
[endsect]
[endsect]

2
doc/tmp/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.qbk
*.xml

1
doc/tmp/boost-no-inspect Normal file
View File

@@ -0,0 +1 @@
This file tells boost inspect to ignore this directory and any sub-directories

14
doc/unique_resource.qbk Normal file
View File

@@ -0,0 +1,14 @@
[/
/ Copyright 2023 Andrey Semashev
/
/ Distributed under the Boost Software License, Version 1.0.
/ (See accompanying file LICENSE_1_0.txt or copy at
/ https://www.boost.org/LICENSE_1_0.txt)
/
/ This document is a part of Boost.Scope library documentation.
/]
[section:unique_resource Unique resource handle]
[endsect]