Skip to content

Commit

Permalink
Merge pull request #42 from njoy/feature/std23-repeat
Browse files Browse the repository at this point in the history
Feature/std23 repeat
  • Loading branch information
whaeck authored Sep 2, 2024
2 parents e0a48b7 + dfcf33f commit 194d3b7
Show file tree
Hide file tree
Showing 5 changed files with 367 additions and 0 deletions.
24 changes: 24 additions & 0 deletions src/tools/std23/test/usecase.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ class TestCase {
return this->all() | std23::views::stride( 2 )
| std23::views::chunk( 2 );
}

auto repeat_bound() const {

using namespace njoy::tools;
return std23::views::repeat( 5, 2 );
}

auto repeat_unbound() const {

using namespace njoy::tools;
return std23::views::repeat( 4.0 );
}
};

SCENARIO( "use case" ) {
Expand Down Expand Up @@ -187,4 +199,16 @@ SCENARIO( "use case" ) {
CHECK( std20::random_access_range< StrideChunk > );
CHECK( std20::sized_range< StrideChunk > );

using RepeatBound = decltype( test.repeat_bound() );
CHECK( std20::view< RepeatBound > );
CHECK( std20::range< RepeatBound > );
CHECK( std20::random_access_range< RepeatBound > );
CHECK( std20::sized_range< RepeatBound > );

using RepeatUnbound = decltype( test.repeat_unbound() );
CHECK( std20::view< RepeatUnbound > );
CHECK( std20::range< RepeatUnbound > );
CHECK( std20::random_access_range< RepeatUnbound > );
CHECK( !std20::sized_range< RepeatUnbound > );

} // SCENARIO
1 change: 1 addition & 0 deletions src/tools/std23/views.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
#include "tools/std23/views/chunk_by.hpp"
#include "tools/std23/views/stride.hpp"
#include "tools/std23/views/zip.hpp"
#include "tools/std23/views/repeat.hpp"
234 changes: 234 additions & 0 deletions src/tools/std23/views/repeat.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
#ifndef NJOY_TOOLS_STD23_RANGES_VIEWS_REPEAT
#define NJOY_TOOLS_STD23_RANGES_VIEWS_REPEAT

#include "tools/std20/detail/views/range_adaptors.hpp"
#include "tools/std20/iterator/unreachable.hpp"
#include "tools/std20/views/interface.hpp"
#include "tools/std20/views/all.hpp"
#include "tools/std20/views/subrange.hpp"
#include "tools/std20/views/take.hpp"
#include "tools/std20/detail/views/semiregular_box.hpp"
#include "tools/std20/detail/iterator/concepts.hpp"
#include "tools/std20/views/iota.hpp"

namespace njoy {
namespace tools {
namespace std23 {
inline namespace ranges {

template <typename Type, typename = void>
struct repeat_view_iterator_difference {
using type = std20::detail::iota_diff_t<Type>;
};

template <typename Type>
struct repeat_view_iterator_difference<Type, typename std::enable_if<std20::detail::is_signed_integer_like<Type>>::type>
{
using type = Type;
};

template <typename Type>
using repeat_view_iterator_difference_t = typename repeat_view_iterator_difference<Type>::type;

/**
* @brief A range factory that generates a sequence of elements by repeatedly
* producing the same value. Can be either bounded or unbounded (infinite).
*
* See https://en.cppreference.com/w/cpp/ranges/repeat_view
*/

template < typename Type, typename Bound = std20::unreachable_sentinel_t >
struct repeat_view : public std20::ranges::view_interface< repeat_view< Type, Bound > > {
// The Type must be a non-const object
static_assert( std::is_object_v<Type> && std20::same_as< Type, std::remove_cv_t< Type > > );

// The Bound has to be integer like of unbound
static_assert( ( std20::detail::is_signed_integer_like< Bound > ||
( std20::detail::is_integer_like< Bound > && std20::weakly_incrementable< Bound > ) ) ||
( std20::same_as< Bound, std20::unreachable_sentinel_t > ) );

private:

std20::detail::semiregular_box< Type > value_;
Bound bound_ = Bound();

struct iterator{
friend class repeat_view;
private:
using IndexT = std::conditional_t<std20::same_as<Bound, std20::unreachable_sentinel_t>, ptrdiff_t, Bound>;
const Type* ivalue_ = nullptr;
IndexT current_ = IndexT();

public:

constexpr explicit iterator(const Type* value, IndexT bound_sentinel = IndexT())
: ivalue_(value), current_(bound_sentinel) {}

using iterator_concept = std20::random_access_iterator_tag;
using iterator_category = std20::random_access_iterator_tag;
using value_type = Type;
using difference_type = repeat_view_iterator_difference_t<IndexT>;

iterator() = default;

constexpr const Type& operator*() const noexcept { return *ivalue_; }

constexpr iterator& operator++() {
++current_;
return *this;
}

constexpr iterator operator++(int) {
auto tmp = *this;
++*this;
return tmp;
}

constexpr iterator& operator--() {
--current_;
return *this;
}

constexpr iterator operator--(int) {
auto tmp = *this;
--*this;
return tmp;
}

constexpr iterator& operator+=(difference_type n) {
current_ += n;
return *this;
}

constexpr iterator& operator-=(difference_type n) {
current_ -= n;
return *this;
}

constexpr const Type& operator[](difference_type n) const noexcept { return *(*this + n); }

friend constexpr bool operator==(const iterator& left, const iterator& right) {
return left.current_ == right.current_;
}

friend constexpr bool operator!=(const iterator& left, const iterator& right) {
return !(left.current_ == right.current_);
}

friend constexpr auto operator> (const iterator& left, const iterator& right) {
return left.current_ > right.current_;
}
friend constexpr auto operator< (const iterator& left, const iterator& right) {
return right.current_ > left.current_;
}

friend constexpr auto operator>= (const iterator& left, const iterator& right) {
return left.current_ >= right.current_;
}
friend constexpr auto operator<= (const iterator& left, const iterator& right) {
return left.current_ <= right.current_;
}


friend constexpr iterator operator+(iterator i, difference_type n) {
i += n;
return i;
}

friend constexpr iterator operator+(difference_type n, iterator i) {
i += n;
return i;
}

friend constexpr iterator operator-(iterator i, difference_type n) {
i -= n;
return i;
}

friend constexpr difference_type operator-(const iterator& left, const iterator& right) {
return static_cast<difference_type>(left.current_) - static_cast<difference_type>(right.current_);
}


};

public:

repeat_view() = default;

constexpr explicit repeat_view( const Type& value, Bound bound_sentinel = Bound())
: value_(std::in_place, value), bound_(bound_sentinel) {
}

constexpr explicit repeat_view(Type&& value, Bound bound_sentinel = Bound())
: value_(std::in_place, std::move(value)), bound_(bound_sentinel) {
}

template <class... _TpArgs, class... _BoundArgs,
std::enable_if_t<std20::constructible_from<Type, _TpArgs...> && std20::constructible_from<Bound, _BoundArgs...>, bool> = true>
constexpr explicit repeat_view( std::piecewise_construct_t, std::tuple<_TpArgs...> __value_args,
std::tuple<_BoundArgs...> __bound_args = std::tuple<>{})
: value_(std::in_place, std::make_from_tuple<Type>(std::move(__value_args))),
bound_(std::make_from_tuple<Bound>(std::move(__bound_args))) {
}

constexpr iterator begin() const { return iterator(std::addressof(*value_)); }

template<typename T=Bound, std::enable_if_t<!std20::same_as<T, std20::unreachable_sentinel_t>, bool> = true>
constexpr iterator end() const
{
return iterator(std::addressof(*value_), bound_);
}

template<typename T=Bound, std::enable_if_t<std20::same_as<T, std20::unreachable_sentinel_t>, bool> = true>
constexpr std20::unreachable_sentinel_t end() const noexcept
{
return std20::unreachable_sentinel;
}

template<typename T=Bound, std::enable_if_t<!std20::same_as<T, std20::unreachable_sentinel_t>, bool> = true>
constexpr auto size() const
{
return std::make_unsigned_t<decltype(bound_)>(bound_);
}
};

template < typename Type >
repeat_view( Type&&, std20::ranges::range_difference_t< Type > ) -> repeat_view< std20::ranges::all_view< Type > >;

} // namespace ranges
} // namespace std23

namespace std23 {
inline namespace ranges {
namespace detail {
struct repeat_view_fn {

template < typename E, typename F >
constexpr auto operator()( E&& e, F&& f ) const
-> decltype( repeat_view{ std::forward< E >( e ), std::forward< F >( f ) } )
{
return repeat_view{ std::forward< E >( e ), std::forward< F >( f ) };
}

template < typename E >
constexpr auto operator()( E&& e ) const
-> decltype( repeat_view{ std::forward< E >( e ) } )
{
return repeat_view{ std::forward< E >( e ) };
}

};

}

namespace views {
NANO_INLINE_VAR(ranges::detail::repeat_view_fn, repeat)
}
} // namespace ranges
} // namespace std23

} // namespace tools
} // namespace njoy

#endif
1 change: 1 addition & 0 deletions src/tools/std23/views/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ add_cpp_test( std23.views.chunk chunk.test.cpp )
add_cpp_test( std23.views.chunk_by chunk_by.test.cpp )
add_cpp_test( std23.views.stride stride.test.cpp )
add_cpp_test( std23.views.zip zip.test.cpp )
add_cpp_test( std23.views.repeat repeat.test.cpp )
107 changes: 107 additions & 0 deletions src/tools/std23/views/test/repeat.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// include Catch2
#include <catch2/catch_test_macros.hpp>

// what we are testing
#include "tools/std23/views.hpp"

// other includes
#include <forward_list>
#include <list>
#include <vector>
#include "tools/std20/algorithm.hpp"
#include <string_view>
#include "tools/std20/iterator/unreachable.hpp"
// convenience typedefs
using namespace njoy::tools;

SCENARIO( "repeat_view" ) {
// Use a single bound limit for all bound tests
int boundLimit = 4;

GIVEN( "Test repeat integer" ) {
int repeated = 9;
WHEN("Bound repeater"){
auto RepeatedInt = std23::views::repeat(repeated, boundLimit);
THEN("Check all ints in bound limit"){
CHECK(RepeatedInt.size() == boundLimit);
for (int i : RepeatedInt){
CHECK(i == repeated);
}
} //THEN
} //WHEN
WHEN("Unbound repeater"){
auto RepeatedInt = std23::views::repeat(repeated);
THEN("Check that end is unbound and int is repeated within 2x listed limit"){
CHECK( std20::same_as<std::remove_cv_t<decltype(std20::unreachable_sentinel)>,
decltype(RepeatedInt.end())>);
int UnboundLimit = 0;
for (int i : RepeatedInt){
UnboundLimit += 1;
CHECK(i == repeated);
if (UnboundLimit > 2*boundLimit){
break;
}
}
} //THEN

} //WHEN
} //GIVEN
GIVEN( "Test repeat float" ) {
float repeated = 9.0;
WHEN("Bound repeater"){
auto RepeatedInt = std23::views::repeat(repeated, boundLimit);
THEN("Check all ints in bound limit"){
CHECK(RepeatedInt.size() == boundLimit);
for (float i : RepeatedInt){
CHECK(i == repeated);
}
} //THEN
} //WHEN
WHEN("Unbound repeater"){
auto RepeatedInt = std23::views::repeat(repeated);
THEN("Check that end is unbound and int is repeated within 2x listed limit"){
CHECK( std20::same_as<std::remove_cv_t<decltype(std20::unreachable_sentinel)>,
decltype(RepeatedInt.end())>);
int UnboundLimit = 0;
for (float i : RepeatedInt){
UnboundLimit += 1;
CHECK(i == repeated);
if (UnboundLimit > 2*boundLimit){
break;
}
}
} //THEN

} //WHEN
} //GIVEN
GIVEN( "Test repeat string views" ) {
using namespace std::literals::string_view_literals;
WHEN("Bound repeater"){
auto RepeatedInt = std23::views::repeat("testMe"sv, boundLimit);
THEN("Check all ints in bound limit"){
CHECK(RepeatedInt.size() == boundLimit);
for (auto i : RepeatedInt){
CHECK(i == "testMe");
}
} //THEN
} //WHEN
WHEN("Unbound repeater"){
auto RepeatedInt = std23::views::repeat("testMe"sv);
THEN("Check that end is unbound and int is repeated within 2x listed limit"){
CHECK( std20::same_as<std::remove_cv_t<decltype(std20::unreachable_sentinel)>,
decltype(RepeatedInt.end())>);
int UnboundLimit = 0;
for (auto i : RepeatedInt){
UnboundLimit += 1;
CHECK(i == "testMe");
if (UnboundLimit > 2*boundLimit){
break;
}
}
} //THEN

} //WHEN
} //GIVEN

} //SCENARIO

0 comments on commit 194d3b7

Please sign in to comment.