From 3972bab1e84d2ca89ceaa1b58e5b03164e98a554 Mon Sep 17 00:00:00 2001 From: Ole Erik Peistorpet Date: Thu, 6 Jun 2024 12:09:48 +0200 Subject: [PATCH] explicit dynarray copy constructor, and from_range Also operator =(const dynarray &&) = delete --- auxi/dynarray_detail.h | 4 +- dynarray.h | 44 ++++++++++--------- ...dynarray_construct_assignop_swap_gtest.cpp | 26 ++++++----- unit_test/dynarray_mutate_gtest.cpp | 2 +- util.h | 11 +++++ 5 files changed, 52 insertions(+), 35 deletions(-) diff --git a/auxi/dynarray_detail.h b/auxi/dynarray_detail.h index 7ee5fb5c..ee4973b2 100644 --- a/auxi/dynarray_detail.h +++ b/auxi/dynarray_detail.h @@ -6,7 +6,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#include "core_util.h" +#include "util.h" // for from_range #include // for uintptr_t #include @@ -144,7 +144,7 @@ namespace oel::_detail template< typename Range > friend auto operator |(Range && r, ToDynarrPartial t) { - return dynarray(static_cast(r), std::move(t)._a); + return dynarray(from_range, static_cast(r), std::move(t)._a); } }; } \ No newline at end of file diff --git a/dynarray.h b/dynarray.h index fcf3a0a6..0f828707 100644 --- a/dynarray.h +++ b/dynarray.h @@ -29,10 +29,15 @@ struct _toDynarrayFn return _detail::ToDynarrPartial{std::move(a)}; } }; -//! Equivalent to `std::ranges::to` (piped) +//! `r | to_dynarray()` is equivalent to `r | std::ranges::to()` +/** +* Example, convert array of std::bitset to `dynarray`: +@code +std::bitset<8> arr[] {3, 5, 7, 11}; +auto result = arr | view::transform(OEL_MEMBER_FN(to_string)) | to_dynarray(); +@endcode */ inline constexpr _toDynarrayFn to_dynarray; - //! Overloads generic unordered_erase(RandomAccessContainer &, Integral) (in range_algo.h) template< typename T, typename A > inline void unordered_erase(dynarray & d, ptrdiff_t index) { d.unordered_erase(d.begin() + index); } @@ -97,28 +102,19 @@ class dynarray explicit dynarray(size_type size, Alloc a = Alloc{}); dynarray(size_type size, const T & val, Alloc a = Alloc{}) : _m(a) { append(size, val); } - /** @brief Equivalent to `std::vector(std::from_range, r, a)`, except `end(r)` is not needed if `r.size()` is valid - * - * To move instead of copy, wrap r with view::move (The same applies for all functions taking a range template) - * - * Example, construct from a standard istream with formatting (using Boost): - @code - #include - auto result = dynarray(boost::range::istream_range(someStream)); - @endcode */ - template< typename InputRange, - typename /*EnableIfRange*/ = iterator_t, - enable_if< !_detail::isSameSansCVRef > = 0 - > - explicit dynarray(InputRange && r, Alloc a = Alloc{}) : _m(a) { append(r); } - - dynarray(std::initializer_list il, Alloc a = Alloc{}) : _m(a) { append(il); } + //! Equivalent to `std::vector(std::from_range, r, a)`, except `end(r)` is not needed if `r.size()` is valid + /** + * To move instead of copy, wrap `r` with view::move (The same applies for all functions taking a range) */ + template< typename InputRange > + dynarray(from_range_t, InputRange && r, Alloc a = Alloc{}) : _m(a) { append(r); } + + dynarray(std::initializer_list il, Alloc a = Alloc{}) : _m(a) { append(il); } dynarray(dynarray && other) noexcept : _m(std::move(other._m)) {} dynarray(dynarray && other, Alloc a); - dynarray(const dynarray & other) : dynarray(other, + explicit dynarray(const dynarray & other) : dynarray(other, _alloTrait::select_on_container_copy_construction(other._m)) {} - dynarray(const dynarray & other, Alloc a) : _m(a) { append(other); } + explicit dynarray(const dynarray & other, Alloc a) : _m(a) { append(other); } ~dynarray() noexcept; @@ -126,6 +122,7 @@ class dynarray noexcept(_alloTrait::propagate_on_container_move_assignment::value or _alloTrait::is_always_equal::value); //! Requires that allocator_type is always equal or does not have propagate_on_container_copy_assignment dynarray & operator =(const dynarray & other) &; + dynarray & operator =(const dynarray &&) = delete; dynarray & operator =(std::initializer_list il) & { assign(il); return *this; } @@ -931,12 +928,17 @@ typename dynarray::iterator dynarray::erase(iterator first, template< typename InputRange, typename Alloc = allocator<> > -explicit dynarray(InputRange &&, Alloc = {}) +dynarray(from_range_t, InputRange &&, Alloc = {}) -> dynarray< iter_value_t< iterator_t >, Alloc >; +#if defined __GNUC__ and __GNUC__ < 12 + template< typename T, typename A > + explicit dynarray(const dynarray &) -> dynarray; +#endif + #if OEL_MEM_BOUND_DEBUG_LVL } // namespace debug #endif diff --git a/unit_test/dynarray_construct_assignop_swap_gtest.cpp b/unit_test/dynarray_construct_assignop_swap_gtest.cpp index bb608435..92cdc086 100644 --- a/unit_test/dynarray_construct_assignop_swap_gtest.cpp +++ b/unit_test/dynarray_construct_assignop_swap_gtest.cpp @@ -257,7 +257,7 @@ TEST_F(dynarrayConstructTest, deductionGuides) static_assert(std::is_same< decltype(d)::allocator_type, StatefulAllocator >()); EXPECT_EQ(d.size(), ar.size()); - dynarray fromTemp(std::array{}); + dynarray fromTemp(from_range, std::array{}); static_assert(std::is_same< decltype(fromTemp)::allocator_type, oel::allocator >()); dynarray sizeAndVal(2, 1.f); @@ -268,7 +268,7 @@ TEST_F(dynarrayConstructTest, deductionGuides) TEST_F(dynarrayConstructTest, constructContiguousRange) { std::string str = "AbCd"; - dynarray test(str); + dynarray test(from_range, str); static_assert(std::is_same()); EXPECT_TRUE( 0 == str.compare(0, 4, test.data(), test.size()) ); } @@ -276,7 +276,7 @@ TEST_F(dynarrayConstructTest, constructContiguousRange) TEST_F(dynarrayConstructTest, constructRangeNoCopyAssign) { auto il = { 1.2, 3.4 }; - dynarray test(il); + dynarray test(from_range, il); EXPECT_TRUE(test.size() == 2); } @@ -285,7 +285,7 @@ TEST_F(dynarrayConstructTest, constructForwardRangeNoSize) for (auto const n : {0u, 1u, 59u}) { std::forward_list li(n, -6); - dynarray d(li); + dynarray d(from_range, li); EXPECT_EQ(n, d.size()); if (0 != n) { @@ -298,7 +298,7 @@ TEST_F(dynarrayConstructTest, constructForwardRangeNoSize) TEST_F(dynarrayConstructTest, constructRangeMutableBeginSize) { int src[1] {1}; - dynarray d(ToMutableBeginSizeView(src)); + dynarray d(from_range, ToMutableBeginSizeView(src)); EXPECT_EQ(1u, d.size()); } @@ -307,27 +307,31 @@ TEST_F(dynarrayConstructTest, constructRangeMutableBeginSize) TEST_F(dynarrayConstructTest, constructMoveOnlyIterator) { std::istringstream words{"Falling Anywhere"}; - auto d = dynarray(std::views::istream(words)); + auto d = dynarray(from_range, std::views::istream(words)); EXPECT_EQ(2u, d.size()); EXPECT_EQ("Falling", d[0]); EXPECT_EQ("Anywhere", d[1]); } #endif - TEST_F(dynarrayConstructTest, copyConstruct) { using Al = StatefulAllocator; auto x = dynarray< TrivialRelocat, Al >({TrivialRelocat{0.5}}, Al(-5)); - EXPECT_EQ(1, g_allocCount.nAllocations); auto y = dynarray(x); EXPECT_EQ(-5, y.get_allocator().id); EXPECT_EQ(2, g_allocCount.nAllocations); - auto z = dynarray(y, Al(7)); + auto const z = dynarray(y, Al(7)); EXPECT_EQ(0.5, *z.front()); - EXPECT_EQ(3, g_allocCount.nAllocations); + + auto const d = dynarray(std::move(z)); + EXPECT_EQ(0.5, *d.front()); + + auto const e = dynarray(std::move(d), Al(9)); + EXPECT_EQ(9, e.get_allocator().id); + EXPECT_EQ(5, g_allocCount.nAllocations); } template @@ -607,7 +611,7 @@ TEST_F(dynarrayConstructTest, constructInputRangeThrowing) MoveOnly::countToThrowOn = 1; ASSERT_THROW( - dynarray(view::subrange(f, l)), + dynarray(from_range, view::subrange(f, l)), TestException ); } diff --git a/unit_test/dynarray_mutate_gtest.cpp b/unit_test/dynarray_mutate_gtest.cpp index f75eb1bb..4b18bb0d 100644 --- a/unit_test/dynarray_mutate_gtest.cpp +++ b/unit_test/dynarray_mutate_gtest.cpp @@ -831,7 +831,7 @@ TEST_F(dynarrayTest, erasePrecondCheck) leakDetector->enabled = false; dynarray di{-2}; - auto copy = di; + auto copy = dynarray(di); ASSERT_DEATH( copy.erase(di.begin()), "" ); } diff --git a/util.h b/util.h index bf70a688..59b9ee7a 100644 --- a/util.h +++ b/util.h @@ -100,6 +100,17 @@ struct for_overwrite_t }; inline constexpr for_overwrite_t for_overwrite; //!< An instance of for_overwrite_t for convenience +#if __cpp_lib_containers_ranges < 202202 + struct from_range_t + { + explicit from_range_t() = default; + }; + inline constexpr from_range_t from_range; +#else + using std::from_range_t; + using std::from_range; +#endif + //! Same as `begin(range)` with a previous `using std::begin;`. For use in classes with a member named begin