Skip to content

Commit

Permalink
Proper view interface for utf_view and grapheme_view
Browse files Browse the repository at this point in the history
  • Loading branch information
gershnik committed Jan 7, 2025
1 parent 835945b commit 0b68469
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 5 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

### Added

- `grapheme_view` which provides ability to iterate over grapheme clusters in `sys_string` and any UTF range.
- `grapheme_view` and `graphemes` adapter which provide ability to iterate over grapheme clusters in `sys_string` and any UTF range.

### Changed

Expand Down
12 changes: 9 additions & 3 deletions lib/inc/sys_string/grapheme_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,6 @@ namespace sysstr
using pointer = typename iterator::pointer;
using const_pointer = pointer;

// static const bool borrowed = (ViewType == byref) ||
// (ViewType == byval && std::ranges::borrowed_range<range>);

public:
grapheme_view(const Range & src) noexcept(noexcept(range(src))) :
m_src(src)
Expand Down Expand Up @@ -150,6 +147,15 @@ namespace sysstr
SYS_STRING_FORCE_INLINE std::default_sentinel_t crend() const requires(is_reversible)
{ return rend(); }

bool empty() const
{ return std::ranges::empty(m_src); }
explicit operator bool() const
{ return !std::ranges::empty(m_src); }
decltype(auto) front() const
{ return *this->begin();}
decltype(auto) back() const requires(is_reversible)
{ return *this->rbegin();}

reverse_iterator reverse(iterator it) const requires(is_reversible)
{ return reverse_iterator(it, std::ranges::rend(m_src)); }

Expand Down
11 changes: 10 additions & 1 deletion lib/inc/sys_string/utf_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ namespace sysstr
SYS_STRING_FORCE_INLINE static auto range_ref(const range * src) -> const range &
{ return *src; }

static constexpr bool is_reversible = ranges::reverse_traversable_range<std::remove_reference_t<range>>;
static constexpr bool is_reversible = ranges::reverse_traversable_range<range>;
static constexpr auto source_encoding = utf_encoding_of<std::ranges::range_value_t<range>>;

using access_iterator = decltype(std::ranges::begin(range_ref(std::declval<stored_type>())));
Expand Down Expand Up @@ -358,6 +358,15 @@ namespace sysstr
SYS_STRING_FORCE_INLINE std::default_sentinel_t crend() const requires(is_reversible)
{ return rend(); }

bool empty() const requires(std::ranges::forward_range<Range>)
{ return std::ranges::empty(range_ref(m_src)); }
explicit operator bool() const requires(std::ranges::forward_range<Range>)
{ return !std::ranges::empty(m_src); }
decltype(auto) front() const requires(std::ranges::forward_range<Range>)
{ return *this->begin();}
decltype(auto) back() const requires(is_reversible)
{ return *this->rbegin();}

reverse_iterator reverse(iterator it) const requires(is_reversible)
{ return reverse_iterator(it, std::ranges::rend(range_ref(m_src))); }

Expand Down
25 changes: 25 additions & 0 deletions test/test_grapheme.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,29 @@ TEST_CASE("ranges") {
check_graphemes_reverse_range(as_utf32("ab"s), {U"a", U"b"});
}

TEST_CASE("view interface") {

auto view = graphemes("abc"s);

CHECK(view);
CHECK(!view.empty());
auto fr = view.front();
CHECK(fr.size() == 1);
CHECK(fr.front() == 'a');
auto bc = view.back();
CHECK(bc.size() == 1);
CHECK(bc.front() == 'c');

CHECK(view.begin() == view.cbegin());
static_assert(std::is_same_v<decltype(view.end()), std::default_sentinel_t>);
static_assert(std::is_same_v<decltype(view.cend()), std::default_sentinel_t>);
CHECK(view.rbegin() == view.crbegin());
static_assert(std::is_same_v<decltype(view.rend()), std::default_sentinel_t>);
static_assert(std::is_same_v<decltype(view.crend()), std::default_sentinel_t>);

auto empty_view = graphemes(""s);
CHECK(!empty_view);
CHECK(empty_view.empty());
}

}
61 changes: 61 additions & 0 deletions test/test_utf_iteration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,67 @@ TEST_CASE( "Ranges" ) {
CHECK(std::ranges::equal(std::vector({u'a', u'b', u'c'}) | as_utf8, std::array{'a', 'b', 'c'}));
}

TEST_CASE("view interface") {

{
auto view = as_utf32("abc"s);

CHECK(view);
CHECK(!view.empty());
CHECK(view.front() == U'a');
CHECK(view.back() == U'c');

CHECK(view.begin() == view.cbegin());
static_assert(std::is_same_v<decltype(view.end()), std::default_sentinel_t>);
static_assert(std::is_same_v<decltype(view.cend()), std::default_sentinel_t>);
CHECK(view.rbegin() == view.crbegin());
static_assert(std::is_same_v<decltype(view.rend()), std::default_sentinel_t>);
static_assert(std::is_same_v<decltype(view.crend()), std::default_sentinel_t>);

auto empty_view = as_utf32(""s);
CHECK(!empty_view);
CHECK(empty_view.empty());
}
{
auto view = as_utf16("abc"s);

CHECK(view);
CHECK(!view.empty());
CHECK(view.front() == u'a');
CHECK(view.back() == u'c');

CHECK(view.begin() == view.cbegin());
static_assert(std::is_same_v<decltype(view.end()), std::default_sentinel_t>);
static_assert(std::is_same_v<decltype(view.cend()), std::default_sentinel_t>);
CHECK(view.rbegin() == view.crbegin());
static_assert(std::is_same_v<decltype(view.rend()), std::default_sentinel_t>);
static_assert(std::is_same_v<decltype(view.crend()), std::default_sentinel_t>);

auto empty_view = as_utf32(""s);
CHECK(!empty_view);
CHECK(empty_view.empty());
}
{
auto view = as_utf8("abc"s);

CHECK(view);
CHECK(!view.empty());
CHECK(view.front() == 'a');
CHECK(view.back() == 'c');

CHECK(view.begin() == view.cbegin());
static_assert(std::is_same_v<decltype(view.end()), std::default_sentinel_t>);
static_assert(std::is_same_v<decltype(view.cend()), std::default_sentinel_t>);
CHECK(view.rbegin() == view.crbegin());
static_assert(std::is_same_v<decltype(view.rend()), std::default_sentinel_t>);
static_assert(std::is_same_v<decltype(view.crend()), std::default_sentinel_t>);

auto empty_view = as_utf32(""s);
CHECK(!empty_view);
CHECK(empty_view.empty());
}
}

#endif

}

0 comments on commit 0b68469

Please sign in to comment.