From 4defdfd23352672dc386b91e966de4cc13e71ca8 Mon Sep 17 00:00:00 2001 From: Glen Fernandes Date: Sat, 12 Mar 2022 10:14:59 -0500 Subject: [PATCH] Add allocator_construct_n to allocator_access and deprecate alloc_construct --- doc/alloc_construct.qbk | 149 ------------------------ doc/allocator_access.qbk | 60 +++++++++- doc/core.qbk | 1 - include/boost/core/alloc_construct.hpp | 88 ++------------ include/boost/core/allocator_access.hpp | 69 +++++++++++ test/Jamfile.v2 | 2 + test/allocator_construct_n_test.cpp | 47 ++++++++ test/allocator_destroy_n_test.cpp | 30 +++++ 8 files changed, 214 insertions(+), 232 deletions(-) delete mode 100644 doc/alloc_construct.qbk create mode 100644 test/allocator_construct_n_test.cpp create mode 100644 test/allocator_destroy_n_test.cpp diff --git a/doc/alloc_construct.qbk b/doc/alloc_construct.qbk deleted file mode 100644 index e06e4e8..0000000 --- a/doc/alloc_construct.qbk +++ /dev/null @@ -1,149 +0,0 @@ -[/ -Copyright 2019 Glen Joseph Fernandes -(glenjofe@gmail.com) - -Distributed under the Boost Software License, Version 1.0. -(http://www.boost.org/LICENSE_1_0.txt) -] - -[section:alloc_construct alloc_construct] - -[simplesect Authors] - -* Glen Fernandes - -[endsimplesect] - -[section Overview] - -The header provides function templates -`alloc_construct`, `alloc_construct_n`, `alloc_destroy`, and `alloc_destroy_n` -for allocator aware and exception safe construction and destruction of objects -and arrays. - -[endsect] - -[section Example] - -The following example allocates storage for an array of `n` elements of `T` -using an allocator `a` and constructs `T` elements in that storage. If any -exception was thrown during construction of an element, the constructed -elements are destroyed in reverse order. - -``` -template -auto create(A& a, std::size_t n) -{ - auto p = a.allocate(n); - try { - boost::alloc_construct_n(a, boost::to_address(p), n); - } catch (...) { - a.deallocate(p, n); - throw; - } - return p; -} -``` - -[endsect] - -[section Reference] - -``` -namespace boost { - -template -void alloc_destroy(A& a, T* p); - -template -void alloc_destroy_n(A& a, T* p, std::size_t n); - -template -void alloc_construct(A& a, T* p, Args&&... args); - -template -void alloc_construct_n(A& a, T* p, std::size_t n); - -template -void alloc_construct_n(A& a, T* p, std::size_t n, const T* l, std::size_t m); - -template -void alloc_construct_n(A& a, T* p, std::size_t n, I begin); - -} /* boost */ -``` - -[section Functions] - -[variablelist -[[`template void alloc_destroy(A& a, T* p);`] -[[variablelist -[[Requires][`A` is an /Allocator/]] -[[Effects][`std::allocator_traits::destroy(a, p)`.]]]]] -[[`template void alloc_destroy_n(A& a, T* p, -std::size_t n);`] -[[variablelist -[[Requires][`A` is an /Allocator/]] -[[Effects] -[Destroys each `i`-th element in reverse order by calling -`std::allocator_traits::destroy(a, &p[i])`.]]]]] -[[`template void alloc_construct(A& a, T* p, -Args&&... args);`] -[[variablelist -[[Requires][`A` is an /Allocator/]] -[[Effects] -[`std::allocator_traits::construct(a, p, std::forward(args)...)`.]]]]] -[[`template void alloc_construct_n(A& a, T* p, -std::size_t n);`] -[[variablelist -[[Requires][`A` is an /Allocator/]] -[[Effects] -[Constructs each `i`-th element in order by calling -`std::allocator_traits::construct(a, &p[i])`.]] -[[Remarks] -[If an exception is thrown destroys each already constructed `j`-th element in -reverse order by calling `std::allocator_traits::destroy(a, &p[j])`.]]]]] -[[`template void alloc_construct_n(A& a, T* p, std::size_t n, -const T* l, std::size_t m);`] -[[variablelist -[[Requires][`A` is an /Allocator/]] -[[Effects] -[Constructs each `i`-th element in order by calling -`std::allocator_traits::construct(a, &p[i], l[i % m])`.]] -[[Remarks] -[If an exception is thrown destroys each already constructed `j`-th element in -reverse order by calling `std::allocator_traits::destroy(a, &p[j])`.]]]]] -[[`template void alloc_construct_n(A& a, T* p, -std::size_t n, I begin);`] -[[variablelist -[[Requires] -[[itemized_list -[`A` is an /Allocator/][`I` is an /InputIterator/]]]] -[[Effects] -[Constructs each `i`-th element in order by calling -`std::allocator_traits::construct(a, &p[i], *begin++])`.]] -[[Remarks] -[If an exception is thrown destroys each already constructed `j`-th element in -reverse order by calling `std::allocator_traits::destroy(a, &p[j])`.]]]]]] - -[endsect] - -[endsect] - -[section Compatibility] - -When `BOOST_NO_CXX11_ALLOCATOR` is defined, and the C++11 allocator model is -not supported, these functions invoke constructors and destructors directly -without going through the supplied allocator. - -[endsect] - -[section Acknowledgments] - -Glen Fernandes originally implemented this functionality in Boost.Smart_Ptr and -later moved these functions to Boost.Core for use in other Boost libraries, -such as Boost.Multi_Array and Boost.Histogram. - -[endsect] - -[endsect] diff --git a/doc/allocator_access.qbk b/doc/allocator_access.qbk index 06161ce..1a0c82f 100644 --- a/doc/allocator_access.qbk +++ b/doc/allocator_access.qbk @@ -1,5 +1,5 @@ [/ -Copyright 2020 Glen Joseph Fernandes +Copyright 2020-2022 Glen Joseph Fernandes (glenjofe@gmail.com) Distributed under the Boost Software License, Version 1.0. @@ -21,6 +21,9 @@ templates to simplify allocator use. It provides the same functionality as the C++ standard library `std::allocator_traits` but with individual templates for each allocator feature. +It also adds additional functionality for allocator aware exception safe +construction and destruction of arrays. + These facilities also simplify existing libraries by avoiding having to check for `BOOST_NO_CXX11_ALLOCATOR` and conditionally use `std::allocator_traits`. @@ -51,6 +54,26 @@ public: In C++11 or above, aliases such as `boost::allocator_pointer_t` can be used instead of `typename boost::allocator_pointer::type`. +The following example allocates storage for an array of `n` elements of `T` +using an allocator `a` and constructs `T` elements in that storage. If any +exception was thrown during construction of an element, the constructed +elements are destroyed in reverse order. + +``` +template +auto create(A& a, std::size_t n) +{ + auto p = a.allocate(n); + try { + boost::allocator_construct_n(a, boost::to_address(p), n); + } catch (...) { + a.deallocate(p, n); + throw; + } + return p; +} +``` + [endsect] [section Reference] @@ -150,9 +173,22 @@ void allocator_deallocate(A& a, allocator_pointer_t p, template void allocator_construct(A& a, T* p, Args&&... args); +template +void allocator_construct_n(A& a, T* p, std::size_t n); + +template +void allocator_construct_n(A& a, T* p, std::size_t n, const T* l, + std::size_t m); + +template +void allocator_construct_n(A& a, T* p, std::size_t n, I begin); + template void allocator_destroy(A& a, T* p); +template +void allocator_destroy_n(A& a, T* p, std::size_t n); + template allocator_size_type_t allocator_max_size(const A& a); @@ -220,8 +256,30 @@ allocator_size_type_t n);`] void allocator_construct(A& a, T*p, Args&&... args);`] [Calls `a.construct(p, std::forward(args)...)` if valid, otherwise calls `::new(static_cast(p)) T(std::forward(args)...)`.]] +[[`template +void alloc_construct_n(A& a, T* p, std::size_t n);`] +[Constructs each `i`-th element in order by calling +`boost::allocator_construct(a, &p[i])`. +If an exception is thrown destroys each already constructed `j`-th element in +reverse order by calling `boost::allocator_destroy(a, &p[j])`.]] +[[`template +void alloc_construct_n(A& a, T* p, std::size_t n, const T* l, std::size_t m);`] +[Constructs each `i`-th element in order by calling +`boost::allocator_construct(a, &p[i], l[i % m])`. +If an exception is thrown destroys each already constructed `j`-th element in +reverse order by calling `boost::allocator_destroy(a, &p[j])`.]] +[[`template +void alloc_construct_n(A& a, T* p, std::size_t n, I begin);`] +[Constructs each `i`-th element in order by calling +`boost::allocator_construct(a, &p[i], *begin++)`. +If an exception is thrown destroys each already constructed `j`-th element in +reverse order by calling `boost::allocator_destroy(a, &p[j])`.]] [[`template void allocator_destroy(A& a, T* p);`] [Calls `a.destroy(p)` if valid, otherwise calls `p->~T()`.]] +[[`template +void allocator_destroy_n(A& a, T* p, std::size_t n);`] +[Destroys each `i`-th element in reverse order by calling +`boost::allocator_destroy(a, &p[i])`.]] [[`template allocator_size_type_t allocator_max_size(const A& a);`] [Returns `a.max_size()` if valid, otherwise returns `std::numeric_limits >::max() / diff --git a/doc/core.qbk b/doc/core.qbk index 33e2faf..fd677c4 100644 --- a/doc/core.qbk +++ b/doc/core.qbk @@ -43,7 +43,6 @@ criteria for inclusion is that the utility component be: [include addressof.qbk] [include allocator_access.qbk] [include allocator_traits.qbk] -[include alloc_construct.qbk] [include bit.qbk] [include checked_delete.qbk] [include cmath.qbk] diff --git a/include/boost/core/alloc_construct.hpp b/include/boost/core/alloc_construct.hpp index e390730..075d3cb 100644 --- a/include/boost/core/alloc_construct.hpp +++ b/include/boost/core/alloc_construct.hpp @@ -8,6 +8,9 @@ Distributed under the Boost Software License, Version 1.0. #ifndef BOOST_CORE_ALLOC_CONSTRUCT_HPP #define BOOST_CORE_ALLOC_CONSTRUCT_HPP +/* +This functionality is now in . +*/ #include namespace boost { @@ -23,56 +26,9 @@ template inline void alloc_destroy_n(A& a, T* p, std::size_t n) { - while (n > 0) { - boost::allocator_destroy(a, p + --n); - } + boost::allocator_destroy_n(a, p, n); } -template -inline void -alloc_destroy(noinit_adaptor&, T* p) -{ - p->~T(); -} - -template -inline void -alloc_destroy_n(noinit_adaptor&, T* p, std::size_t n) -{ - while (n > 0) { - p[--n].~T(); - } -} - -namespace detail { - -template -class alloc_destroyer { -public: - alloc_destroyer(A& a, T* p) BOOST_NOEXCEPT - : a_(a), - p_(p), - n_(0) { } - - ~alloc_destroyer() { - boost::alloc_destroy_n(a_, p_, n_); - } - - std::size_t& size() BOOST_NOEXCEPT { - return n_; - } - -private: - alloc_destroyer(const alloc_destroyer&); - alloc_destroyer& operator=(const alloc_destroyer&); - - A& a_; - T* p_; - std::size_t n_; -}; - -} /* detail */ - template inline void alloc_construct(A& a, T* p) @@ -117,51 +73,21 @@ template inline void alloc_construct_n(A& a, T* p, std::size_t n) { - detail::alloc_destroyer hold(a, p); - for (std::size_t& i = hold.size(); i < n; ++i) { - boost::allocator_construct(a, p + i); - } - hold.size() = 0; + boost::allocator_construct_n(a, p, n); } template inline void alloc_construct_n(A& a, T* p, std::size_t n, const T* l, std::size_t m) { - detail::alloc_destroyer hold(a, p); - for (std::size_t& i = hold.size(); i < n; ++i) { - boost::allocator_construct(a, p + i, l[i % m]); - } - hold.size() = 0; + boost::allocator_construct_n(a, p, n, l, m); } template inline void alloc_construct_n(A& a, T* p, std::size_t n, I b) { - detail::alloc_destroyer hold(a, p); - for (std::size_t& i = hold.size(); i < n; void(++i), void(++b)) { - boost::allocator_construct(a, p + i, *b); - } - hold.size() = 0; -} - -template -inline void -alloc_construct(noinit_adaptor&, T* p) -{ - ::new(static_cast(p)) T; -} - -template -inline void -alloc_construct_n(noinit_adaptor& a, T* p, std::size_t n) -{ - detail::alloc_destroyer, T> hold(a, p); - for (std::size_t& i = hold.size(); i < n; ++i) { - ::new(static_cast(p + i)) T; - } - hold.size() = 0; + boost::allocator_construct_n(a, p, n, b); } } /* boost */ diff --git a/include/boost/core/allocator_access.hpp b/include/boost/core/allocator_access.hpp index e8e5f58..7717ba0 100644 --- a/include/boost/core/allocator_access.hpp +++ b/include/boost/core/allocator_access.hpp @@ -714,6 +714,75 @@ allocator_select_on_container_copy_construction(const A& a) return a; } +template +inline void +allocator_destroy_n(A& a, T* p, std::size_t n) +{ + while (n > 0) { + boost::allocator_destroy(a, p + --n); + } +} + +namespace detail { + +template +class alloc_destroyer { +public: + alloc_destroyer(A& a, T* p) BOOST_NOEXCEPT + : a_(a), p_(p), n_(0) { } + + ~alloc_destroyer() { + boost::allocator_destroy_n(a_, p_, n_); + } + + std::size_t& size() BOOST_NOEXCEPT { + return n_; + } + +private: + alloc_destroyer(const alloc_destroyer&); + alloc_destroyer& operator=(const alloc_destroyer&); + + A& a_; + T* p_; + std::size_t n_; +}; + +} /* detail */ + +template +inline void +allocator_construct_n(A& a, T* p, std::size_t n) +{ + detail::alloc_destroyer d(a, p); + for (std::size_t& i = d.size(); i < n; ++i) { + boost::allocator_construct(a, p + i); + } + d.size() = 0; +} + +template +inline void +allocator_construct_n(A& a, T* p, std::size_t n, const T* l, std::size_t m) +{ + detail::alloc_destroyer d(a, p); + for (std::size_t& i = d.size(); i < n; ++i) { + boost::allocator_construct(a, p + i, l[i % m]); + } + d.size() = 0; +} + +template +inline void +allocator_construct_n(A& a, T* p, std::size_t n, I b) +{ + detail::alloc_destroyer d(a, p); + for (std::size_t& i = d.size(); i < n; void(++i), void(++b)) { + boost::allocator_construct(a, p + i, *b); + } + d.size() = 0; +} + #if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) template using allocator_value_type_t = typename allocator_value_type::type; diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 4bb7499..71a6544 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -242,6 +242,8 @@ run allocator_max_size_test.cpp ; run allocator_soccc_test.cpp ; run allocator_construct_test.cpp ; run allocator_destroy_test.cpp ; +run allocator_construct_n_test.cpp ; +run allocator_destroy_n_test.cpp ; run allocator_traits_test.cpp ; lib lib_typeid : lib_typeid.cpp : shared:LIB_TYPEID_DYN_LINK=1 ; diff --git a/test/allocator_construct_n_test.cpp b/test/allocator_construct_n_test.cpp new file mode 100644 index 0000000..2e0b898 --- /dev/null +++ b/test/allocator_construct_n_test.cpp @@ -0,0 +1,47 @@ +/* +Copyright 2022 Glen Joseph Fernandes +(glenjofe@gmail.com) + +Distributed under the Boost Software License, Version 1.0. +(http://www.boost.org/LICENSE_1_0.txt) +*/ +#include +#include + +template +struct A { + typedef T value_type; + A() { } +}; + +int main() +{ + { + A a; + int i[3] = { 5, 5, 5 }; + boost::allocator_construct_n(a, &i[0], 3); + BOOST_TEST_EQ(i[0], 0); + BOOST_TEST_EQ(i[1], 0); + BOOST_TEST_EQ(i[2], 0); + } + { + A a; + int i[4] = { 5, 5, 5, 5 }; + int j[2] = { 1, 2 }; + boost::allocator_construct_n(a, &i[0], 4, &j[0], 2); + BOOST_TEST_EQ(i[0], 1); + BOOST_TEST_EQ(i[1], 2); + BOOST_TEST_EQ(i[2], 1); + BOOST_TEST_EQ(i[3], 2); + } + { + A a; + int i[3] = { 5, 5, 5 }; + int j[3] = { 1, 2, 3 }; + boost::allocator_construct_n(a, &i[0], 3, &j[0]); + BOOST_TEST_EQ(i[0], 1); + BOOST_TEST_EQ(i[1], 2); + BOOST_TEST_EQ(i[2], 3); + } + return boost::report_errors(); +} diff --git a/test/allocator_destroy_n_test.cpp b/test/allocator_destroy_n_test.cpp new file mode 100644 index 0000000..fc19469 --- /dev/null +++ b/test/allocator_destroy_n_test.cpp @@ -0,0 +1,30 @@ +/* +Copyright 2022 Glen Joseph Fernandes +(glenjofe@gmail.com) + +Distributed under the Boost Software License, Version 1.0. +(http://www.boost.org/LICENSE_1_0.txt) +*/ +#include +#include + +template +struct A { + typedef T value_type; + A() { } + template + void destroy(U* p) { + *p = U(); + } +}; + +int main() +{ + A a; + int i[3] = { 5, 5, 5 }; + boost::allocator_destroy_n(a, &i[0], 3); + BOOST_TEST_EQ(i[0], 0); + BOOST_TEST_EQ(i[1], 0); + BOOST_TEST_EQ(i[2], 0); + return boost::report_errors(); +}