Skip to content

Commit

Permalink
Support ranges with only const_iterator types (e.g. std::set)
Browse files Browse the repository at this point in the history
  • Loading branch information
rollbear committed Dec 23, 2024
1 parent d6be4b9 commit bc40795
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 23 deletions.
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
* Make strong::range work with range types that only has
const_iterator types (e.g. std::set). Thanks David Feltell for
reporting the problem.

v15 2024-07-07

* CMake package is now ARCH_INDEPENDENT, as it should be when it's
Expand Down
103 changes: 80 additions & 23 deletions include/strong_type/range.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,64 +39,59 @@ namespace internal {
struct not_an_iterator {};


template <typename R, typename C, typename ... Ts>
auto member_function_ret_type(R (C::*)(Ts...)) -> R;
template <typename R, typename C, typename ... Ts>
auto const_member_function_ret_type(R (C::*)(Ts...) const) -> R;

#if defined(STRONG_TYPE_HAS_RANGES) && (!defined(__GLIBCXX__) || (__GLIBCXX__ >= 20230601L))
#if defined(STRONG_TYPE_HAS_RANGES) && (!defined(__GLIBCXX__) || (__GLIBCXX__ >= 20230528L))
template <typename T>
auto begin_type(T* t) -> decltype(std::ranges::begin(*t));
#else
template <typename T>
auto begin_type(T*) -> decltype(member_function_ret_type(&T::begin));
auto begin_type(T* t) -> decltype(t->begin());
#endif

auto begin_type(...) -> not_an_iterator;

#if defined(STRONG_TYPE_HAS_RANGES) && (!defined(__GLIBCXX__) || (__GLIBCXX__ >= 20230601L))
#if defined(STRONG_TYPE_HAS_RANGES) && (!defined(__GLIBCXX__) || (__GLIBCXX__ >= 20230528L))
template <typename T>
auto const_begin_type(const T* t) -> decltype(std::ranges::begin(*t));
#else
template <typename T>
auto const_begin_type(const T* t) -> decltype(const_member_function_ret_type(&T::begin));
auto const_begin_type(const T* t) -> decltype(t->begin());
#endif
auto const_begin_type(...) -> not_an_iterator;

#if defined(STRONG_TYPE_HAS_RANGES) && (!defined(__GLIBCXX__) || (__GLIBCXX__ >= 20230601L))
#if defined(STRONG_TYPE_HAS_RANGES) && (!defined(__GLIBCXX__) || (__GLIBCXX__ >= 20230528L))
template <typename T>
auto end_type(T* t) -> decltype(std::ranges::end(*t));
#else
template <typename T>
auto end_type(T*) -> decltype(member_function_ret_type(&T::end));
auto end_type(T* t) -> decltype(t->end());
#endif
auto end_type(...) -> not_an_iterator;

#if defined(STRONG_TYPE_HAS_RANGES) && (!defined(__GLIBCXX__) || (__GLIBCXX__ >= 20230601L))
#if defined(STRONG_TYPE_HAS_RANGES) && (!defined(__GLIBCXX__) || (__GLIBCXX__ >= 20230528L))
template <typename T>
auto const_end_type(const T* t) -> decltype(std::ranges::end(*t));
#else
template <typename T>
auto const_end_type(T*) -> decltype(const_member_function_ret_type(&T::end));
auto const_end_type(const T* t) -> decltype(t->end());
#endif

auto const_end_type(...) -> not_an_iterator;

#if defined(STRONG_TYPE_HAS_RANGES) && (!defined(__GLIBCXX__) || (__GLIBCXX__ >= 20230601L))
#if defined(STRONG_TYPE_HAS_RANGES) && (!defined(__GLIBCXX__) || (__GLIBCXX__ >= 20230528L))
template <typename T>
auto cbegin_type(const T* t) -> decltype(std::ranges::cbegin(*t));
#else
template <typename T>
auto cbegin_type(T*) -> decltype(const_member_function_ret_type(&T::cbegin));
auto cbegin_type(const T* t) -> decltype(t->cbegin());
#endif
auto cbegin_type(...) -> not_an_iterator;

#if defined(STRONG_TYPE_HAS_RANGES) && (!defined(__GLIBCXX__) || (__GLIBCXX__ >= 20230601L))
#if defined(STRONG_TYPE_HAS_RANGES) && (!defined(__GLIBCXX__) || (__GLIBCXX__ >= 20230528L))
template <typename T>
auto cend_type(const T* t) -> decltype(std::ranges::cend(*t));
#else
template <typename T>
auto cend_type(T*) -> decltype(const_member_function_ret_type(&T::cend));
auto cend_type(const T* t) -> decltype(t->cend());
#endif
auto cend_type(...) -> not_an_iterator;

Expand Down Expand Up @@ -155,16 +150,78 @@ class range::modifier<

};

template <typename T, typename Tag, typename ... M, typename iterator>
template <typename T, typename Tag, typename ... M, typename r_iterator>
class range::modifier<
type<T, Tag, M...>,
iterator, iterator,
iterator, iterator,
iterator, iterator>
r_iterator, r_iterator,
r_iterator, r_iterator,
r_iterator, r_iterator>
{
static_assert(impl::always_false<T>, "the underlying type is lying about its iterator type");
};
using type = ::strong::type<T, Tag, M...>;
static constexpr bool random_access = internal::is_random_access(typename internal::iterator_traits<r_iterator>::iterator_category{});
public:
using const_iterator = std::conditional_t<random_access,
::strong::type<r_iterator, Tag, strong::iterator, strong::default_constructible, strong::equality_with<r_iterator>, strong::ordered_with<r_iterator>>,
::strong::type<r_iterator, Tag, strong::iterator, strong::default_constructible, strong::equality_with<r_iterator>>
>;

STRONG_NODISCARD
constexpr
const_iterator
begin()
const
noexcept(noexcept(STRONG_TYPE_BEGIN(std::declval<const T&>())))
{
auto& self = static_cast<const type&>(*this);
return const_iterator{STRONG_TYPE_BEGIN(value_of(self))};
}

STRONG_NODISCARD
constexpr
const_iterator
end()
const
noexcept(noexcept(STRONG_TYPE_END(std::declval<const T&>())))
{
auto& self = static_cast<const type&>(*this);
return const_iterator{STRONG_TYPE_END(value_of(self))};
}

STRONG_NODISCARD
constexpr
const_iterator
cbegin()
const
noexcept(noexcept(STRONG_TYPE_CBEGIN(std::declval<const T&>())))
{
auto& self = static_cast<const type&>(*this);
return const_iterator{STRONG_TYPE_CBEGIN(value_of(self))};
}

STRONG_NODISCARD
constexpr
const_iterator
cend()
const
noexcept(noexcept(STRONG_TYPE_CEND(std::declval<const T&>())))
{
auto& self = static_cast<const type&>(*this);
return const_iterator{STRONG_TYPE_CEND(value_of(self))};
}

template <typename TT = const T&>
STRONG_NODISCARD
constexpr
decltype(std::declval<TT>().size())
size()
const
noexcept(noexcept(std::declval<TT>().size()))
{
auto& self = static_cast<const type&>(*this);
return value_of(self).size();
}

};
template <typename T, typename Tag, typename ... M, typename r_iterator, typename r_const_iterator>
class range::modifier<
type<T, Tag, M...>,
Expand Down
21 changes: 21 additions & 0 deletions test/test_range.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <forward_list>
#include <array>
#include <sstream>
#include <set>

namespace {
template <typename T>
Expand Down Expand Up @@ -118,6 +119,26 @@ TEST_CASE("constexpr size")
STATIC_REQUIRE(a.size() == 4);
}

TEST_CASE("a range with only const_iterators is still a range")
{
using is = strong::type
<
std::set<int>,
struct is_,
strong::range,
strong::default_constructible
>;
is set{1,3,5,7,9};
REQUIRE(set.size() == 5);
int v = 1;
for (auto x : set)
{
REQUIRE(x == v);
v+= 2;
}
REQUIRE(v == 11);
}

#if defined(STRONG_TYPE_HAS_RANGES)
#if (!defined(_LIBCPP_VERSION)) || (_LIBCPP_VERSION >= 16000)
TEST_CASE("a range with a sentinel can be iterated")
Expand Down

0 comments on commit bc40795

Please sign in to comment.