basic_match_range#

Synopsis#

The lazy range find_iter() returns. Iterate it with a range-for; each element is a match result. Nothing is scanned until the iterator advances, and each step applies the non-overlapping and empty-match advance rules.

Interface#

The range is what find_iter returns. Constructing one from a program_view is the engine’s job; user code only iterates.

template<typename Storage, bool TrailingLA = false>
class basic_match_range#

A range of matches, returned by find_iter() and usable in range-for.

The regex and the text must outlive the range. Empty matches follow Python: an empty match is yielded, then the scan advances one codepoint.

Template Parameters:

Storage – The regex’s storage policy.

Public Functions

inline constexpr basic_match_iterator<Storage, TrailingLA> begin() const#

Returns an iterator to the first match.

Returns:

An iterator positioned on the first match, or equal to end when there is none.

inline constexpr basic_match_iterator<Storage, TrailingLA> end() const#

Returns the end sentinel.

Returns:

The past-the-end iterator.

template<typename Storage, bool TrailingLA = false>
class basic_match_iterator#

Forward iterator over the non-overlapping matches in a text.

Follows Python’s empty-match rules: an empty match is yielded (even right after a non-empty one), then the scan advances by one codepoint. The regex and the text must outlive the iterator. Obtained from basic_match_range.

Template Parameters:

Storage – The regex’s storage policy (selects the result/scratch types).

Public Types

using value_type = basic_match_result<typename Storage::slot_storage>#

Yielded match type.

Public Functions

inline constexpr basic_match_iterator() noexcept#

Constructs the end sentinel.

User-provided, not = default: end()’s return {} value-initializes, and with a defaulted constructor that zero-fills the whole object (its VM state included, ~10 KB) before constructing it. The members a sentinel is read through carry their own initializers (done_ is true).

inline constexpr const value_type &operator*() const#

Returns the current match.

Returns:

A reference to the result, valid until the next increment.

inline constexpr const value_type *operator->() const#

Returns pointer to the current match.

Returns:

A pointer to the result, valid until the next increment.

inline constexpr basic_match_iterator &operator++()#

Advances to the next match.

Returns:

*this.

inline constexpr basic_match_iterator operator++(int)#

Advances to the next match (post-increment).

Returns:

A copy of the iterator at its pre-increment position.

inline constexpr bool exhausted() const noexcept#

Whether the walk is over, without building an end sentinel to compare against.

Prefer this in a hand-rolled loop: it == basic_match_iterator{} answers the same question but materialises a full walker (a whole heap-backed scratch state) just to test.

Returns:

true once no further match will be produced.

inline constexpr bool operator==(const basic_match_iterator &other) const#

Returns true if both denote the same position in the same walk, or both are the end.

Live iterators compare offset, subject and program. Exhausted ones are equal whatever they walked: basic_match_range::end is a default-constructed sentinel carrying neither.

Parameters:

other – [in] Another iterator.

Returns:

true if both are exhausted, or both are live at the same offset of the same walk.

Complexity#

Lazy: each increment advances to the next match. A full traversal is one guaranteed linear scan of the text – O(len(text)) – and never backtracks (ReDoS-safe by construction). Prefer exhausted() over comparing against a default-constructed end iterator in a hand-rolled loop.

Example#

Compiled and run by the example-check gate on every push – empty-match advance and a region (pos is a start offset, not a slice):

  // Empty-match rule (Python): yield the empty match, then advance one codepoint.
  const real::regex maybe {R"(a*)"};
  std::size_t empties = 0;
  for (const auto& hit : maybe.find_iter("bb")) {
    if (hit.start() == hit.end()) {
      ++empties;
    }
  }
  // "bb" has three cursor positions, including the end: [0,0) [1,1) [2,2).

  // pos is a start offset, not a slice -- iteration begins at byte 4.
  const real::regex word {R"(\w+)"};
  constexpr auto phrase = "one two three"sv;
  for (const auto& hit : word.find_iter(phrase, 4)) {
    std::cout << hit[0] << "\n";  // two, then three
  }

See also#