diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..1936cc1 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1 @@ +html diff --git a/doc/Jamfile b/doc/Jamfile new file mode 100644 index 0000000..b594b91 --- /dev/null +++ b/doc/Jamfile @@ -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 = + RECURSIVE=YES + ALPHABETICAL_INDEX=YES + REPEAT_BRIEF=YES + ALWAYS_DETAILED_SEC=YES + BRIEF_MEMBER_DESC=NO + ABBREVIATE_BRIEF=YES + INHERIT_DOCS=YES + HIDE_UNDOC_MEMBERS=YES + HIDE_UNDOC_CLASSES=YES + HIDE_SCOPE_NAMES=YES + EXTRACT_ALL=NO + EXTRACT_PRIVATE=NO + BUILTIN_STL_SUPPORT=YES + ENABLE_PREPROCESSING=YES + MACRO_EXPANSION=YES + TAB_SIZE=4 + SOURCE_BROWSER=YES + VERBATIM_HEADERS=NO +# SEARCH_INCLUDES=YES +# "INCLUDE_PATH=../../.." +# EXCLUDE_SYMBOLS="detail detail::*" + "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" + boost.doxygen.detailns=detail +# 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) + "boost.doxygen.reftitle=Reference" + tmp + ; + +notfile top_level_refs : @gen-references : top_level_reference.xml ; + + +xml scope_doc + : + scope.qbk + : + top_level_refs + ; + +boostbook scope + : + scope_doc + : + "boost.root=../../../.." + "boost.libraries=../../../libs/libraries.htm" + "nav.layout=none" + "boost.image=Boost" + "navig.graphics=1" + "chunk.section.depth=1" + "boost.compact.function=0" + pdf:"boost.url.prefix=https://www.boost.org/doc/libs/release/libs/scope/doc/html" + ; + +############################################################################### +alias boostdoc ; +explicit boostdoc ; +alias boostrelease : scope ; +explicit boostrelease ; diff --git a/doc/changelog.qbk b/doc/changelog.qbk new file mode 100644 index 0000000..5adf9cd --- /dev/null +++ b/doc/changelog.qbk @@ -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] diff --git a/doc/gen_references.xsl b/doc/gen_references.xsl new file mode 100644 index 0000000..a4f14aa --- /dev/null +++ b/doc/gen_references.xsl @@ -0,0 +1,81 @@ + + + + + +[/ + / 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! + /] + + + + + + + + + + + + + + +[template [][headerref ]] + + + + + + + + + + + +:: + + + + + + + + + + + +[template [][classref ]] + + + + + + + + + + diff --git a/doc/scope.qbk b/doc/scope.qbk new file mode 100644 index 0000000..d78a6f4 --- /dev/null +++ b/doc/scope.qbk @@ -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 ``] 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] diff --git a/doc/scope_guards.qbk b/doc/scope_guards.qbk new file mode 100644 index 0000000..bae6441 --- /dev/null +++ b/doc/scope_guards.qbk @@ -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] diff --git a/doc/tmp/.gitignore b/doc/tmp/.gitignore new file mode 100644 index 0000000..a9a0caf --- /dev/null +++ b/doc/tmp/.gitignore @@ -0,0 +1,2 @@ +*.qbk +*.xml diff --git a/doc/tmp/boost-no-inspect b/doc/tmp/boost-no-inspect new file mode 100644 index 0000000..7263ef9 --- /dev/null +++ b/doc/tmp/boost-no-inspect @@ -0,0 +1 @@ +This file tells boost inspect to ignore this directory and any sub-directories diff --git a/doc/unique_resource.qbk b/doc/unique_resource.qbk new file mode 100644 index 0000000..95cd845 --- /dev/null +++ b/doc/unique_resource.qbk @@ -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]