diff --git a/doc/interprocess.qbk b/doc/interprocess.qbk index ba0ebb7..49c7021 100644 --- a/doc/interprocess.qbk +++ b/doc/interprocess.qbk @@ -3452,36 +3452,6 @@ The object construction API allows constructing a type `T` in a managed memory s [endsect] -[section:uses_allocator Uses-allocator protocol for object construction] - -Since Boost 1.91, [*Boost.Interprocess] uses [*Boost.Container]'s -extended [*uses-allocator construction] utilities -(see [@https://www.boost.org/doc/libs/latest/doc/html/container/cpp_conformance.html Boost.Container]) -so that constructing objects that use [*Boost.Interprocess] allocators is simplified. As a result a user: - -* Does not longer need to explicitly pass allocator arguments (or `segment_manager*` arguments that are convertible - to allocators) when constructing an object and its subobjects. -* [*Boost.Inteprocess] allocators' `construct` methods, in cooperation with [*Boost.Container] containers - and uses-allocator utilities, take advantage of the protocol to automatically propagate the state - of the allocator to the uses_allocator compatible types stored in those containers. -* Containers of containers automatically propagate the allocator recursively without the user explicitly needing to do so. - -More formally, a type `T` is compatible with the [*Boost.Interprocess] `uses_allocator` protocol for a managed segment `MS` if: - -* Defines `T::allocator_type` as one of [*Boost.Interprocess] allocator types and - uses one of the following conventions when constructed from an argument list `args...` - * The leading-allocator convention: `T` is constructible as `T(allocator_arg, alloc, args...)` --> - * The trailing-allocator convention `T` is constructible as `T(args..., alloc)`. - -If a managed memory segment's construction utility takes the arguments `par1, par2` to construct a type -named `MyType`, then `MyType` instances are constructed as follows: - -* If `MyType` does not support the uses-allocator protocol, it calls `MyType(par1, par2)` -* Otherwise, if the constructor is viable, it calls `MyType(arg0, par1, par2)` -* Otherwise, if the constructor is viable, it calls `MyType(allocator_arg, uses_segment_manager, par1, par2)` - -[endsect] - [section:named Named Object construction function family] When constructing objects in a managed memory segment (managed shared memory, @@ -3657,6 +3627,39 @@ Here is an example showing this functionality: [endsect] +[section:uses_allocator Uses-allocator protocol for object construction] + +Since Boost 1.91, [*Boost.Interprocess] uses [*Boost.Container]'s +extended [*uses-allocator construction] utilities +(see [@https://www.boost.org/doc/libs/latest/doc/html/container/cpp_conformance.html Boost.Container]) +so that constructing objects that use [*Boost.Interprocess] allocators is simplified. As a result a user: + +* Does not longer need to explicitly pass allocator arguments (or `segment_manager*` arguments that are convertible + to allocators) when constructing an object and its subobjects. +* [*Boost.Inteprocess] allocators' `construct` methods, in cooperation with [*Boost.Container] containers + and uses-allocator utilities, take advantage of the protocol to automatically propagate the state + of the allocator to the uses_allocator compatible types stored in those containers. +* Containers of containers, all using [*Boost.Interprocess] allocators, automatically propagate the allocator + state recursively without the user explicitly needing to do so. + +More formally, a type `T` is compatible with the [*Boost.Interprocess] implicit allocator +propagation protocol for a managed segment instance `ms` if the following conditions are fulfilled +when a user tries a `ms.construct` or `ms.construct_it` operation with argument list `args...`: + +* `T::allocator_type` is one of [*Boost.Interprocess] allocator types (a void allocator fulfills this requirement) +* `T` can be constructed with one of the following conventions: + * The leading-allocator convention: `T` is constructible as `T(allocator_arg, allocator_type, args...)` --> + * The trailing-allocator convention `T` is constructible as `T(args..., allocator_type)`. + +See the following example to see the difference between a type that does follow the uses-allocator +protocol (`UAType`) and another that does not (`Type`). Note how when constructing `UAType` using +the managed memory construction functions, the allocator is implicitly passed in constructors: + +[import ../example/doc_uses_allocator_construction.cpp] +[doc_uses_allocator_construction] + +[endsect] + [endsect] [section:managed_memory_segment_advanced_features Managed Memory Segment Advanced Features] diff --git a/example/doc_uses_allocator_construction.cpp b/example/doc_uses_allocator_construction.cpp new file mode 100644 index 0000000..ed4fa9e --- /dev/null +++ b/example/doc_uses_allocator_construction.cpp @@ -0,0 +1,104 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2025-2025. 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) +// +// See http://www.boost.org/libs/interprocess for documentation. +// +////////////////////////////////////////////////////////////////////////////// + +#include +//[doc_uses_allocator_construction +#include +#include +#include +#include //for allocator_arg_t +//<- +#include "../test/get_process_id_name.hpp" +//-> + +using namespace boost::interprocess; + +typedef managed_shared_memory::segment_manager seg_mngr_t; +typedef allocator valloc_t; + +struct Type //Requires explicit allocator arguments +{ + typedef allocator vec_alloc_t; + boost::container::vector m_vec; //Recursive vector + float m_v1; + int m_v2; + + Type(valloc_t va) //0-arg + alloc constructor + : m_vec(va), m_v1(), m_v2() {} + + Type(std::size_t size, valloc_t va) //1-arg + alloc constructor + : m_vec(vec_alloc_t(va)) //We can't use vector(size_type, allocator_type) as Type + , m_v1(), m_v2() //has no default constructor. Forced to one-by-one initialization. + { for(std::size_t i = 0; i != size; ++i) m_vec.emplace_back(va); } + + Type (valloc_t va, float v1, int v2) //allocator + 2-arg constructor + : m_vec(vec_alloc_t(va)) //We can't use vector(size_type, allocator_type) as Type + , m_v1(v1), m_v2(v2) //has no default constructor. Forced to one-by-one initialization. + { for(std::size_t i = 0; i != 2; ++i) m_vec.emplace_back(va); } +}; + +struct UAType //Uses-Allocator compatible type +{ + typedef allocator vec_alloc_t; + boost::container::vector m_vec; //Recursive vector + float m_v1; + int m_v2; + + typedef valloc_t allocator_type; //Signals uses-allocator construction is available + + UAType(valloc_t va) //0 explicit args + allocator + : m_vec(vec_alloc_t(va)), m_v1(), m_v2() {} + + UAType( boost::container::allocator_arg_t + , allocator_type a , std::size_t size) //1 explicit arg + leading-allocator convention + : m_vec(size, vec_alloc_t(a)), m_v1(), m_v2() {} + + UAType(float v1, int v2, allocator_type a) //2 explicit args + trailing-allocator convention + : m_vec(vec_alloc_t(a)), m_v1(v1), m_v2(v2) {} +}; + +int main () +{ + //<- + //Remove shared memory on construction and destruction + struct shm_remove + { + shm_remove() { shared_memory_object::remove(test::get_process_id_name()); } + ~shm_remove(){ shared_memory_object::remove(test::get_process_id_name()); } + } remover; + + (void)remover; + //-> + managed_shared_memory ms(create_only, test::get_process_id_name(), 65536); + + // 1 arg + allocator: Requires explicit allocator argument + // Type(size_type, valloc_t) called + Type *ptype1 = ms.construct< Type >(anonymous_instance)(3u, ms.get_allocator()); + + // allocator + 2 arg: Requires explicit allocator-convertible argument (segment_manager*) + // Type(valloc_t, float, int) called + Type *ptype2 = ms.construct< Type >(anonymous_instance)(ms.get_segment_manager(), 0.0f, 0); + + // 1 explicit arg + implicit leading allocator: + // UAType(allocator_arg_t, allocator_type, std::size_t) called + UAType *pua1 = ms.construct(anonymous_instance)(3u); + + // 2 explicit args + implicit trailing allocator: + // UAType(float, int, allocator_type) called + UAType *pua2 = ms.construct(anonymous_instance)(0.0f, 0); + //<- + ms.destroy_ptr(ptype1); + ms.destroy_ptr(ptype2); + ms.destroy_ptr(pua1); + ms.destroy_ptr(pua2); + //-> + return 0; +} +//]