Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/std23 repeat #42

Merged
merged 7 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
235 changes: 235 additions & 0 deletions src/tools/std23/views/repeat.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
#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 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 std20 {
inline namespace ranges {

template <typename Type>
inline constexpr bool enable_borrowed_range<std23::ranges::repeat_view<Type>> = forward_range<Type> && enable_borrowed_range<Type>;

} // namespace ranges
} // namespace std20

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);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to see tests for the concepts that the view needs to satisfy:

        CHECK( std20::ranges::viewable_range< Range > );

        CHECK( std20::ranges::range< Range > );
        CHECK( std20::ranges::view< Range > );
        CHECK( std20::ranges::sized_range< Range > );
        CHECK( std20::ranges::forward_range< Range > );
        CHECK( std20::ranges::bidirectional_range< Range > );
        CHECK( std20::ranges::random_access_range< Range > );
        CHECK( ! std20::ranges::contiguous_range< Range > );
        CHECK( std20::ranges::common_range< Range > );

and tests for the remainder of the view interface. like this:

        CHECK( 5 == chunk.size() );

        CHECK( false == chunk.empty() );
        CHECK( true == bool( chunk ) );

        CHECK( std20::ranges::equal( equal, chunk ) );

        CHECK( equal[0] == chunk.front() );
        CHECK( equal[4] == chunk.back() );

        CHECK( 1 == chunk[0] );
        CHECK( 3 == chunk[1] );
        CHECK( 5 == chunk[2] );
        CHECK( 7 == chunk[3] );
        CHECK( 9 == chunk[4] );

All other views have tests like these to ensure the views function appropriately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Random access issue is fixed with inclusion of >= and <= operators. Pushing this shortly.

Size Checks are already being done with bound limits with lines like
CHECK(RepeatedInt.size() == boundLimit);
and with unbound limits with
CHECK( std20::same_as<std::remove_cv_t<decltype(std20::unreachable_sentinel)>, decltype(RepeatedInt.end())>);

Adding to use case test an unbound example as well. Pushing shortly:

} //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