diff --git a/include/boost/spirit/home/x3/directive/skip.hpp b/include/boost/spirit/home/x3/directive/skip.hpp index 8f7c6c748..a8ae4de58 100644 --- a/include/boost/spirit/home/x3/directive/skip.hpp +++ b/include/boost/spirit/home/x3/directive/skip.hpp @@ -1,7 +1,7 @@ /*============================================================================= Copyright (c) 2001-2014 Joel de Guzman Copyright (c) 2013 Agustin Berge - Copyright (c) 2024 Nana Sakisaka + Copyright (c) 2024-2025 Nana Sakisaka Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,67 +14,94 @@ #include #include #include -#include -namespace boost { namespace spirit { namespace x3 +#include +#include +#include + +namespace boost::spirit::x3 { template struct reskip_directive : unary_parser> { - typedef unary_parser> base_type; - static bool const is_pass_through_unary = true; - static bool const handles_container = Subject::handles_container; + using base_type = unary_parser>; + static constexpr bool is_pass_through_unary = true; + static constexpr bool handles_container = Subject::handles_container; - constexpr reskip_directive(Subject const& subject) - : base_type(subject) {} + template + requires + (!std::is_same_v, reskip_directive>) && + std::is_constructible_v + constexpr reskip_directive(SubjectT&& subject) + noexcept(std::is_nothrow_constructible_v) + : base_type(std::forward(subject)) + {} - template - typename disable_if, bool>::type - parse(Iterator& first, Iterator const& last - , Context& context, RContext& rcontext, Attribute& attr) const + template Se, typename Context, typename RContext, typename Attribute> + requires has_skipper_v + [[nodiscard]] constexpr bool + parse(It& first, Se const& last, Context const& context, RContext& rcontext, Attribute& attr) const + noexcept(is_nothrow_parsable_v) { - auto const& skipper = - detail::get_unused_skipper(x3::get(context)); + static_assert(Parsable); + return this->subject.parse(first, last, context, rcontext, attr); + } - auto const local_ctx = make_context(skipper, context); + private: + template + using context_t = x3::context< + skipper_tag, + decltype(detail::get_unused_skipper(x3::get(std::declval()))), + Context + >; + + public: + template Se, typename Context, typename RContext, typename Attribute> + requires (!has_skipper_v) + [[nodiscard]] constexpr bool + parse(It& first, Se const& last, Context const& context, RContext& rcontext, Attribute& attr) const + // never noexcept (requires expectation failure modification) + { + static_assert(Parsable, RContext, Attribute>); + + auto const& skipper = detail::get_unused_skipper(x3::get(context)); + + auto const local_ctx = x3::make_context(skipper, context); bool const r = this->subject.parse(first, last, local_ctx, rcontext, attr); #if !BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE - if (has_expectation_failure(local_ctx)) + if (x3::has_expectation_failure(local_ctx)) { - set_expectation_failure(get_expectation_failure(local_ctx), context); + x3::set_expectation_failure(x3::get_expectation_failure(local_ctx), context); } #endif return r; } - template - typename enable_if, bool>::type - parse(Iterator& first, Iterator const& last - , Context const& context, RContext& rcontext, Attribute& attr) const - { - return this->subject.parse(first, last, context, rcontext, attr); - } }; template struct skip_directive : unary_parser> { - typedef unary_parser> base_type; - static bool const is_pass_through_unary = true; - static bool const handles_container = Subject::handles_container; + using base_type = unary_parser>; + static constexpr bool is_pass_through_unary = true; + static constexpr bool handles_container = Subject::handles_container; - constexpr skip_directive(Subject const& subject, Skipper const& skipper) - : base_type(subject) - , skipper(skipper) + template + requires std::is_constructible_v && std::is_constructible_v + constexpr skip_directive(SubjectT&& subject, SkipperT&& skipper) + noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : base_type(std::forward(subject)) + , skipper_(skipper) {} - template - bool parse(Iterator& first, Iterator const& last - , unused_type const&, RContext& rcontext, Attribute& attr) const + template Se, typename RContext, typename Attribute> + [[nodiscard]] constexpr bool + parse(It& first, Se const& last, unused_type const&, RContext& rcontext, Attribute& attr) const + noexcept(is_nothrow_parsable_v, RContext, Attribute>) { + static_assert(Parsable, RContext, Attribute>); + // It is perfectly fine to omit the expectation_failure context // even in non-throwing mode if and only if the skipper itself // is expectation-less. @@ -94,73 +121,126 @@ namespace boost { namespace spirit { namespace x3 // Anyways, we don't need to repack the expectation context // into our brand new skipper context, in contrast to the // repacking process done in `x3::skip_over`. - return this->subject.parse(first, last, - make_context(skipper), rcontext, attr); + return this->subject.parse( + first, last, x3::make_context(skipper_), rcontext, attr + ); } - template - bool parse(Iterator& first, Iterator const& last - , Context const& context, RContext& rcontext, Attribute& attr) const + template Se, typename Context, typename RContext, typename Attribute> + [[nodiscard]] constexpr bool + parse(It& first, Se const& last, Context const& context, RContext& rcontext, Attribute& attr) const + // never noexcept (requires expectation failure modification) { + static_assert(Parsable, RContext, Attribute>); + #if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE - return this->subject.parse(first, last, make_context(skipper, context), rcontext, attr); + return this->subject.parse(first, last, x3::make_context(skipper_, context), rcontext, attr); #else static_assert( !std::is_same_v, unused_type>, "Context type was not specified for x3::expectation_failure_tag. " "You probably forgot: `x3::with(failure)[p]`. " - "Note that you must also bind the context to your skipper."); + "Note that you must also bind the context to your skipper." + ); // This logic is heavily related to the instantiation chain; // see `x3::skip_over` for details. - auto const local_ctx = make_context(skipper, context); + auto const local_ctx = x3::make_context(skipper_, context); bool const r = this->subject.parse(first, last, local_ctx, rcontext, attr); - if (has_expectation_failure(local_ctx)) + if (x3::has_expectation_failure(local_ctx)) { - set_expectation_failure(get_expectation_failure(local_ctx), context); + x3::set_expectation_failure(x3::get_expectation_failure(local_ctx), context); } return r; #endif } - Skipper const skipper; + private: + Skipper skipper_; }; - struct reskip_gen + namespace detail { - template - struct skip_gen + template + struct skip_gen_impl { - constexpr skip_gen(Skipper const& skipper) - : skipper_(skipper) {} + // Unreference rvalue reference, but hold lvalue reference as-is + using skipper_type = std::conditional_t< + std::is_rvalue_reference_v, + std::remove_reference_t, + Skipper + >; - template - constexpr skip_directive::value_type, Skipper> - operator[](Subject const& subject) const + template + requires std::is_same_v, std::remove_cvref_t> + constexpr skip_gen_impl(SkipperT&& skipper) + noexcept(std::is_nothrow_constructible_v) + : skipper_(std::forward(skipper)) + {} + + template + [[nodiscard]] constexpr skip_directive, std::remove_cvref_t> + operator[](Subject&& subject) const + noexcept( + is_parser_nothrow_castable_v && + std::is_nothrow_constructible_v< + skip_directive, std::remove_cvref_t>, + as_parser_t, + skipper_type const& + > + ) { - return { as_parser(subject), skipper_ }; + return { as_parser(std::forward(subject)), skipper_ }; } - Skipper skipper_; + private: + skipper_type skipper_; }; - template - constexpr skip_gen const operator()(Skipper const& skipper) const + struct skip_gen { - return { skipper }; - } + template + [[nodiscard]] + static constexpr skip_gen_impl> + operator()(Skipper&& skipper) + noexcept( + is_parser_nothrow_castable_v && + std::is_nothrow_constructible_v>, as_parser_t> + ) + { + return { as_parser(std::forward(skipper)) }; + } - template - constexpr reskip_directive::value_type> - operator[](Subject const& subject) const + template + [[nodiscard, deprecated("Use `x3::reskip[p]`.")]] + /* static */ constexpr reskip_directive> + operator[](Subject&& subject) const // MSVC 2022 bug: cannot define `static operator[]` even in C++26 mode + noexcept(is_parser_nothrow_constructible_v>, Subject>) + { + return { as_parser(std::forward(subject)) }; + } + }; + + struct reskip_gen { - return { as_parser(subject) }; - } - }; + template + [[nodiscard]] + /* static */ constexpr reskip_directive> + operator[](Subject&& subject) const // MSVC 2022 bug: cannot define `static operator[]` even in C++26 mode + noexcept(is_parser_nothrow_constructible_v>, Subject>) + { + return { as_parser(std::forward(subject)) }; + } + }; + } // detail - constexpr auto skip = reskip_gen{}; -}}} + inline namespace cpos + { + inline constexpr detail::skip_gen skip{}; + inline constexpr detail::reskip_gen reskip{}; + } +} // boost::spirit::x3 #endif diff --git a/test/x3/skip.cpp b/test/x3/skip.cpp index 30abc86a2..7a07b1ef8 100644 --- a/test/x3/skip.cpp +++ b/test/x3/skip.cpp @@ -1,24 +1,25 @@ /*============================================================================= Copyright (c) 2001-2015 Joel de Guzman Copyright (c) 2013 Agustin Berge + Copyright (c) 2025 Nana Sakisaka Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ +#include "test.hpp" + #include #include -#include "test.hpp" -int -main() +int main() { using spirit_test::test; using spirit_test::test_attr; - using boost::spirit::x3::ascii::space; - using boost::spirit::x3::ascii::space_type; - using boost::spirit::x3::ascii::char_; - using boost::spirit::x3::ascii::alpha; + using boost::spirit::x3::standard::space; + using boost::spirit::x3::standard::space_type; + using boost::spirit::x3::standard::char_; + using boost::spirit::x3::standard::alpha; using boost::spirit::x3::lexeme; using boost::spirit::x3::skip; using boost::spirit::x3::lit; @@ -29,13 +30,15 @@ main() BOOST_TEST((test("a b c d", skip(space)[*char_]))); } - { // test attribute + { + // test attribute std::string s; BOOST_TEST((test_attr("a b c d", skip(space)[*char_], s))); BOOST_TEST(s == "abcd"); } - { // reskip + { + // reskip BOOST_TEST((test("ab c d", lexeme[lit('a') >> 'b' >> skip[lit('c') >> 'd']], space))); BOOST_TEST((test("abcd", lexeme[lit('a') >> 'b' >> skip[lit('c') >> 'd']], space))); BOOST_TEST(!(test("a bcd", lexeme[lit('a') >> 'b' >> skip[lit('c') >> 'd']], space)));