Skip to content

Commit 3d87b19

Browse files
committed
More work on range semantics
1 parent 7a83714 commit 3d87b19

File tree

4 files changed

+176
-25
lines changed

4 files changed

+176
-25
lines changed

lib/inc/sys_string/impl/unicode/algorithms.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ namespace sysstr
2828
{
2929
static constexpr auto max_output_length = util::unicode::mapper::max_mapped_length;
3030

31-
template<class OutIt>
31+
template<std::output_iterator<utf_char_of<OutEnc>> OutIt>
3232
auto operator()(char32_t c, OutIt dest) const noexcept(noexcept(*dest++ = utf_char_of<OutEnc>())) -> OutIt
3333
{
3434
return util::unicode::mapper::case_fold.map_char<OutEnc>(c, dest);
@@ -40,11 +40,11 @@ namespace sysstr
4040
class sigma_tolower
4141
{
4242
public:
43-
template<class Range>
43+
template<ranges::reversible_range Range>
4444
auto operator()(const Range & range,
4545
std::ranges::iterator_t<Range> where) const noexcept -> char32_t
4646
{
47-
auto reversed = range.reverse(where);
47+
auto reversed = ranges::make_reverse_iterator(range, where);
4848
if (any_non_cased_then_cased(reversed, std::rend(range)))
4949
{
5050
++where;
@@ -75,7 +75,7 @@ namespace sysstr
7575
template<utf_encoding OutEnc>
7676
struct tolower
7777
{
78-
template<class Range, std::output_iterator<utf_char_of<OutEnc>> OutIt>
78+
template<ranges::reversible_range Range, std::output_iterator<utf_char_of<OutEnc>> OutIt>
7979
inline auto operator()(const Range & range, OutIt dest) noexcept(noexcept(*dest++ = utf_char_of<OutEnc>())) -> OutIt
8080
{
8181
using namespace util::unicode;
@@ -106,7 +106,7 @@ namespace sysstr
106106
template<utf_encoding OutEnc>
107107
struct toupper
108108
{
109-
template<class Range, std::output_iterator<utf_char_of<OutEnc>> OutIt>
109+
template<ranges::reversible_range Range, std::output_iterator<utf_char_of<OutEnc>> OutIt>
110110
inline auto operator()(const Range & range, OutIt dest) noexcept(noexcept(*dest++ = utf_char_of<OutEnc>())) -> OutIt
111111
{
112112
using namespace util::unicode;

lib/inc/sys_string/impl/util/iter_util.h

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,10 @@
1111
#include <sys_string/config.h>
1212

1313
#include <iterator>
14+
#include <ranges>
1415

15-
namespace sysstr::util
16+
namespace sysstr::ranges //non-standard extensions to std::ranges
1617
{
17-
enum class iter_direction : bool
18-
{
19-
backward = false,
20-
forward = true
21-
};
22-
constexpr iter_direction reverse(iter_direction dir) noexcept
23-
{ return iter_direction(!bool(dir)); }
24-
25-
2618
template<class T, class Base>
2719
concept reverse_iterator_of =
2820
std::input_iterator<T> &&
@@ -33,6 +25,74 @@ namespace sysstr::util
3325
{ t.base() } -> std::same_as<Base>;
3426
};
3527

28+
template<std::ranges::range R>
29+
using reverse_iterator_t = decltype(std::ranges::rbegin(std::declval<R&>()));
30+
template<std::ranges::range R>
31+
using const_reverse_iterator_t = decltype(std::ranges::crbegin(std::declval<R&>()));
32+
template<std::ranges::range R>
33+
using reverse_sentinel_t = decltype(std::ranges::rend(std::declval<R&>()));
34+
template<std::ranges::range R>
35+
using const_reverse_sentinel_t = decltype(std::ranges::crend(std::declval<R&>()));
36+
37+
template<class R>
38+
concept reverse_traversable_range = std::ranges::range<R> && requires(R & r) {
39+
std::ranges::rbegin(r);
40+
std::ranges::rend(r);
41+
std::ranges::crbegin(r);
42+
std::ranges::crend(r);
43+
};
44+
45+
template<class R>
46+
concept common_reverse_traversable_range = reverse_traversable_range<R> &&
47+
std::ranges::common_range<R> &&
48+
std::same_as<reverse_iterator_t<R>, reverse_sentinel_t<R>>;
49+
50+
template<class R>
51+
concept standard_reverse_traversable_range = common_reverse_traversable_range<R> &&
52+
reverse_iterator_of<reverse_iterator_t<R>, std::ranges::iterator_t<R>>;
53+
54+
55+
template<class R>
56+
concept custom_reverse_traversable_range = reverse_traversable_range<R> && requires(R & r)
57+
{
58+
{ r.reverse(std::declval<std::ranges::iterator_t<R>>()) } -> std::same_as<reverse_iterator_t<R>>;
59+
{ r.reverse(std::declval<reverse_iterator_t<R>>()) } -> std::same_as<std::ranges::iterator_t<R>>;
60+
};
61+
62+
63+
template<standard_reverse_traversable_range R>
64+
decltype(auto) make_reverse_iterator(R && /*range*/, std::ranges::iterator_t<std::remove_reference_t<R>> it)
65+
{ return reverse_iterator_t<R>(it); }
66+
67+
template<standard_reverse_traversable_range R>
68+
decltype(auto) make_reverse_iterator(R && /*range*/, reverse_iterator_t<std::remove_reference_t<R>> it)
69+
{ return it.base(); }
70+
71+
template<custom_reverse_traversable_range R>
72+
decltype(auto) make_reverse_iterator(R && range, std::ranges::iterator_t<std::remove_reference_t<R>> it)
73+
{ return range.reverse(it); }
74+
75+
template<custom_reverse_traversable_range R>
76+
decltype(auto) make_reverse_iterator(R && range, reverse_iterator_t<std::remove_reference_t<R>> it)
77+
{ return range.reverse(it); }
78+
79+
template<class R>
80+
concept reversible_range = reverse_traversable_range<R> && requires(R & r) {
81+
make_reverse_iterator(r, std::declval<std::ranges::iterator_t<std::remove_reference_t<R>>>());
82+
make_reverse_iterator(r, std::declval<reverse_iterator_t<std::remove_reference_t<R>>>());
83+
};
84+
}
85+
86+
namespace sysstr::util
87+
{
88+
enum class iter_direction : bool
89+
{
90+
backward = false,
91+
forward = true
92+
};
93+
constexpr iter_direction reverse(iter_direction dir) noexcept
94+
{ return iter_direction(!bool(dir)); }
95+
3696

3797
template<class T>
3898
concept indexable = requires(T & t, decltype(std::size(t)) n)
@@ -43,6 +103,8 @@ namespace sysstr::util
43103
template<class Container, iter_direction Direction>
44104
class index_iterator
45105
{
106+
//We cannot declare ourselves as template<indexable Container> becuase iterators are often declared as nested
107+
//types and within a type the Container is incomplete. Sigh.
46108
static_assert(indexable<Container>);
47109

48110
friend index_iterator<Container, reverse(Direction)>;
@@ -53,7 +115,7 @@ namespace sysstr::util
53115
using size_type = decltype(std::size(std::declval<Container>()));
54116
using reference = decltype(std::declval<Container>()[0]);
55117
using difference_type = std::make_signed_t<size_type>;
56-
using value_type = std::remove_cv_t<std::remove_reference_t<reference>>;
118+
using value_type = std::remove_cvref_t<reference>;
57119

58120
static constexpr bool is_forward = (Direction == iter_direction::forward);
59121
static constexpr bool is_reverse = !is_forward;

lib/inc/sys_string/utf_view.h

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ namespace sysstr
3232
} && (T::enable_view || !T::enable_view) && (T::enable_borrowed_range || !T::enable_borrowed_range);
3333

3434
template<utf_encoding Enc, class Container, utf_access_traits_t<Container> Traits = utf_access_traits<Container>>
35+
requires(std::ranges::input_range<std::remove_reference_t<decltype(Traits::access(std::declval<typename Traits::stored_reference>()))>>)
3536
class utf_view;
3637

3738
namespace util
@@ -77,7 +78,7 @@ namespace sysstr
7778
}
7879

7980
template<std::input_iterator OtherIt, std::sentinel_for<OtherIt> OtherEndIt>
80-
requires(Direction == iter_direction::forward && reverse_iterator_of<OtherIt, It>)
81+
requires(Direction == iter_direction::forward && ranges::reverse_iterator_of<OtherIt, It>)
8182
utf_iterator(const utf_iterator<OutputEnc, OtherIt, OtherEndIt, iter_direction::backward> & rev,
8283
EndIt last):
8384
m_current(rev.storage_next().base()),
@@ -91,7 +92,7 @@ namespace sysstr
9192
}
9293

9394
template<std::input_iterator OtherIt, std::sentinel_for<OtherIt> OtherEndIt>
94-
requires(Direction == iter_direction::backward && reverse_iterator_of<It, OtherIt>)
95+
requires(Direction == iter_direction::backward && ranges::reverse_iterator_of<It, OtherIt>)
9596
utf_iterator(const utf_iterator<OutputEnc, OtherIt, OtherEndIt, iter_direction::forward> & fwd,
9697
EndIt last):
9798
m_current(fwd.storage_next()),
@@ -221,14 +222,14 @@ namespace sysstr
221222
}
222223

223224
template<std::input_iterator OtherIt, std::sentinel_for<OtherIt> OtherEndIt>
224-
requires(Direction == iter_direction::forward && reverse_iterator_of<OtherIt, It>)
225+
requires(Direction == iter_direction::forward && ranges::reverse_iterator_of<OtherIt, It>)
225226
utf_iterator(const utf_iterator<utf32, OtherIt, OtherEndIt, iter_direction::backward> & rev,
226227
EndIt last):
227228
utf_iterator(rev.storage_current().base(), last)
228229
{}
229230

230231
template<std::input_iterator OtherIt, std::sentinel_for<OtherIt> OtherEndIt>
231-
requires(Direction == iter_direction::backward && reverse_iterator_of<It, OtherIt>)
232+
requires(Direction == iter_direction::backward && ranges::reverse_iterator_of<It, OtherIt>)
232233
utf_iterator(const utf_iterator<utf32, OtherIt, OtherEndIt, iter_direction::forward> & fwd,
233234
EndIt last):
234235
utf_iterator(It(fwd.storage_current()), last)
@@ -289,29 +290,76 @@ namespace sysstr
289290
};
290291
}
291292

292-
template<std::ranges::random_access_range Container>
293+
template<std::ranges::input_range Container>
293294
struct utf_access_traits<Container>
294295
{
295-
using stored_reference = std::add_pointer_t<std::remove_reference_t<Container>>;
296+
using stored_reference = std::add_pointer_t<std::add_const_t<std::remove_reference_t<Container>>>;
296297

297298
static constexpr bool enable_view = true;
298299
static constexpr bool enable_borrowed_range = true;
299300

300-
static decltype(auto) adapt(std::add_lvalue_reference_t<Container> src) noexcept
301+
static decltype(auto) adapt(const std::add_const_t<std::remove_reference_t<Container>> & src) noexcept
301302
{ return &src; }
302-
static std::add_lvalue_reference_t<Container> access(stored_reference ptr) noexcept
303+
static std::add_const_t<std::remove_reference_t<Container>> & access(stored_reference ptr) noexcept
303304
{ return *ptr; }
304305
};
305306

306-
307307
template<utf_encoding Enc, class Container, utf_access_traits_t<Container> Traits>
308+
requires(std::ranges::input_range<std::remove_reference_t<decltype(Traits::access(std::declval<typename Traits::stored_reference>()))>>)
308309
class utf_view
309310
{
310311
private:
311312
using stored_reference = typename Traits::stored_reference;
312313

313314
static constexpr auto source_encoding = utf_encoding_of<std::ranges::range_value_t<decltype(Traits::access(std::declval<stored_reference>()))>>;
314315

316+
using access_iterator = decltype(std::begin(Traits::access(std::declval<stored_reference>())));
317+
using access_sentinel = decltype(std::end(Traits::access(std::declval<stored_reference>())));
318+
public:
319+
using iterator = util::utf_iterator<Enc, access_iterator, access_sentinel, util::iter_direction::forward>;
320+
using const_iterator = iterator;
321+
322+
using value_type = typename iterator::value_type;
323+
using reference = typename iterator::reference;
324+
using const_reference = reference;
325+
using pointer = typename iterator::pointer;
326+
using const_pointer = pointer;
327+
328+
public:
329+
utf_view(const Container & src) noexcept(noexcept(stored_reference(Traits::adapt(src)))) :
330+
m_ref(Traits::adapt(src))
331+
{}
332+
SYS_STRING_FORCE_INLINE iterator begin() const
333+
{ return iterator(std::begin(Traits::access(this->m_ref)), std::end(Traits::access(this->m_ref))); }
334+
SYS_STRING_FORCE_INLINE std::default_sentinel_t end() const
335+
{ return std::default_sentinel; }
336+
SYS_STRING_FORCE_INLINE const_iterator cbegin() const
337+
{ return begin(); }
338+
SYS_STRING_FORCE_INLINE std::default_sentinel_t cend() const
339+
{ return end(); }
340+
341+
342+
template<class Func>
343+
decltype(auto) each(Func func) const
344+
{
345+
return utf_converter<source_encoding, Enc>::for_each_converted(Traits::access(this->m_ref), func);
346+
}
347+
348+
private:
349+
stored_reference m_ref;
350+
};
351+
352+
353+
template<utf_encoding Enc, class Container, utf_access_traits_t<Container> Traits>
354+
requires(std::ranges::input_range<std::remove_reference_t<decltype(Traits::access(std::declval<typename Traits::stored_reference>()))>> &&
355+
ranges::reverse_traversable_range<std::remove_reference_t<decltype(Traits::access(std::declval<typename Traits::stored_reference>()))>>)
356+
class utf_view<Enc, Container, Traits>
357+
{
358+
private:
359+
using stored_reference = typename Traits::stored_reference;
360+
361+
static constexpr auto source_encoding = utf_encoding_of<std::ranges::range_value_t<decltype(Traits::access(std::declval<stored_reference>()))>>;
362+
315363
using access_iterator = decltype(std::begin(Traits::access(std::declval<stored_reference>())));
316364
using access_sentinel = decltype(std::end(Traits::access(std::declval<stored_reference>())));
317365
using access_reverse_iterator = decltype(std::rbegin(Traits::access(std::declval<stored_reference>())));
@@ -368,6 +416,33 @@ namespace sysstr
368416
private:
369417
stored_reference m_ref;
370418
};
419+
420+
#if __cpp_lib_ranges >= 202202L
421+
422+
template<utf_encoding Enc>
423+
struct as_utf_func :
424+
#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 190000
425+
//libc++ lies about __cpp_lib_ranges
426+
public std::__range_adaptor_closure<as_utf_func<Enc>>
427+
#else
428+
public std::ranges::range_adaptor_closure<as_utf_func<Enc>>
429+
#endif
430+
{
431+
template <class Range>
432+
[[nodiscard]] constexpr auto operator()(Range && range) const
433+
noexcept(noexcept(utf_view<Enc, std::remove_reference_t<Range>>(std::forward<Range>(range))))
434+
-> decltype(utf_view<Enc, std::remove_reference_t<Range>>(std::forward<Range>(range)))
435+
{ return utf_view<Enc, std::remove_reference_t<Range>>(std::forward<Range>(range)); }
436+
437+
};
438+
439+
template<utf_encoding Enc>
440+
inline constexpr auto as_utf = as_utf_func<Enc>{};
441+
inline constexpr auto as_utf8 = as_utf_func<utf8>{};
442+
inline constexpr auto as_utf16 = as_utf_func<utf16>{};
443+
inline constexpr auto as_utf32 = as_utf_func<utf32>{};
444+
445+
#endif
371446
}
372447

373448
namespace std::ranges {

test/test_utf_iteration.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,5 +680,19 @@ TEST_CASE( "UTF32 Iteration on UTF32 sequence", "[utf_iteration]") {
680680
check_iteration<utf32, utf32>(U"\x110000""a", U"\uFFFDa"); //too large + good
681681
}
682682

683+
TEST_CASE( "Ranges", "[utf_iteration]" ) {
684+
685+
#if __cpp_lib_ranges >= 202202L
686+
CHECK(std::ranges::equal(as_utf32(std::vector({'a', 'b', 'c'})), std::array{U'a', U'b', U'c'}));
687+
CHECK(std::ranges::equal(std::vector({'a', 'b', 'c'}) | as_utf32, std::array{U'a', U'b', U'c'}));
688+
689+
CHECK(std::ranges::equal(as_utf16(std::vector({'a', 'b', 'c'})), std::array{u'a', u'b', u'c'}));
690+
CHECK(std::ranges::equal(std::vector({'a', 'b', 'c'}) | as_utf16, std::array{u'a', u'b', u'c'}));
691+
692+
CHECK(std::ranges::equal(as_utf8(std::vector({u'a', u'b', u'c'})), std::array{'a', 'b', 'c'}));
693+
CHECK(std::ranges::equal(std::vector({u'a', u'b', u'c'}) | as_utf8, std::array{'a', 'b', 'c'}));
694+
#endif
695+
}
696+
683697

684698

0 commit comments

Comments
 (0)