std::regex compatibility#

Synopsis#

real::compat – the <regex> API you already type, on the linear engine: basic_regex (aliases regex / wregex), the regex_match / regex_search / regex_replace families, match_results (aliases smatch / cmatch) and the two iterators. Which patterns run on REAL and which fall back is the compatibility contract; migration lives in the std::regex drop-in – this page is the object reference.

Interface#

template<typename CharT = char, typename Traits = std::regex_traits<CharT>>
class basic_regex#

A std::basic_regex-compatible pattern, backed by real where proven, else std.

Template Parameters:
  • CharT – Character type (char; other types route straight to std).

  • Traits – Regex traits (std parity).

Public Functions

basic_regex() = default#

An empty pattern on the std backend — the variant’s first alternative default-constructs.

inline explicit basic_regex(const CharT *pattern, flag_type f = regex_constants::ECMAScript, policy pol = policy::strict)#

Compiles pattern from a C string.

Parameters:
  • pattern[in] NUL-terminated pattern text.

  • f[in] Syntax options; the grammar they select may route the pattern to std.

  • pol[in] Strict rejects a pattern REAL cannot represent linearly; fallback routes it to std.

Throws:

real::compat::regex_error – on an invalid pattern, or on a strict-policy rejection.

inline explicit basic_regex(const string_type &pattern, flag_type f = regex_constants::ECMAScript, policy pol = policy::strict)#

Compiles pattern from an owned string.

Parameters:
  • pattern[in] The pattern text.

  • f[in] Syntax options.

  • pol[in] Rejection policy; see the C-string overload.

Throws:

real::compat::regex_error – on an invalid pattern, or on a strict-policy rejection.

inline basic_regex(const CharT *pattern, std::size_t len, flag_type f = regex_constants::ECMAScript, policy pol = policy::strict)#

Compiles the first len characters of pattern, which need not be NUL-terminated.

Parameters:
  • pattern[in] Pattern text.

  • len[in] Its length in characters.

  • f[in] Syntax options.

  • pol[in] Rejection policy.

Throws:

real::compat::regex_error – on an invalid pattern, or on a strict-policy rejection.

template<typename It>
inline basic_regex(It begin, It end, flag_type f = regex_constants::ECMAScript, policy pol = policy::strict)#

Compiles the pattern in [begin, end).

Parameters:
  • begin[in] Start of the pattern text.

  • end[in] One past its end.

  • f[in] Syntax options.

  • pol[in] Rejection policy.

Throws:

real::compat::regex_error – on an invalid pattern, or on a strict-policy rejection.

inline std::size_t mark_count() const noexcept#

Number of marked sub-expressions (excluding group 0), as std::basic_regex.

Returns:

The group count.

inline flag_type flags() const noexcept#

The flags this regex was built with.

Returns:

Those flags.

inline void swap(basic_regex &other) noexcept#

Exchanges engines, flags, policy and cached state with other.

Parameters:

other[inout] The regex to swap with.

inline bool uses_real() const noexcept#

True if this regex is backed by the real engine (vs the std fallback).

Returns:

true if REAL’s linear engine holds it.

inline compat::policy policy() const noexcept#

The drop-in policy this regex was constructed with.

Returns:

Strict or fallback.

inline bool nullable() const noexcept#

Whether the pattern can match the empty string (real’s empty_match_possible hint).

Empty-match traversal (replace / iterate) follows Python’s advance rules in real, which differ from ECMAScript. So a nullable real-backed pattern routes those operations to a lazily built std::regex (std_engine) — per operation, not at construction, so search/match keep real’s linear-time guarantee even on nullable-ReDoS patterns like (a*)*.

Returns:

true if it is nullable; regex_replace then routes to std, whose empty-match traversal differs from REAL’s Python-lineage one.

inline bool uses_real_traversal() const noexcept#

Whether replace/iterate run on the real traversal (real-backed AND non-nullable AND no nullable captured-repeat group). A nullable pattern delegates replace/iterate to std (the empty-match traversal differs; and iterating a nullable pattern whose per-position match cost is O(n) is O(n²) on any linear engine, so routing it buys correctness but not a linear guarantee — see the nullable note in COMPATIBILITY.md). A pattern with a capturing group that is nullable under a quantifier ((ab|)+a) is itself non-nullable as a whole, but real’s last-consuming-iteration capture (RE2/Rust/Go lineage) diverges from an ECMAScript backtracker’s extra empty final iteration on that GROUP’s span — so it routes too, for the same reason: regex_search/match are unaffected (see the nullable-loop group-capture section of COMPATIBILITY.md — the search residue is intentional, not an oversight).

Returns:

true for a real-backed, non-nullable pattern.

template<typename BidirIt, typename Alloc = std::allocator<sub_match<BidirIt>>>
class match_results#

The result of a match: group sub-matches plus the prefix and suffix.

Stores both ends of the searched sequence (first_, last_) so suffix() and lengths are exact (the end is not derivable from a base pointer alone). Filled either from real’s byte offsets or copied from a std::match_results on the fallback path.

Template Parameters:
  • BidirIt – A contiguous iterator into the searched sequence.

  • Alloc – Allocator for the sub-match vector (std parity; default suffices).

Public Functions

inline bool ready() const noexcept#

Whether a successful match has been stored.

Returns:

true once a search or match has filled this object.

inline size_type size() const noexcept#

Number of marks (groups), including group 0; 0 when there was no match.

Returns:

The sub-match count.

inline bool empty() const noexcept#

Whether there are no marks at all.

Returns:

true when size is 0, i.e. no match was stored.

inline const_reference operator[](size_type n) const#

The sub-match for group n (group 0 is the whole match). Out-of-range n returns a reference to an unmatched sub_match anchored at the sequence end {last_, last_, false}, exactly like std::match_results::operator[] (verified on libc++ and libstdc++) — never out-of-bounds. A token selector {2}/{5} or a negative field relies on this.

Parameters:

n[in] Group index; 0 is the whole match.

Returns:

That group’s sub-match, or the end-anchored unmatched one when n is out of range.

inline difference_type position(size_type n = 0) const#

Start offset of group n from the sequence start. For an out-of-range group std anchors the sub_match at the end, so the offset is the full sequence length.

Parameters:

n[in] Group index; 0 is the whole match.

Returns:

Its start offset from the sequence start.

inline difference_type length(size_type n = 0) const#

Length of group n (0 if out of range or unmatched).

Parameters:

n[in] Group index; 0 is the whole match.

Returns:

Its length in characters.

inline string_type str(size_type n = 0) const#

Matched text of group n (empty if out of range or unmatched).

Parameters:

n[in] Group index; 0 is the whole match.

Returns:

An owned copy of its text.

inline const value_type &prefix() const#

The unmatched prefix (sequence start up to the whole match).

Returns:

The prefix sub-match; see rebase_prefix for what it means during iteration.

inline const value_type &suffix() const#

The unmatched suffix (whole match end to sequence end).

Returns:

The suffix sub-match.

inline const_iterator begin() const#

Iteration over the marks, group 0 first.

Returns:

An iterator to the first sub-match. Iterators are const, as in std::match_results.

inline const_iterator end() const#

End of the mark range.

Returns:

One past the last sub-match.

template<typename BidirIt>
class sub_match#

A matched sub-expression: a [first, second) range into the searched sequence.

Contiguous iterators only — sub_match is built from byte offsets, which requires the underlying storage to be contiguous (a std::deque::iterator is random-access but not contiguous, so it is rejected).

Template Parameters:

BidirIt – A contiguous iterator into the searched sequence.

Public Functions

inline difference_type length() const#

Length of the sub-match (0 if it did not participate).

Returns:

second - first, or 0 when matched is false.

inline string_type str() const#

The matched text as an owned string (empty if it did not participate).

Returns:

A copy of [first, second), or an empty string when matched is false.

inline int compare(const string_type &other) const#

Three-way length/lexicographic comparison against a string (std::sub_match::compare).

Parameters:

other[in] The string to compare against.

Returns:

Negative, zero or positive as str() orders before, equal to, or after other.

inline int compare(const sub_match &other) const#

Three-way comparison against another sub-match, by matched text.

Parameters:

other[in] The sub-match to compare against.

Returns:

Negative, zero or positive as str() orders before, equal to, or after other.str().

Public Members

BidirIt first = {}#

Start of the sub-match.

BidirIt second = {}#

One past the end of the sub-match.

bool matched = {false}#

Whether this sub-expression participated.

class regex_error : public std::regex_error#

std::regex_error-compatible exception.

Thrown on two paths, both preserving the std::regex_error contract:

  • Invalid for both backends (a syntax error): real rejects, and so does std — the exact std .code() is preserved and what() keeps std’s message, so a syntax error is byte-for-byte std.

  • Strict-policy rejection (policy): a pattern real cannot represent linearly but std could (a backreference, an unbounded lookaround, a POSIX class) — code() is error_complexity and what() carries a REAL-identifiable message. Under policy::fallback this path delegates to std instead of throwing, so the only thrown case there is the invalid-for-both one above.

Public Functions

inline explicit regex_error(const std::regex_error &error)#

From a std backend error (the fallback path); keeps std’s exact code.

Parameters:

error[in] The standard library error to adopt.

inline regex_error(std::regex_constants::error_type code, std::string message)#

With an explicit code and message — the strict-policy rejection of a pattern REAL cannot represent linearly (error_complexity), carrying a REAL-identifiable message.

Parameters:
  • code[in] The std::regex_constants::error_type to report.

  • message[in] The text what returns.

inline const char *what() const noexcept override#

The message, which for a strict-policy rejection identifies REAL as the source.

Returns:

A NUL-terminated message valid for this object’s lifetime.

template<typename BidirIt, typename CharT = typename std::iterator_traits<BidirIt>::value_type, typename Traits = std::regex_traits<CharT>>
class regex_iterator#

Iterates the non-overlapping matches of a pattern in a sequence (std::regex_iterator).

Same per-operation routing as regex_replace — a real-backed, non-nullable pattern drives real’s linear traversal (repeated region search — a non-nullable pattern never matches empty, so the position always advances past the match and the ECMAScript and real sequences agree); the std backend and nullable patterns wrap std::regex_iterator (whose empty-match advance is ECMAScript’s). The default-constructed iterator is the end sentinel.

Template Parameters:

BidirIt – A contiguous iterator into the searched sequence.

Public Functions

regex_iterator() = default#

Constructs the end sentinel.

inline regex_iterator(BidirIt first, BidirIt last, const regex_type &re, regex_constants::match_flag_type flags = regex_constants::match_default)#

Constructs a begin iterator over [first, last) and finds the first match. A constraining match flag (see detail::real_honors) routes to the std backend, which carries the flags through the wrapped std::regex_iterator.

Parameters:
  • first[in] Start of the character sequence.

  • last[in] End of the character sequence.

  • re[in] The pattern; it must outlive this iterator.

  • flags[in] Match flags, defaulting to regex_constants::match_default.

inline regex_iterator(const regex_iterator &other)#

Copies the iteration position, but NOT the walker.

The walker is an accelerator over real_pos_, never the state itself, and that is what makes this cheap where the obvious designs are not. Copying it would clone the VM scratch it embeds; sharing it copy-on-write would clone on the first advance, which operator++(int) performs on every call &#8212; buying ++it at the price of it++. Leaving the copy without one costs it a single walker construction IF it ever advances, and nothing at all if it does not, which is what a post-increment’s discarded result actually does.

Parameters:

other[in] The iterator to copy.

regex_iterator(regex_iterator&&) noexcept = default#

Moves the walker along with the position.

regex_iterator(BidirIt first, BidirIt last, const regex_type &&re, regex_constants::match_flag_type flags = regex_constants::match_default) = delete#

Constructing from a temporary regex would dangle (std::regex_iterator parity).

inline reference operator*() const#

The current match.

Returns:

A reference to it, valid until the next increment.

inline regex_iterator &operator++()#

Advances to the next match, becoming the end sentinel when there is none.

Returns:

*this.

inline regex_iterator operator++(int)#

Advances to the next match, returning the previous position.

Returns:

A copy of *this as it was before the increment.

inline bool operator==(const regex_iterator &other) const#

Equality. Two non-end iterators compare equal only for the same regex, sequence, flags and current match — not for a coincidental same position across different patterns.

Parameters:

other[in] The iterator to compare against.

Returns:

Whether the two denote the same iteration position.

template<typename BidirIt, typename CharT = typename std::iterator_traits<BidirIt>::value_type, typename Traits = std::regex_traits<CharT>>
class regex_token_iterator#

Enumerates selected sub-matches (or the text between matches) — std::regex_token_iterator.

Wraps regex_iterator, so it inherits the per-operation nullable routing untouched (it never replays the engine choice). For each match it yields the requested fields in order: a field N >= 0 is capture group N (a non-participating group yields an empty matched == false token); the field -1 is the text before this match since the previous one — i.e. the match’s prefix() — which turns -1 into a splitter. After the last match, a trailing -1 field yields the final suffix iff it is non-empty (std’s rule; an empty field between adjacent matches is still produced, the asymmetry std pins). With -1 and no match at all, the whole sequence is the single token.

Template Parameters:

BidirIt – A contiguous iterator into the searched sequence.

Public Functions

regex_token_iterator() = default#

Constructs the end sentinel.

inline regex_token_iterator(BidirIt first, BidirIt last, const regex_type &re, int submatch = 0, regex_constants::match_flag_type flags = regex_constants::match_default)#

Selects a single sub-match field (0 = whole match, N = group N, -1 = split).

Parameters:
  • first[in] Start of the character sequence.

  • last[in] End of the character sequence.

  • re[in] The pattern; it must outlive this iterator.

  • submatch[in] The field to yield per match.

  • flags[in] Match flags, defaulting to regex_constants::match_default.

inline regex_token_iterator(BidirIt first, BidirIt last, const regex_type &re, const std::vector<int> &submatches, regex_constants::match_flag_type flags = regex_constants::match_default)#

Selects a list of fields, cycled per match (e.g. {1, 2}, {-1}). The match flags are forwarded to the wrapped regex_iterator, so the nullable/honors routing is inherited.

Parameters:
  • first[in] Start of the character sequence.

  • last[in] End of the character sequence.

  • re[in] The pattern; it must outlive this iterator.

  • submatches[in] The fields to cycle through; an empty list is treated as {0}.

  • flags[in] Match flags, defaulting to regex_constants::match_default.

inline regex_token_iterator(BidirIt first, BidirIt last, const regex_type &re, std::initializer_list<int> submatches, regex_constants::match_flag_type flags = regex_constants::match_default)#

Selects a list of fields from a braced list (e.g. {-1}).

Parameters:
  • first[in] Start of the character sequence.

  • last[in] End of the character sequence.

  • re[in] The pattern; it must outlive this iterator.

  • submatches[in] The fields to cycle through.

  • flags[in] Match flags, defaulting to regex_constants::match_default.

regex_token_iterator(BidirIt first, BidirIt last, const regex_type &&re, int submatch = 0, regex_constants::match_flag_type flags = regex_constants::match_default) = delete#

Constructing from a temporary regex would dangle (std::regex_token_iterator parity).

regex_token_iterator(BidirIt first, BidirIt last, const regex_type &&re, const std::vector<int> &submatches, regex_constants::match_flag_type flags = regex_constants::match_default) = delete#

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

inline reference operator*() const#

The current token.

Returns:

A reference to it, valid until the next increment.

inline regex_token_iterator &operator++()#

Advances to the next token, becoming the end sentinel when the fields are exhausted.

Returns:

*this.

inline regex_token_iterator operator++(int)#

Advances to the next token, returning the previous position.

Returns:

A copy of *this as it was before the increment.

inline bool operator==(const regex_token_iterator &other) const#

Equality. Two non-end iterators compare equal only for the same underlying walk, field selectors, field index, suffix state and current token.

Parameters:

other[in] The iterator to compare against.

Returns:

Whether the two denote the same iteration position.

Matching and replacing#

The <regex> free functions ship with their full standard overload sets (C strings, std::string, iterator pairs, with or without a match_results):

  • regex_match – the whole range must match.

  • regex_search – leftmost match anywhere in the range.

  • regex_replace – template substitution over every match.

Their complete signatures render in the Doxygen namespace reference:

real::compat — every overload (/api).

Constants and policy#

The <regex> constants the constructors and free functions take, and the policy knob that picks strict rejection or the std::regex fallback:

enum real::compat::regex_constants::syntax_option_type#

Grammar / option flags (own bit values; mapped to real::flags or std at construction).

Values:

enumerator ECMAScript#

The default grammar.

enumerator icase#

Case-insensitive (ASCII).

enumerator nosubs#

Do not expose sub-expressions (groups still computed).

enumerator optimize#

Hint to favour matching speed; honoured as a no-op.

enumerator collate#

Locale-sensitive ranges; forces the std backend.

enumerator multiline#

^/$ match at line boundaries.

enumerator basic#

POSIX BRE — translated onto REAL when the pattern translates, else the std backend.

enumerator extended#

POSIX ERE — translated onto REAL when the pattern translates, else the std backend.

enumerator awk#

awk grammar (ERE + C escapes) — translated onto REAL when it translates, else std.

enumerator grep#

grep grammar (BRE, lines joined by |) — translated onto REAL when it translates, else std.

enumerator egrep#

egrep grammar (ERE, lines joined by |) — translated onto REAL when it translates, else std.

enum real::compat::regex_constants::match_flag_type#

Match-control flags: the common subset.

Values:

enumerator match_default#

No constraint; the operation may stay on REAL.

enumerator match_not_bol#

^ does not match the start of the sequence.

enumerator match_not_eol#

$ does not match the end of the sequence.

enumerator match_not_bow#

\b does not match at the start.

enumerator match_not_eow#

\b does not match at the end.

enumerator match_any#

Any match will do; REAL satisfies it by returning the leftmost one.

enumerator match_not_null#

Do not match an empty sequence.

enumerator match_continuous#

The match must start at the first character.

enumerator match_prev_avail#

--first is valid, so ^ and \b may inspect the character before it.

enumerator format_default#

ECMAScript replacement syntax, copying the unmatched text.

enumerator format_sed#

sed/POSIX replacement syntax (routes to std).

enumerator format_no_copy#

Do not copy the parts of the text that did not match.

enumerator format_first_only#

Replace only the first match.

enum class real::compat::policy : std::uint8_t#

The drop-in policy for a pattern the linear engine cannot represent (backreferences, an unbounded lookaround, a POSIX class, …). strict (the default) rejects it, so every accepted pattern executes each regex_search/regex_match in time linear in the input — the ReDoS-safety guarantee (replace/iterate compose O(n) such operations: quadratic worst-case on any linear engine, never exponential); fallback delegates it to std::regex, which may accept it but forfeits the guarantee for that pattern.

Values:

enumerator strict#

Reject an ineligible pattern (throws regex_error with error_complexity). The default.

enumerator fallback#

Delegate an ineligible pattern to std::regex (backtracking — not ReDoS-safe).

Complexity#

A pattern the shim routes to REAL matches in guaranteed linear time – O(len(text)) – and never backtracks (ReDoS-safe). Which patterns route, and what the opt-in fallback changes, is exactly the compatibility contract.

Example#

Compiled and run by the example-check gate on every push:

  namespace rc = real::compat;

  const rc::regex   date {R"((\d{4})-(\d{2}))"};
  const std::string text {"released 2026-07-21"};

  rc::smatch m;
  if (rc::regex_search(text, m, date)) {
    std::cout << m[1].str() << "/" << m[2].str() << "\n";  // 2026/07
  }

See also#