diff --git a/doc/alloc_construct.qbk b/doc/alloc_construct.qbk new file mode 100644 index 0000000..85f296b --- /dev/null +++ b/doc/alloc_construct.qbk @@ -0,0 +1,125 @@ +[/ +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(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, U&&... u); + +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); + +} /* 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 element in reverse order by calling +`std::allocator_traits::destroy(a, &p[i])`.]]]]] +[[`template void alloc_construct(A& a, T* p, +U&&... u);`] +[[variablelist +[[Requires][`A` is an _Allocator_]] +[[Effects] +[`std::allocator_traits::construct(a, p, std::forward(u)...)`.]]]]] +[[`template void alloc_construct_n(A& a, T* p, +std::size_t n);`] +[[variablelist +[[Requires][`A` is an _Allocator_]] +[[Effects] +[Constructs each element in order by calling +`std::allocator_traits::construct(a, &p[i])`.]] +[[Remarks] +[If an exception is thrown, destroys each element in reverse order by +calling `std::allocator_traits::destroy(a, &p[i])`.]]]]] +[[`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 element in order by calling +`std::allocator_traits::construct(a, &p[i], l[i % m])`.]] +[[Remarks] +[If an exception is thrown, destroys each element in reverse order by +calling `std::allocator_traits::destroy(a, &p[i])`.]]]]]] + +[endsect] + +[endsect] + +[section Acknowledgments] + +Glen Fernandes originally implemented this functionality in Boost.Smart_ptr and +later moved these functions to Boost.Core for use in multiple Boost libraries. + +[endsect] + +[endsect] diff --git a/doc/core.qbk b/doc/core.qbk index aacff9e..b4f8756 100644 --- a/doc/core.qbk +++ b/doc/core.qbk @@ -39,6 +39,7 @@ criteria for inclusion is that the utility component be: [endsect] [include addressof.qbk] +[include alloc_construct.qbk] [include checked_delete.qbk] [include default_allocator.qbk] [include demangle.qbk] diff --git a/include/boost/core/alloc_construct.hpp b/include/boost/core/alloc_construct.hpp new file mode 100644 index 0000000..2e6f9d0 --- /dev/null +++ b/include/boost/core/alloc_construct.hpp @@ -0,0 +1,209 @@ +/* +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) +*/ +#ifndef BOOST_CORE_ALLOC_CONSTRUCT_HPP +#define BOOST_CORE_ALLOC_CONSTRUCT_HPP + +#include + +namespace boost { + +#if !defined(BOOST_NO_CXX11_ALLOCATOR) +template +inline void +alloc_destroy(A& a, T* p) +{ + std::allocator_traits::destroy(a, p); +} + +template +inline void +alloc_destroy_n(A& a, T* p, std::size_t n) +{ + while (n > 0) { + std::allocator_traits::destroy(a, p + --n); + } +} +#else +template +inline void +alloc_destroy(A&, T* p) +{ + p->~T(); +} + +template +inline void +alloc_destroy_n(A&, T* p, std::size_t n) +{ + while (n > 0) { + p[--n].~T(); + } +} +#endif + +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 */ + +#if !defined(BOOST_NO_CXX11_ALLOCATOR) +template +inline void +alloc_construct(A& a, T* p) +{ + std::allocator_traits::construct(a, p); +} + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) +template +inline void +alloc_construct(A& a, T* p, U&& u, V&&... v) +{ + std::allocator_traits::construct(a, p, std::forward(u), + std::forward(v)...); +} +#else +template +inline void +alloc_construct(A& a, T* p, U&& u) +{ + std::allocator_traits::construct(a, p, std::forward(u)); +} +#endif +#else +template +inline void +alloc_construct(A& a, T* p, const U& u) +{ + std::allocator_traits::construct(a, p, u); +} +#endif + +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) { + std::allocator_traits::construct(a, p + i); + } + hold.size() = 0; +} + +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) { + std::allocator_traits::construct(a, p + i, l[i % m]); + } + hold.size() = 0; +} +#else +template +inline void +alloc_construct(A&, T* p) +{ + ::new(static_cast(p)) T(); +} + +template +inline void +alloc_construct(noinit_adaptor&, T* p) +{ + ::new(static_cast(p)) T; +} + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) +template +inline void +alloc_construct(A&, T* p, U&& u, V&&... v) +{ + ::new(static_cast(p)) T(std::forward(u), std::forward(v)...); +} +#else +template +inline void +alloc_construct(A& a, T* p, U&& u) +{ + ::new(static_cast(p)) T(std::forward(u)); +} +#endif +#else +template +inline void +alloc_construct(A&, T* p, const U& u) +{ + ::new(static_cast(p)) T(u); +} +#endif + +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) { + ::new(static_cast(p + i)) T(); + } + hold.size() = 0; +} + +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; +} + +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) { + ::new(static_cast(p + i)) T(l[i % m]); + } + hold.size() = 0; +} +#endif + +} /* boost */ + +#endif diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 238c4b9..fc33e91 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -138,6 +138,9 @@ compile use_default_test.cpp ; run default_allocator_test.cpp ; run noinit_adaptor_test.cpp ; +run alloc_construct_test.cpp ; +run alloc_construct_throws_test.cpp ; +run alloc_construct_cxx11_test.cpp ; lib lib_typeid : lib_typeid.cpp : shared:LIB_TYPEID_DYN_LINK=1 ; diff --git a/test/alloc_construct_cxx11_test.cpp b/test/alloc_construct_cxx11_test.cpp new file mode 100644 index 0000000..ed2bad5 --- /dev/null +++ b/test/alloc_construct_cxx11_test.cpp @@ -0,0 +1,79 @@ +/* +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) +*/ +#include +#if !defined(BOOST_NO_CXX11_ALLOCATOR) +#include +#include + +class type { +public: + explicit type(int x) + : value_(x) { } + + int value() const { + return value_; + } + + static int count; + +private: + type(const type&); + type& operator=(const type&); + + int value_; +}; + +int type::count = 0; + +template +struct creator { + typedef T value_type; + + creator() { } + + template + creator(const creator&) { } + + T* allocate(std::size_t size) { + return static_cast(::operator new(sizeof(T) * size)); + } + + void deallocate(T* ptr, std::size_t) { + ::operator delete(ptr); + } + + template + void construct(U* ptr, const V& value) { + ::new(static_cast(ptr)) U(value + 1); + ++type::count; + } + + template + void destroy(U* ptr) { + ptr->~U(); + --type::count; + } +}; + +int main() +{ + creator a; + type* p = a.allocate(1); + boost::alloc_construct(a, p, 1); + BOOST_TEST_EQ(type::count, 1); + BOOST_TEST_EQ(p->value(), 2); + boost::alloc_destroy(a, p); + BOOST_TEST_EQ(type::count, 0); + return boost::report_errors(); +} +#else +int main() +{ + return 0; +} +#endif diff --git a/test/alloc_construct_test.cpp b/test/alloc_construct_test.cpp new file mode 100644 index 0000000..3e46e3c --- /dev/null +++ b/test/alloc_construct_test.cpp @@ -0,0 +1,119 @@ +/* +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) +*/ +#include +#include +#include + +class type { +public: + explicit type(int x = 0, int y = 0) + : value_(x + y) { + ++count; + } + + type(const type& other) + : value_(other.value_) { + ++count; + } + + ~type() { + --count; + } + + int value() const { + return value_; + } + + static int count; + +private: + int value_; +}; + +int type::count = 0; + +void test_construct() +{ + boost::default_allocator a; + type* p = a.allocate(1); + boost::alloc_construct(a, p); + BOOST_TEST_EQ(type::count, 1); + BOOST_TEST_EQ(p->value(), 0); + boost::alloc_destroy(a, p); + BOOST_TEST_EQ(type::count, 0); + a.deallocate(p, 1); +} + +void test_construct_value() +{ + boost::default_allocator a; + type* p = a.allocate(1); + boost::alloc_construct(a, p, 1); + BOOST_TEST_EQ(type::count, 1); + BOOST_TEST_EQ(p->value(), 1); + boost::alloc_destroy(a, p); + BOOST_TEST_EQ(type::count, 0); + a.deallocate(p, 1); +} + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && \ + !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) +void test_construct_args() +{ + boost::default_allocator a; + type* p = a.allocate(1); + boost::alloc_construct(a, p, 1, 2); + BOOST_TEST_EQ(type::count, 1); + BOOST_TEST_EQ(p->value(), 3); + boost::alloc_destroy(a, p); + BOOST_TEST_EQ(type::count, 0); + a.deallocate(p, 1); +} +#endif + +void test_construct_n() +{ + boost::default_allocator a; + type* p = a.allocate(3); + boost::alloc_construct_n(a, p, 3); + BOOST_TEST_EQ(type::count, 3); + BOOST_TEST_EQ(p[0].value(), 0); + BOOST_TEST_EQ(p[1].value(), 0); + BOOST_TEST_EQ(p[2].value(), 0); + boost::alloc_destroy_n(a, p, 3); + BOOST_TEST_EQ(type::count, 0); + a.deallocate(p, 3); +} + +void test_construct_n_list() +{ + boost::default_allocator a; + type* p = a.allocate(3); + type q(1); + boost::alloc_construct_n(a, p, 3, &q, 1); + BOOST_TEST_EQ(type::count, 4); + BOOST_TEST_EQ(p[0].value(), 1); + BOOST_TEST_EQ(p[1].value(), 1); + BOOST_TEST_EQ(p[2].value(), 1); + boost::alloc_destroy_n(a, p, 3); + BOOST_TEST_EQ(type::count, 1); + a.deallocate(p, 3); +} + +int main() +{ + test_construct(); + test_construct_value(); +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && \ + !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + test_construct_args(); +#endif + test_construct_n(); + test_construct_n_list(); + return boost::report_errors(); +} diff --git a/test/alloc_construct_throws_test.cpp b/test/alloc_construct_throws_test.cpp new file mode 100644 index 0000000..0a86fc8 --- /dev/null +++ b/test/alloc_construct_throws_test.cpp @@ -0,0 +1,46 @@ +/* +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) +*/ +#include +#include +#include + +class type { +public: + type() { + if (count == 4) { + throw true; + } + ++count; + } + + ~type() { + --count; + } + + static int count; + +private: + type(const type&); + type& operator=(const type&); +}; + +int type::count = 0; + +int main() +{ + boost::default_allocator a; + type* p = a.allocate(5); + try { + boost::alloc_construct_n(a, p, 5); + BOOST_ERROR("construct_n did not throw"); + } catch (...) { + BOOST_TEST_EQ(type::count, 0); + } + a.deallocate(p, 5); + return boost::report_errors(); +}