From 898745be86e9370c3b52033cd55a0beb4bb1ca97 Mon Sep 17 00:00:00 2001 From: Ole Erik Peistorpet Date: Mon, 27 May 2024 16:56:28 +0200 Subject: [PATCH] view::generate always std::input_range --- auxi/detail_tight_pair.h | 68 +++++++++++++++++++++++++++++++++++ unit_test/view_gtest.cpp | 21 +++++++---- util.h | 28 --------------- view/generate.h | 41 +++++++++++++++------ view/subrange.h | 3 +- view/transform_iterator.h | 75 +++++++++++++-------------------------- 6 files changed, 139 insertions(+), 97 deletions(-) create mode 100644 auxi/detail_tight_pair.h diff --git a/auxi/detail_tight_pair.h b/auxi/detail_tight_pair.h new file mode 100644 index 00000000..c762d74a --- /dev/null +++ b/auxi/detail_tight_pair.h @@ -0,0 +1,68 @@ +#pragma once + +// Copyright 2020 Ole Erik Peistorpet +// +// 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) + + +#include "core_util.h" + + +namespace oel::_detail +{ + template< typename T, typename U, + bool = std::is_empty_v > + struct TightPair + { + T first; + U _sec; + + OEL_ALWAYS_INLINE constexpr const U & second() const { return _sec; } + OEL_ALWAYS_INLINE constexpr U & second() { return _sec; } + }; + + // MSVC needs unique names to guard against name collision due to inheritance + template< typename T_Qai6Y, typename U_Qai6Y > + struct TightPair + : U_Qai6Y + { + T_Qai6Y first; + + TightPair() = default; + constexpr TightPair(T_Qai6Y f, U_Qai6Y s) + : U_Qai6Y{s}, first{std::move(f)} + {} + + OEL_ALWAYS_INLINE constexpr const U_Qai6Y & second() const { return *this; } + OEL_ALWAYS_INLINE constexpr U_Qai6Y & second() { return *this; } + }; + + + template< typename T, typename F, + bool = std::is_empty_v > + struct TightPairMutableFn + { + T first; + F mutable _fn; + + OEL_ALWAYS_INLINE constexpr F & func() const { return _fn; } + }; + + template< typename T_Qai6Y, typename F_Qai6Y > + struct TightPairMutableFn + : F_Qai6Y + { + T_Qai6Y first; + + TightPairMutableFn() = default; + constexpr TightPairMutableFn(T_Qai6Y fir, F_Qai6Y fn) + : F_Qai6Y{fn}, first{std::move(fir)} + {} + + OEL_ALWAYS_INLINE constexpr F_Qai6Y & func() const + { + return const_cast(*this); + } + }; +} \ No newline at end of file diff --git a/unit_test/view_gtest.cpp b/unit_test/view_gtest.cpp index 3d57c132..6819292b 100644 --- a/unit_test/view_gtest.cpp +++ b/unit_test/view_gtest.cpp @@ -144,10 +144,9 @@ using StdArrInt2 = std::array; constexpr auto multBy2(StdArrInt2 a) { StdArrInt2 res{}; - struct - { constexpr auto operator()(int i) const { return 2 * i; } - } mult2{}; + auto mult2 = [j = 2](int i) { return i * j; }; auto v = view::transform(a, mult2); + size_t i{}; for (auto val : v) res[i++] = val; @@ -253,6 +252,13 @@ struct Ints TEST(viewTest, viewGenerate) { + { + auto v = view::generate([] { return 7; }, 1); + for (auto i : v) + EXPECT_EQ(7, i); + + static_assert(sizeof v.begin() <= sizeof(ptrdiff_t)); + } auto d = view::generate(Ints{1}, 2) | oel::to_dynarray(); ASSERT_EQ(2U, d.size()); @@ -261,6 +267,12 @@ TEST(viewTest, viewGenerate) d.assign(oel::view::generate(Ints{}, 0)); EXPECT_TRUE(d.empty()); + +#if OEL_STD_RANGES + using G = decltype( view::generate(Ints{}, 2) ); + static_assert(std::ranges::input_range); + static_assert(std::ranges::sized_range); +#endif } TEST(viewTest, viewMoveEndDifferentType) @@ -284,9 +296,6 @@ TEST(viewTest, viewMoveMutableEmptyAndSize) EXPECT_EQ(1U, v.size()); } -using IntGenIter = oel::iterator_t; -static_assert(std::input_iterator); - TEST(viewTest, chainWithStd) { auto f = [](int i) { return -i; }; diff --git a/util.h b/util.h index 9307e3e9..cd3c225f 100644 --- a/util.h +++ b/util.h @@ -153,34 +153,6 @@ namespace _detail OEL_THROW(std::out_of_range(what), what); } }; - - - - template< typename T, typename U, - bool = std::is_empty_v > - struct TightPair - { - T first; - U _sec; - - OEL_ALWAYS_INLINE constexpr const U & second() const { return _sec; } - OEL_ALWAYS_INLINE constexpr U & second() { return _sec; } - }; - - template< typename Type_unique_name_for_MSVC, typename Empty_type_MSVC_unique_name > - struct TightPair - : Empty_type_MSVC_unique_name - { - Type_unique_name_for_MSVC first; - - TightPair() = default; - constexpr TightPair(Type_unique_name_for_MSVC f, Empty_type_MSVC_unique_name s) - : Empty_type_MSVC_unique_name{s}, first{std::move(f)} - {} - - OEL_ALWAYS_INLINE constexpr const Empty_type_MSVC_unique_name & second() const { return *this; } - OEL_ALWAYS_INLINE constexpr Empty_type_MSVC_unique_name & second() { return *this; } - }; } } // namespace oel diff --git a/view/generate.h b/view/generate.h index 5a8176a2..bbc59681 100644 --- a/view/generate.h +++ b/view/generate.h @@ -6,7 +6,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#include "counted.h" +#include "subrange.h" #include "../auxi/assignable.h" /** @file @@ -15,27 +15,44 @@ namespace oel { +struct _defaultSentinel {}; + + template< typename Generator > -class generate_iterator +class _generateIterator { - typename _detail::AssignableWrap::Type mutable _g; + _detail::TightPairMutableFn< ptrdiff_t, typename _detail::AssignableWrap::Type > _m; public: using iterator_category = std::input_iterator_tag; + using difference_type = ptrdiff_t; using reference = decltype( std::declval()() ); using pointer = void; using value_type = std::remove_cv_t< std::remove_reference_t >; - using difference_type = ptrdiff_t; - constexpr explicit generate_iterator(Generator g) : _g{std::move(g)} {} + constexpr _generateIterator(Generator g, difference_type count) : _m{count, std::move(g)} {} constexpr reference operator*() const { - return static_cast(_g)(); + Generator & g = _m.func(); return g(); } - constexpr generate_iterator & operator++() OEL_ALWAYS_INLINE { return *this; } - constexpr void operator++(int) & OEL_ALWAYS_INLINE {} + constexpr _generateIterator & operator++() OEL_ALWAYS_INLINE { --_m.first; return *this; } + + constexpr void operator++(int) & OEL_ALWAYS_INLINE { --_m.first; } + + friend constexpr difference_type operator - + (_defaultSentinel, const _generateIterator & right) { return right._m.first; } + friend constexpr difference_type operator - + (const _generateIterator & left, _defaultSentinel) { return -left._m.first; } + + friend constexpr bool operator==(const _generateIterator & left, _defaultSentinel) { return 0 == left._m.first; } + + friend constexpr bool operator==(_defaultSentinel left, const _generateIterator & right) { return right == left; } + + friend constexpr bool operator!=(const _generateIterator & left, _defaultSentinel) { return 0 != left._m.first; } + + friend constexpr bool operator!=(_defaultSentinel, const _generateIterator & right) { return 0 != right._m.first; } }; @@ -43,9 +60,11 @@ namespace view { //! Returns a view that generates `count` elements by calling the given generator function /** -* Like `generate_n` in the Range-v3 library, but this is only for use within OE-Lib. */ -inline constexpr auto generate = - [](auto generator, ptrdiff_t count) { return counted(generate_iterator{std::move(generator)}, count); }; +* Almost same as `generate_n` in the Range-v3 library. */ +inline constexpr auto generate = [](auto generator, ptrdiff_t count) + { + return subrange(_generateIterator{std::move(generator), count}, _defaultSentinel{}); + }; } } // oel diff --git a/view/subrange.h b/view/subrange.h index 2cccab94..e20817e9 100644 --- a/view/subrange.h +++ b/view/subrange.h @@ -6,7 +6,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#include "../util.h" +#include "../util.h" // for as_unsigned +#include "../auxi/detail_tight_pair.h" /** @file */ diff --git a/view/transform_iterator.h b/view/transform_iterator.h index f2acfd05..391f77fe 100644 --- a/view/transform_iterator.h +++ b/view/transform_iterator.h @@ -6,39 +6,15 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#include "../util.h" // for TightPair #include "../auxi/assignable.h" +#include "../auxi/range_traits.h" +#include "../auxi/detail_tight_pair.h" /** @file */ namespace oel { -namespace _detail -{ - template< typename Func, typename Iter, - bool = std::is_invocable_v< const Func &, decltype(*std::declval()) > - > - struct TransformIterBase - { - using FnRef = const Func &; - - static constexpr auto canCallConst = true; - - TightPair< Iter, typename AssignableWrap::Type > m; - }; - - template< typename Func, typename Iter > - struct TransformIterBase - { - using FnRef = Func &; - - static constexpr auto canCallConst = false; - - TightPair< Iter, typename AssignableWrap::Type > mutable m; - }; -} - /** @brief Similar to boost::transform_iterator * @@ -49,18 +25,15 @@ namespace _detail * - Function objects (including lambda) can have non-const `operator()`, then merely std::input_iterator is modeled */ template< typename UnaryFunc, typename Iterator > class transform_iterator - : private _detail::TransformIterBase { - using _base = typename transform_iterator::TransformIterBase; - - using _base::m; + _detail::TightPairMutableFn< Iterator, typename _detail::AssignableWrap::Type > _m; static constexpr auto _isBidirectional = iter_is_bidirectional; public: using iterator_category = std::conditional_t< - std::is_copy_constructible_v and _base::canCallConst, + std::is_copy_constructible_v and std::is_invocable_v< const UnaryFunc &, decltype(*_m.first) >, std::conditional_t< _isBidirectional, std::bidirectional_iterator_tag, @@ -69,68 +42,68 @@ class transform_iterator std::input_iterator_tag >; using difference_type = iter_difference_t; - using reference = decltype( std::declval()(*m.first) ); + using reference = decltype( std::declval()(*_m.first) ); using pointer = void; using value_type = std::remove_cv_t< std::remove_reference_t >; transform_iterator() = default; - constexpr transform_iterator(UnaryFunc f, Iterator it) : _base{{std::move(it), std::move(f)}} {} + constexpr transform_iterator(UnaryFunc f, Iterator it) : _m{std::move(it), std::move(f)} {} - constexpr Iterator base() && { return std::move(m.first); } - constexpr Iterator base() const && { return m.first; } - constexpr const Iterator & base() const & noexcept OEL_ALWAYS_INLINE { return m.first; } + constexpr Iterator base() && { return std::move(_m.first); } + constexpr Iterator base() const && { return _m.first; } + constexpr const Iterator & base() const & noexcept OEL_ALWAYS_INLINE { return _m.first; } constexpr reference operator*() const { - typename _base::FnRef f = m.second(); - return f(*m.first); + UnaryFunc & f = _m.func(); + return f(*_m.first); } - constexpr transform_iterator & operator++() OEL_ALWAYS_INLINE { ++m.first; return *this; } + constexpr transform_iterator & operator++() OEL_ALWAYS_INLINE { ++_m.first; return *this; } //! Post-increment: return type is transform_iterator if iterator_category is-a forward_iterator_tag, else void constexpr auto operator++(int) & { if constexpr (std::is_same_v) { - ++m.first; + ++_m.first; } else { auto tmp = *this; - ++m.first; + ++_m.first; return tmp; } } constexpr transform_iterator & operator--() - OEL_REQUIRES(_isBidirectional) OEL_ALWAYS_INLINE { --m.first; return *this; } + OEL_REQUIRES(_isBidirectional) OEL_ALWAYS_INLINE { --_m.first; return *this; } constexpr transform_iterator operator--(int) & OEL_REQUIRES(_isBidirectional) { auto tmp = *this; - --m.first; + --_m.first; return tmp; } constexpr difference_type operator -(const transform_iterator & right) const - OEL_REQUIRES(std::sized_sentinel_for) { return m.first - right.m.first; } + OEL_REQUIRES(std::sized_sentinel_for) { return _m.first - right._m.first; } template< typename S > OEL_REQUIRES(std::sized_sentinel_for) friend constexpr difference_type operator - - (sentinel_wrapper left, const transform_iterator & right) { return left._s - right.m.first; } + (sentinel_wrapper left, const transform_iterator & right) { return left._s - right._m.first; } template< typename S > OEL_REQUIRES(std::sized_sentinel_for) friend constexpr difference_type operator - - (const transform_iterator & left, sentinel_wrapper right) { return left.m.first - right._s; } + (const transform_iterator & left, sentinel_wrapper right) { return left._m.first - right._s; } - constexpr bool operator==(const transform_iterator & right) const { return m.first == right.m.first; } + constexpr bool operator==(const transform_iterator & right) const { return _m.first == right._m.first; } // These are not hidden friends because MSC 2017 gives error C3615 - constexpr bool operator!=(const transform_iterator & right) const { return m.first != right.m.first; } + constexpr bool operator!=(const transform_iterator & right) const { return _m.first != right._m.first; } template< typename S > friend constexpr bool operator== - (const transform_iterator & left, sentinel_wrapper right) { return left.m.first == right._s; } + (const transform_iterator & left, sentinel_wrapper right) { return left._m.first == right._s; } template< typename S > friend constexpr bool operator== @@ -138,11 +111,11 @@ class transform_iterator template< typename S > friend constexpr bool operator!= - (const transform_iterator & left, sentinel_wrapper right) { return left.m.first != right._s; } + (const transform_iterator & left, sentinel_wrapper right) { return left._m.first != right._s; } template< typename S > friend constexpr bool operator!= - (sentinel_wrapper left, const transform_iterator & right) { return right.m.first != left._s; } + (sentinel_wrapper left, const transform_iterator & right) { return right._m.first != left._s; } }; #if __cpp_lib_concepts < 201907