diff --git a/include/match/and.hpp b/include/match/and.hpp index 739ebf26..1c1c9200 100644 --- a/include/match/and.hpp +++ b/include/match/and.hpp @@ -15,7 +15,8 @@ namespace match { template struct or_t; template struct and_t : bin_op_t { - [[nodiscard]] constexpr auto operator()(auto const &event) const -> bool { + [[nodiscard]] constexpr auto operator()(auto const &event) const + -> decltype(this->lhs(event) and this->rhs(event)) { return this->lhs(event) and this->rhs(event); } diff --git a/include/match/not.hpp b/include/match/not.hpp index 98b251f5..98835c60 100644 --- a/include/match/not.hpp +++ b/include/match/not.hpp @@ -20,7 +20,8 @@ template struct not_t { using is_matcher = void; [[no_unique_address]] M m; - [[nodiscard]] constexpr auto operator()(auto const &event) const -> bool { + [[nodiscard]] constexpr auto operator()(auto const &event) const + -> decltype(not m(event)) { return not m(event); } diff --git a/include/match/or.hpp b/include/match/or.hpp index 9ab54ed4..3837b7f3 100644 --- a/include/match/or.hpp +++ b/include/match/or.hpp @@ -1,3 +1,4 @@ + #pragma once #include @@ -14,7 +15,8 @@ namespace match { template struct and_t; template struct or_t : bin_op_t { - [[nodiscard]] constexpr auto operator()(auto const &event) const -> bool { + [[nodiscard]] constexpr auto operator()(auto const &event) const + -> decltype(this->lhs(event) or this->rhs(event)) { return this->lhs(event) or this->rhs(event); } diff --git a/include/msg/callback.hpp b/include/msg/callback.hpp index 398005ed..05a65fa7 100644 --- a/include/msg/callback.hpp +++ b/include/msg/callback.hpp @@ -86,6 +86,13 @@ template struct callback_construct_t { std::forward(f)); } + template + [[nodiscard]] constexpr auto operator()(F &&f) const { + using matcher_t = typename Msg::matcher_t; + return callback>{ + matcher_t{}, std::forward(f)}; + } + private: template struct matching_name { template diff --git a/include/msg/field.hpp b/include/msg/field.hpp index d67b6f6f..1947e1d5 100644 --- a/include/msg/field.hpp +++ b/include/msg/field.hpp @@ -527,6 +527,10 @@ class field_t : public field_spec_t>, field_t, Ats...>; + template + using with_predicate = + field_t, Ats...>; + // ====================================================================== // "const value" for construction and matching template diff --git a/include/msg/field_matchers.hpp b/include/msg/field_matchers.hpp index 53b2d424..e86c1bb8 100644 --- a/include/msg/field_matchers.hpp +++ b/include/msg/field_matchers.hpp @@ -130,6 +130,15 @@ template constexpr auto to_string() { return "!="_ctst; } } + +template +[[nodiscard]] constexpr static auto extract_field(Msg const &msg) { + if constexpr (stdx::range) { + return Field::extract(msg); + } else { + return msg.get(Field{}); + } +} } // namespace detail template @@ -138,7 +147,7 @@ struct rel_matcher_t { template [[nodiscard]] constexpr auto operator()(MsgType const &msg) const -> bool { - return RelOp{}(extract_field(msg), ExpectedValue); + return RelOp{}(detail::extract_field(msg), ExpectedValue); } [[nodiscard]] constexpr auto describe() const { @@ -157,12 +166,12 @@ struct rel_matcher_t { [[nodiscard]] constexpr auto describe_match(MsgType const &msg) const { if constexpr (std::integral) { return stdx::ct_format<"{} (0x{:x}) {} 0x{:x}">( - Field::name, extract_field(msg), detail::to_string(), - stdx::ct()); + Field::name, detail::extract_field(msg), + detail::to_string(), stdx::ct()); } else { return stdx::ct_format<"{} ({}) {} {}">( - Field::name, extract_field(msg), detail::to_string(), - stdx::ct()); + Field::name, detail::extract_field(msg), + detail::to_string(), stdx::ct()); } } @@ -180,15 +189,6 @@ struct rel_matcher_t { return ExpectedValue == OtherValue or RelOp{}(ExpectedValue, OtherValue); } - - template - [[nodiscard]] constexpr static auto extract_field(Msg const &msg) { - if constexpr (stdx::range) { - return Field::extract(msg); - } else { - return msg.get(Field{}); - } - } }; template @@ -341,4 +341,31 @@ template using equal_default_t = equal_to_t; template constexpr auto equal_default = equal_default_t{}; + +template struct pred_matcher_t { + using is_matcher = void; + + template + [[nodiscard]] constexpr auto operator()(MsgType const &msg) const -> bool { + return P(detail::extract_field(msg)); + } + + [[nodiscard]] constexpr auto describe() const { + return stdx::ct_format<"({})">(Field::name); + } + + template + [[nodiscard]] constexpr auto describe_match(MsgType const &msg) const { + if constexpr (std::integral) { + return stdx::ct_format<"({}(0x{:x}))">( + Field::name, detail::extract_field(msg)); + } else { + return stdx::ct_format<"({}({}))">( + Field::name, detail::extract_field(msg)); + } + } +}; + +template +constexpr auto pred_matcher = pred_matcher_t{}; } // namespace msg diff --git a/test/msg/callback.cpp b/test/msg/callback.cpp index f43529ef..e5684188 100644 --- a/test/msg/callback.cpp +++ b/test/msg/callback.cpp @@ -344,3 +344,30 @@ TEST_CASE("alternative matcher syntax (or-combine matcher with matcher maker)", CHECK(cb2.handle(msg_match)); CHECK(dispatched); } + +namespace { +using extended_msg_defn = + message<"msg", id_field::with_equal_to<0x80>, field1, field2, field3>; + +constexpr auto pred = match::predicate([](msg::viewlike auto) { return true; }); +} // namespace + +TEST_CASE("callback matches message by predicates with different constraints", + "[callback]") { + auto callback = msg::callback<"cb", extended_msg_defn>(pred, [] {}); + auto const msg_match = msg::owning{"id"_field = 0x80}; + CHECK(callback.is_match(msg_match)); +} + +namespace { +constexpr auto field_pred = [](std::uint32_t id) { return id == 0x80; }; +} // namespace + +TEST_CASE("callback matches message by custom predicate on field", + "[callback]") { + using defn = message<"msg", id_field::with_predicate, field1, + field2, field3>; + auto callback = msg::callback<"cb", defn>([] {}); + auto const msg_match = msg::owning{"id"_field = 0x80}; + CHECK(callback.is_match(msg_match)); +} diff --git a/test/msg/field_matchers.cpp b/test/msg/field_matchers.cpp index e94dbceb..787e76c0 100644 --- a/test/msg/field_matchers.cpp +++ b/test/msg/field_matchers.cpp @@ -236,3 +236,34 @@ TEST_CASE("not_equal_to X and less_than Y is less_than Y (X >= Y)", STATIC_REQUIRE( std::is_same_v const>); } + +TEST_CASE("predicate matcher description", "[field matchers]") { + using namespace stdx::literals; + constexpr auto m = + msg::pred_matcher_t{}; + constexpr auto desc = m.describe(); + STATIC_REQUIRE(desc.str == "(test_field)"_ctst); +} + +TEST_CASE("predicate matcher description of match", "[field matchers]") { + using namespace stdx::literals; + using msg_data = std::array; + + constexpr auto m = + msg::pred_matcher_t{}; + constexpr auto desc = m.describe_match(msg_data{0x01ff'ffff}); + STATIC_REQUIRE(desc.str == "(test_field(0x{:x}))"_ctst); + STATIC_REQUIRE(desc.args == stdx::tuple{1}); +} + +TEST_CASE("predicate matcher description of match (enum field)", + "[field matchers]") { + using namespace stdx::literals; + using msg_data = std::array; + + constexpr auto m = + msg::pred_matcher_t{}; + constexpr auto desc = m.describe_match(msg_data{0x01ff'ffff}); + STATIC_REQUIRE(desc.str == "(enum_field({}))"_ctst); + STATIC_REQUIRE(desc.args == stdx::tuple{E::B}); +}