diff --git a/include/boost/url/grammar/impl/token_rule.hpp b/include/boost/url/grammar/impl/token_rule.hpp index 76945aee2..e10af04c7 100644 --- a/include/boost/url/grammar/impl/token_rule.hpp +++ b/include/boost/url/grammar/impl/token_rule.hpp @@ -31,7 +31,8 @@ parse( BOOST_URL_RETURN_EC( error::need_more); } - it = (find_if_not)(it, end, cs_); + auto const& cs = this->get(); + it = grammar::find_if_not(it, end, cs); if(it != it0) return core::string_view(it0, it - it0); BOOST_URL_RETURN_EC( diff --git a/include/boost/url/grammar/token_rule.hpp b/include/boost/url/grammar/token_rule.hpp index 27332b242..df806879d 100644 --- a/include/boost/url/grammar/token_rule.hpp +++ b/include/boost/url/grammar/token_rule.hpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include namespace boost { namespace urls { @@ -22,6 +24,7 @@ namespace grammar { namespace implementation_defined { template struct token_rule_t + : private empty_value { using value_type = core::string_view; @@ -39,12 +42,21 @@ struct token_rule_t constexpr token_rule_t( CharSet const& cs) noexcept - : cs_(cs) + : empty_value( + empty_init, cs) { } -private: - CharSet const cs_; + template + constexpr + token_rule_t( + typename std::enable_if< + std::is_default_constructible::value, + int>::type = 0) noexcept + : empty_value( + empty_init) + { + } }; } @@ -86,6 +98,48 @@ token_rule( return {cs}; } +/** Match a non-empty string of characters from a default-constructible set + + This overload is only available when CharSet is + default constructible. + + If there is no more input, the error code + @ref error::need_more is returned. + + @par Value Type + @code + using value_type = core::string_view; + @endcode + + @par Example + Rules are used with the function @ref parse. + @code + system::result< core::string_view > rv = parse( "abcdef", token_rule() ); + @endcode + + @par BNF + @code + token = 1*( ch ) + @endcode + + @tparam CharSet The character set type to use + @return The token rule + + @see + @ref alpha_chars, + @ref parse. +*/ +template +constexpr +auto +token_rule() noexcept -> + typename std::enable_if< + std::is_default_constructible::value, + implementation_defined::token_rule_t>::type +{ + return implementation_defined::token_rule_t(); +} + } // grammar } // urls } // boost diff --git a/test/unit/grammar/token_rule.cpp b/test/unit/grammar/token_rule.cpp index 781d366ca..c1df3ba6b 100644 --- a/test/unit/grammar/token_rule.cpp +++ b/test/unit/grammar/token_rule.cpp @@ -37,6 +37,31 @@ struct token_rule_test ok(r, "a", "a"); bad(r, "", error::need_more); bad(r, "1", error::mismatch); + + // Test default construction with stateless CharSet + { + constexpr auto r2 = token_rule(); + ok(r2, "abc", "abc"); + ok(r2, "ABC", "ABC"); + bad(r2, "", error::need_more); + bad(r2, "123", error::mismatch); + } + + // Test EBO - token_rule_t with empty CharSet should be minimal size + { + // alpha_chars_t is empty (no data members) + static_assert( + sizeof(implementation_defined::token_rule_t) == 1, + "EBO should reduce size to 1 byte for empty CharSet"); + } + + // Test that default-constructed rules work correctly at runtime + { + auto r3 = token_rule(); + auto rv = parse("abcdef", r3); + BOOST_TEST(rv.has_value()); + BOOST_TEST_EQ(rv.value(), "abcdef"); + } } };