From e210c5728d30b5b2f9453e8246c59eaa2b676625 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Wed, 21 Jun 2017 03:02:48 +0300 Subject: [PATCH] Add local_shared_ptr.adoc --- doc/smart_ptr.adoc | 2 + doc/smart_ptr/introduction.adoc | 5 +- doc/smart_ptr/local_shared_ptr.adoc | 719 ++++++++++++++++++++++++++++ 3 files changed, 724 insertions(+), 2 deletions(-) create mode 100644 doc/smart_ptr/local_shared_ptr.adoc diff --git a/doc/smart_ptr.adoc b/doc/smart_ptr.adoc index d3210d7..3c299c3 100644 --- a/doc/smart_ptr.adoc +++ b/doc/smart_ptr.adoc @@ -37,6 +37,8 @@ include::smart_ptr/intrusive_ptr.adoc[] include::smart_ptr/intrusive_ref_counter.adoc[] +include::smart_ptr/local_shared_ptr.adoc[] + include::smart_ptr/pointer_cast.adoc[] include::smart_ptr/pointer_to_other.adoc[] diff --git a/doc/smart_ptr/introduction.adoc b/doc/smart_ptr/introduction.adoc index 2154014..ff51823 100644 --- a/doc/smart_ptr/introduction.adoc +++ b/doc/smart_ptr/introduction.adoc @@ -24,13 +24,14 @@ deletion of the object when it is no longer needed. As such, they are examples o acquisition is initialization" idiom described in Bjarne Stroustrup's "The C++ Programming Language", 3rd edition, Section 14.4, Resource Management. -This library provides five smart pointer class templates: +This library provides six smart pointer class templates: * `<>`, used to contain ownership of a dynamically allocated object to the current scope; * `<>`, which provides scoped ownership for a dynamically allocated array; * `<>`, a versatile tool for managing shared ownership of an object or array; * `<>`, a non-owning observer to a shared_ptr-managed object that can be promoted temporarily to shared_ptr; -* `<>`, a pointer to objects with an embedded reference count. +* `<>`, a pointer to objects with an embedded reference count; +* `<>`, providing shared ownership within a single thread. `shared_ptr` and `weak_ptr` are part of the {cpp} standard since its 2011 iteration. diff --git a/doc/smart_ptr/local_shared_ptr.adoc b/doc/smart_ptr/local_shared_ptr.adoc new file mode 100644 index 0000000..d7d34dd --- /dev/null +++ b/doc/smart_ptr/local_shared_ptr.adoc @@ -0,0 +1,719 @@ +//// +Copyright 2017 Peter Dimov + +Distributed under the Boost Software License, Version 1.0. + +See accompanying file LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt +//// + +[#local_shared_ptr] +# local_shared_ptr: Shared Ownership within a Single Thread +:toc: +:toc-title: +:idprefix: local_shared_ptr_ + +## Description + +`local_shared_ptr` is nearly identical to `shared_ptr`, with the only difference of note being that its reference count is +updated with non-atomic operations. As such, a `local_shared_ptr` and all its copies must reside in (be local to) a single +thread (hence the name.) + +`local_shared_ptr` can be converted to `shared_ptr` and vice versa. Creating a `local_shared_ptr` from a `shared_ptr` creates +a new local reference count; this means that two `local_shared_ptr` instances, both created from the same `shared_ptr`, refer +to the same object but don't share the same count, and as such, can safely be used by two different threads. + +.Two local_shared_ptr instances created from a shared_ptr +``` +shared_ptr p1( new X ); + +local_shared_ptr p2( p1 ); // p2.local_use_count() == 1 +local_shared_ptr p3( p1 ); // p3.local_use_count() also 1 +``` + +Creating the second `local_shared_ptr` from the first one, however, does lead to the two sharing the same count: + +.A local_shared_ptr created from another local_shared_ptr +``` +shared_ptr p1( new X ); + +local_shared_ptr p2( p1 ); // p2.local_use_count() == 1 +local_shared_ptr p3( p2 ); // p3.local_use_count() == 2 +``` + +Two `shared_ptr` instances created from the same `local_shared_ptr` do share ownership: + +.Two shared_ptr instances created from a local_shared_ptr +``` +local_shared_ptr p1( new X ); + +shared_ptr p2( p1 ); // p2.use_count() == 2 +shared_ptr p3( p1 ); // p3.use_count() == 3 +``` + +Here `p2.use_count()` is 2, because `p1` holds a reference, too. + +One can think of `local_shared_ptr` as `shared_ptr>`, with the outer `shared_ptr` using non-atomic operations for +its count. Converting from `local_shared_ptr` to `shared_ptr` gives you a copy of the inner `shared_ptr`; converting from `shared_ptr` +wraps it into an outer `shared_ptr` with a non-atomic use count (conceptually speaking) and returns the result. + +## Synopsis + +`local_shared_ptr` is defined in ``. + +``` +namespace boost { + + template class local_shared_ptr { + public: + + typedef /*see below*/ element_type; + + // constructors + + constexpr local_shared_ptr() noexcept; + constexpr local_shared_ptr(std::nullptr_t) noexcept; + + template explicit local_shared_ptr(Y * p); + + template local_shared_ptr(Y * p, D d); + template local_shared_ptr(std::nullptr_t p, D d); + + template local_shared_ptr(Y * p, D d, A a); + template local_shared_ptr(std::nullptr_t p, D d, A a); + + local_shared_ptr(local_shared_ptr const & r) noexcept; + template local_shared_ptr(local_shared_ptr const & r) noexcept; + + local_shared_ptr(local_shared_ptr && r) noexcept; + template local_shared_ptr(local_shared_ptr && r) noexcept; + + template local_shared_ptr( shared_ptr const & r ); + template local_shared_ptr( shared_ptr && r ); + + template local_shared_ptr(local_shared_ptr const & r, element_type * p) noexcept; + template local_shared_ptr(local_shared_ptr && r, element_type * p) noexcept; + + template local_shared_ptr(std::unique_ptr && r); + + // destructor + + ~local_shared_ptr() noexcept; + + // assignment + + local_shared_ptr & operator=(local_shared_ptr const & r) noexcept; + template local_shared_ptr & operator=(local_shared_ptr const & r) noexcept; + + local_shared_ptr & operator=(local_shared_ptr const && r) noexcept; + template local_shared_ptr & operator=(local_shared_ptr const && r) noexcept; + + template local_shared_ptr & operator=(std::unique_ptr && r); + + local_shared_ptr & operator=(std::nullptr_t) noexcept; + + // reset + + void reset() noexcept; + + template void reset(Y * p); + template void reset(Y * p, D d); + template void reset(Y * p, D d, A a); + + template void reset(local_shared_ptr const & r, element_type * p) noexcept; + template void reset(local_shared_ptr && r, element_type * p) noexcept; + + // accessors + + T & operator*() const noexcept; // only valid when T is not an array type + T * operator->() const noexcept; // only valid when T is not an array type + + // only valid when T is an array type + element_type & operator[](std::ptrdiff_t i) const noexcept; + + element_type * get() const noexcept; + + long local_use_count() const noexcept; + + // conversions + + explicit operator bool() const noexcept; + + template operator shared_ptr() const noexcept; + template operator weak_ptr() const noexcept; + + // swap + + void swap(local_shared_ptr & b) noexcept; + + // owner_before + + template bool owner_before(local_shared_ptr const & rhs) const noexcept; + }; + + // comparisons + + template + bool operator==(local_shared_ptr const & a, local_shared_ptr const & b) noexcept; + template + bool operator==(local_shared_ptr const & a, shared_ptr const & b) noexcept; + template + bool operator==(shared_ptr const & a, local_shared_ptr const & b) noexcept; + + template + bool operator!=(local_shared_ptr const & a, local_shared_ptr const & b) noexcept; + template + bool operator!=(local_shared_ptr const & a, shared_ptr const & b) noexcept; + template + bool operator!=(shared_ptr const & a, local_shared_ptr const & b) noexcept; + + template bool operator==(local_shared_ptr const & p, std::nullptr_t) noexcept; + template bool operator==(std::nullptr_t, local_shared_ptr const & p) noexcept; + + template bool operator!=(local_shared_ptr const & p, std::nullptr_t) noexcept; + template bool operator!=(std::nullptr_t, local_shared_ptr const & p) noexcept; + + template + bool operator<(local_shared_ptr const & a, local_shared_ptr const & b) noexcept; + + // swap + + template void swap(local_shared_ptr & a, local_shared_ptr & b) noexcept; + + // get_pointer + + template + typename local_shared_ptr::element_type * + get_pointer(local_shared_ptr const & p) noexcept; + + // casts + + template + local_shared_ptr static_pointer_cast(local_shared_ptr const & r) noexcept; + + template + local_shared_ptr const_pointer_cast(local_shared_ptr const & r) noexcept; + + template + local_shared_ptr dynamic_pointer_cast(local_shared_ptr const & r) noexcept; + + template + local_shared_ptr reinterpret_pointer_cast(local_shared_ptr const & r) noexcept; + + // stream I/O + + template + std::basic_ostream & + operator<< (std::basic_ostream & os, local_shared_ptr const & p); + + // get_deleter + + template D * get_deleter(local_shared_ptr const & p) noexcept; +} +``` + +## Members + +### element_type +``` +typedef ... element_type; +``` +`element_type` is `T` when `T` is not an array type, and `U` when `T` is `U[]` or `U[N]`. + +### default constructor +``` +constexpr local_shared_ptr() noexcept; +``` +``` +constexpr local_shared_ptr(std::nullptr_t) noexcept; +``` +[none] +* {blank} ++ +Effects:: Constructs an empty `local_shared_ptr`. +Postconditions:: `local_use_count() == 0 && get() == 0`. + +### pointer constructor +``` +template explicit local_shared_ptr(Y * p); +``` +[none] +* {blank} ++ +Effects:: Constructs a `local_shared_ptr` that owns `shared_ptr( p )`. + +Postconditions:: `local_use_count() == 1 && get() == p`. + +Throws:: `std::bad_alloc`, or an implementation-defined exception when a resource other than memory could not be obtained. + +### constructors taking a deleter +``` +template local_shared_ptr(Y * p, D d); +``` +``` +template local_shared_ptr(std::nullptr_t p, D d); +``` +[none] +* {blank} ++ +Effects:: Constructs a `local_shared_ptr` that owns `shared_ptr( p, d )`. + +Postconditions:: `local_use_count() == 1 && get() == p`. + +Throws:: `std::bad_alloc`, or an implementation-defined exception when a resource other than memory could not be obtained. + +``` +template local_shared_ptr(Y * p, D d, A a); +``` +``` +template local_shared_ptr(std::nullptr_t p, D d, A a); +``` +[none] +* {blank} ++ +Effects:: Constructs a `local_shared_ptr` that owns `shared_ptr( p, d, a )`. + +Postconditions:: `local_use_count() == 1 && get() == p`. + +Throws:: `std::bad_alloc`, or an implementation-defined exception when a resource other than memory could not be obtained. + +### copy and converting constructors +``` +local_shared_ptr(local_shared_ptr const & r) noexcept; +``` +``` +template local_shared_ptr(local_shared_ptr const & r) noexcept; +``` +[none] +* {blank} ++ +Requires:: `Y*` should be convertible to `T*`. + +Effects:: If `r` is empty, constructs an empty `local_shared_ptr`; otherwise, constructs a `local_shared_ptr` that shares ownership with `r`. + +Postconditions:: `get() == r.get() && local_use_count() == r.local_use_count()`. + +### move constructors +``` +local_shared_ptr(local_shared_ptr && r) noexcept; +``` +``` +template local_shared_ptr(local_shared_ptr && r) noexcept; +``` +[none] +* {blank} ++ +Requires:: `Y*` should be convertible to `T*`. + +Effects:: Move-constructs a `local_shared_ptr` from `r`. + +Postconditions:: `*this` contains the old value of `r`. `r` is empty and `r.get() == 0`. + +### shared_ptr constructor +``` +template local_shared_ptr( shared_ptr const & r ); +``` +``` +template local_shared_ptr( shared_ptr && r ); +``` +[none] +* {blank} ++ +Effects:: Constructs a `local_shared_ptr` that owns `r`. + +Postconditions:: `local_use_count() == 1`. `get()` returns the old value of `r.get()`. + +Throws:: `std::bad_alloc`, or an implementation-defined exception when a resource other than memory could not be obtained. + +### aliasing constructor +``` +template local_shared_ptr(local_shared_ptr const & r, element_type * p) noexcept; +``` +[none] +* {blank} ++ +Effects:: constructs a `local_shared_ptr` that shares ownership with `r` and stores `p`. + +Postconditions:: `get() == p && local_use_count() == r.local_use_count()`. + +### aliasing move constructor +``` +template local_shared_ptr(local_shared_ptr && r, element_type * p) noexcept; +``` +[none] +* {blank} ++ +Effects:: Move-constructs a `local_shared_ptr` from `r`, while storing `p` instead. + +Postconditions:: `get() == p` and `local_use_count()` equals the old count of `r`. `r` is empty and `r.get() == 0`. + +### unique_ptr constructor +``` +template local_shared_ptr(std::unique_ptr && r); +``` +[none] +* {blank} ++ +Requires:: `Y*` should be convertible to `T*`. + +Effects:: +- When `r.get() == 0`, equivalent to `local_shared_ptr()`; +- Otherwise, constructs a `local_shared_ptr` that owns `shared_ptr( std::move(r) )`. + +Throws:: `std::bad_alloc`, or an implementation-defined exception when a resource other than memory could not be obtained. + +Exception safety:: If an exception is thrown, the constructor has no effect. + +### destructor +``` +~local_shared_ptr() noexcept; +``` +[none] +* {blank} ++ +Effects:: +- If `*this` is empty, or shares ownership with another `local_shared_ptr` instance (`local_use_count() > 1`), there are no side effects. +- Otherwise, destroys the owned `shared_ptr`. + +### assignment +``` +local_shared_ptr & operator=(local_shared_ptr const & r) noexcept; +``` +``` +template local_shared_ptr & operator=(local_shared_ptr const & r) noexcept; +``` +[none] +* {blank} ++ +Effects:: Equivalent to `local_shared_ptr(r).swap(*this)`. +Returns:: `*this`. + +``` +local_shared_ptr & operator=(local_shared_ptr && r) noexcept; +``` +``` +template local_shared_ptr & operator=(local_shared_ptr && r) noexcept; +``` +``` +template local_shared_ptr & operator=(std::unique_ptr && r); +``` +[none] +* {blank} ++ +Effects:: Equivalent to `local_shared_ptr(std::move(r)).swap(*this)`. +Returns:: `*this`. + +``` +local_shared_ptr & operator=(std::nullptr_t) noexcept; +``` +[none] +* {blank} ++ +Effects:: Equivalent to `local_shared_ptr().swap(*this)`. +Returns:: `*this`. + +### reset +``` +void reset() noexcept; +``` +[none] +* {blank} ++ +Effects:: Equivalent to `local_shared_ptr().swap(*this)`. + +``` +template void reset(Y * p); +``` +[none] +* {blank} ++ +Effects:: Equivalent to `local_shared_ptr(p).swap(*this)`. + +``` +template void reset(Y * p, D d); +``` +[none] +* {blank} ++ +Effects:: Equivalent to `local_shared_ptr(p, d).swap(*this)`. + +``` +template void reset(Y * p, D d, A a); +``` +[none] +* {blank} ++ +Effects:: Equivalent to `local_shared_ptr(p, d, a).swap(*this)`. + +``` +template void reset(local_shared_ptr const & r, element_type * p) noexcept; +``` +[none] +* {blank} ++ +Effects:: Equivalent to `local_shared_ptr(r, p).swap(*this)`. + +``` +template void reset(local_shared_ptr && r, element_type * p) noexcept; +``` +[none] +* {blank} ++ +Effects:: Equivalent to `local_shared_ptr(std::move(r), p).swap(*this)`. + +### indirection +``` +T & operator*() const noexcept; +``` +[none] +* {blank} ++ +Requires:: `T` should not be an array type. +Returns:: `*get()`. + +``` +T * operator->() const noexcept; +``` +[none] +* {blank} ++ +Requires:: `T` should not be an array type. +Returns:: `get()`. + +``` +element_type & operator[](std::ptrdiff_t i) const noexcept; +``` +[none] +* {blank} ++ +Requires:: `T` should be an array type. The stored pointer must not be 0. `i >= 0`. If `T` is `U[N]`, `i < N`. +Returns:: `get()[i]`. + +### get + +``` +element_type * get() const noexcept; +``` +[none] +* {blank} ++ +Returns:: The stored pointer. + +### local_use_count +``` +long local_use_count() const noexcept; +``` +[none] +* {blank} ++ +Returns:: The number of `local_shared_ptr` objects, `*this` included, that share ownership with `*this`, or 0 when `*this` is empty. + +### conversions +``` +explicit operator bool() const noexcept; +``` +[none] +* {blank} ++ +Returns:: `get() != 0`. + +NOTE: On C++03 compilers, the return value is of an unspecified type. + +``` +template operator shared_ptr() const noexcept; +``` +``` +template operator weak_ptr() const noexcept; +``` +[none] +* {blank} ++ +Requires:: `T*` should be convertible to `Y*`. +Returns:: a copy of the owned `shared_ptr`. + +### swap +``` +void swap(local_shared_ptr & b) noexcept; +``` +[none] +* {blank} ++ +Effects:: Exchanges the contents of the two smart pointers. + +### owner_before +``` +template bool owner_before(local_shared_ptr const & rhs) const noexcept; +``` +[none] +* {blank} ++ +Effects:: See the description of `operator<`. + +## Free Functions + +### comparison +``` +template + bool operator==(local_shared_ptr const & a, local_shared_ptr const & b) noexcept; +``` +``` +template + bool operator==(local_shared_ptr const & a, shared_ptr const & b) noexcept; +``` +``` +template + bool operator==(shared_ptr const & a, local_shared_ptr const & b) noexcept; +``` +[none] +* {blank} ++ +Returns:: `a.get() == b.get()`. + +``` +template + bool operator!=(local_shared_ptr const & a, local_shared_ptr const & b) noexcept; +``` +``` +template + bool operator!=(local_shared_ptr const & a, shared_ptr const & b) noexcept; +``` +``` +template + bool operator!=(shared_ptr const & a, local_shared_ptr const & b) noexcept; +``` +[none] +* {blank} ++ +Returns:: `a.get() != b.get()`. + +``` +template bool operator==(local_shared_ptr const & p, std::nullptr_t) noexcept; +``` +``` +template bool operator==(std::nullptr_t, local_shared_ptr const & p) noexcept; +``` +[none] +* {blank} ++ +Returns:: `p.get() == 0`. + +``` +template bool operator!=(local_shared_ptr const & p, std::nullptr_t) noexcept; +``` +``` +template bool operator!=(std::nullptr_t, local_shared_ptr const & p) noexcept; +``` +[none] +* {blank} ++ +Returns:: `p.get() != 0`. + +``` +template + bool operator<(local_shared_ptr const & a, local_shared_ptr const & b) noexcept; +``` +[none] +* {blank} ++ +Returns:: An unspecified value such that + - `operator<` is a strict weak ordering as described in section [lib.alg.sorting] of the {cpp} standard; + - under the equivalence relation defined by `operator<`, `!(a < b) && !(b < a)`, two `local_shared_ptr` instances + are equivalent if and only if they share ownership or are both empty. + +NOTE: Allows `local_shared_ptr` objects to be used as keys in associative containers. + +NOTE: The rest of the comparison operators are omitted by design. + +### swap +``` +template void swap(local_shared_ptr & a, local_shared_ptr & b) noexcept; +``` +[none] +* {blank} ++ +Effects:: Equivalent to `a.swap(b)`. + +### get_pointer +``` +template + typename local_shared_ptr::element_type * + get_pointer(local_shared_ptr const & p) noexcept; +``` +[none] +* {blank} ++ +Returns:: `p.get()`. + +NOTE: Provided as an aid to generic programming. Used by `mem_fn`. + +### static_pointer_cast +``` +template + local_shared_ptr static_pointer_cast(local_shared_ptr const & r) noexcept; +``` +[none] +* {blank} ++ +Requires:: The expression `static_cast( (U*)0 )` must be well-formed. +Returns:: `local_shared_ptr( r, static_cast::element_type*>(r.get()) )`. + +CAUTION: The seemingly equivalent expression `local_shared_ptr(static_cast(r.get()))` will eventually +result in undefined behavior, attempting to delete the same object twice. + +### const_pointer_cast +``` +template + local_shared_ptr const_pointer_cast(local_shared_ptr const & r) noexcept; +``` +[none] +* {blank} ++ +Requires:: The expression `const_cast( (U*)0 )` must be well-formed. +Returns:: `local_shared_ptr( r, const_cast::element_type*>(r.get()) )`. + +### dynamic_pointer_cast +``` +template + local_shared_ptr dynamic_pointer_cast(local_shared_ptr const & r) noexcept; +``` +[none] +* {blank} ++ +Requires:: The expression `dynamic_cast( (U*)0 )` must be well-formed. +Returns:: + - When `dynamic_cast::element_type*>(r.get())` returns a nonzero value `p`, `local_shared_ptr(r, p)`; + - Otherwise, `local_shared_ptr()`. + +### reinterpret_pointer_cast +``` +template + local_shared_ptr reinterpret_pointer_cast(local_shared_ptr const & r) noexcept; +``` +[none] +* {blank} ++ +Requires:: The expression `reinterpret_cast( (U*)0 )` must be well-formed. +Returns:: `local_shared_ptr( r, reinterpret_cast::element_type*>(r.get()) )`. + +### operator<< +``` +template + std::basic_ostream & + operator<< (std::basic_ostream & os, local_shared_ptr const & p); +``` +[none] +* {blank} ++ +Effects:: `os << p.get();`. +Returns:: `os`. + +### get_deleter +``` +template + D * get_deleter(local_shared_ptr const & p) noexcept; +``` +[none] +* {blank} ++ +Returns:: If `*this` owns a `shared_ptr` instance `p`, `get_deleter( p )`, otherwise 0. +