diff --git a/include/msg/callback.hpp b/include/msg/callback.hpp index 550f53a4..3259330a 100644 --- a/include/msg/callback.hpp +++ b/include/msg/callback.hpp @@ -39,12 +39,12 @@ template struct extra_callback_args {}; * A Class that defines a message callback and provides methods for validating * and handling incoming messages. */ -template -struct callback, M, +template +struct callback, M, Callables...> { private: - decltype(std::declval() and typename MsgDefn::matcher_t{}) matcher; + M matcher; stdx::tuple callbacks; template @@ -59,8 +59,7 @@ struct callback, M, public: template constexpr explicit callback(M const &m, CBs &&...cbs) - : matcher{m and typename MsgDefn::matcher_t{}}, - callbacks{std::forward(cbs)...} {} + : matcher{m}, callbacks{std::forward(cbs)...} {} template [[nodiscard]] auto is_match(Msg const &m) const -> bool { @@ -89,10 +88,10 @@ struct callback, M, }; } // namespace detail -template +template constexpr auto callback = []( M const &m, CBs &&...callbacks) { - return detail::callback, M, std::remove_cvref_t...>{ m, std::forward(callbacks)...}; diff --git a/include/msg/field_matchers.hpp b/include/msg/field_matchers.hpp index c0bf6499..0bca5d23 100644 --- a/include/msg/field_matchers.hpp +++ b/include/msg/field_matchers.hpp @@ -103,6 +103,10 @@ template constexpr auto inverse_op() { return std::less_equal{}; } else if constexpr (std::same_as>) { return std::less{}; + } else if constexpr (std::same_as>) { + return std::not_equal_to{}; + } else if constexpr (std::same_as>) { + return std::equal_to{}; } } @@ -115,6 +119,10 @@ template constexpr auto to_string() { return ">"_sc; } else if constexpr (std::same_as>) { return ">="_sc; + } else if constexpr (std::same_as>) { + return "=="_sc; + } else if constexpr (std::same_as>) { + return "!="_sc; } } } // namespace detail @@ -198,82 +206,88 @@ tag_invoke(match::implies_t, greater_than_t const &, return X + ++inc >= Y; } -template struct equal_to_t { - using is_matcher = void; +template +using equal_to_t = rel_matcher_t, Field, T, ExpectedValue>; - using field_type = Field; - constexpr static auto expected_values = stdx::make_tuple(ExpectedValue); +template +[[nodiscard]] constexpr auto +tag_invoke(match::implies_t, equal_to_t const &, + rel_matcher_t const &) -> bool { + return RelOp{}(X, Y); +} - template - [[nodiscard]] constexpr auto operator()(MsgType const &msg) const -> bool { - return ExpectedValue == msg.get(Field{}); - } +template +constexpr auto tag_invoke(index_terms_t, equal_to_t const &, + stdx::callable auto const &f, std::size_t idx) + -> void { + f.template operator()(idx, X); +} - [[nodiscard]] constexpr auto describe() const { - if constexpr (std::is_integral_v) { - return format("{} == 0x{:x}"_sc, Field::name, - sc::int_(ExpectedValue)>); - } else { - return format("{} == {} (0x{:x})"_sc, Field::name, - sc::enum_, - sc::int_(ExpectedValue)>); - } - } +template +using not_equal_to_t = + rel_matcher_t, Field, T, ExpectedValue>; + +template +constexpr auto tag_invoke(index_not_terms_t, + not_equal_to_t const &, + stdx::callable auto const &f, std::size_t idx, + bool = false) -> void { + f.template operator()(idx, X); +} - template - [[nodiscard]] constexpr auto describe_match(MsgType const &msg) const { - if constexpr (std::is_integral_v) { - return format("{} (0x{:x}) == 0x{:x}"_sc, Field::name, - static_cast(msg.get(Field{})), - sc::int_(ExpectedValue)>); - } else { - return format("{} (0x{:x}) == {} (0x{:x})"_sc, Field::name, - static_cast(msg.get(Field{})), - sc::enum_, - sc::int_(ExpectedValue)>); - } +template +[[nodiscard]] constexpr auto tag_invoke(remove_terms_t, + equal_to_t const &m, + std::type_identity...) + -> match::matcher auto { + if constexpr ((std::is_same_v or ...)) { + return match::always; + } else { + return m; } +} - private: - friend constexpr auto tag_invoke(index_terms_t, equal_to_t const &, - stdx::callable auto const &f, - std::size_t idx) -> void { - f.template operator()(idx, ExpectedValue); +template +[[nodiscard]] constexpr auto tag_invoke(remove_terms_t, + not_equal_to_t const &m, + std::type_identity...) + -> match::matcher auto { + if constexpr ((std::is_same_v or ...)) { + return match::always; + } else { + return m; } +} - friend constexpr auto tag_invoke(index_not_terms_t, equal_to_t const &, - stdx::callable auto const &f, - std::size_t idx, bool negated) -> void { - if (negated) { - f.template operator()(idx, ExpectedValue); - } - } +template +[[nodiscard]] constexpr auto tag_invoke(match::implies_t, + less_than_t const &, + not_equal_to_t const &) + -> bool { + return X <= Y; +} - template - [[nodiscard]] friend constexpr auto - tag_invoke(remove_terms_t, equal_to_t const &m, - std::type_identity...) -> match::matcher auto { - if constexpr ((std::is_same_v or ...)) { - return match::always; - } else { - return m; - } - } +template +[[nodiscard]] constexpr auto tag_invoke(match::implies_t, + greater_than_t const &, + not_equal_to_t const &) + -> bool { + return X >= Y; +} - template - [[nodiscard]] friend constexpr auto - tag_invoke(match::implies_t, equal_to_t, - rel_matcher_t) -> bool { - return RelOp{}(ExpectedValue, OtherValue); - } +template +[[nodiscard]] constexpr auto +tag_invoke(match::implies_t, less_than_or_equal_to_t const &, + not_equal_to_t const &) -> bool { + return X < Y; +} - template - [[nodiscard]] friend constexpr auto - tag_invoke(match::implies_t, equal_to_t, - match::not_t>) -> bool { - return ExpectedValue != OtherValue; - } -}; +template +[[nodiscard]] constexpr auto +tag_invoke(match::implies_t, greater_than_or_equal_to_t const &, + not_equal_to_t const &) -> bool { + return X > Y; +} template using in_t = diff --git a/include/msg/message.hpp b/include/msg/message.hpp index 1c9ab114..860403ad 100644 --- a/include/msg/message.hpp +++ b/include/msg/message.hpp @@ -22,7 +22,6 @@ namespace msg { template struct msg_matcher : M { - template [[nodiscard]] constexpr auto operator()(Data const &d) const -> bool { auto view = typename Msg::view_t{d}; diff --git a/test/msg/callback.cpp b/test/msg/callback.cpp index 98085a4a..4905059a 100644 --- a/test/msg/callback.cpp +++ b/test/msg/callback.cpp @@ -23,6 +23,10 @@ using field3 = field<"f3", std::uint32_t>::located; using msg_defn = message<"msg", id_field::WithRequired<0x80>, field1, field2, field3>; +constexpr auto id_match = + msg::msg_matcher>{}; + std::string log_buffer{}; } // namespace @@ -31,26 +35,26 @@ inline auto logging::config<> = logging::fmt::config{std::back_inserter(log_buffer)}; TEST_CASE("callback matches message", "[handler]") { - auto callback = msg::callback<"cb", msg_defn>(match::always, [] {}); + auto callback = msg::callback<"cb">(id_match, [] {}); auto const msg_match = std::array{0x8000ba11u, 0x0042d00du}; CHECK(callback.is_match(msg_match)); } TEST_CASE("callback matches message (alternative range)", "[handler]") { - auto callback = msg::callback<"cb", msg_defn>(match::always, [] {}); + auto callback = msg::callback<"cb">(id_match, [] {}); auto const msg_match = std::array{0x11, 0xba, 0x00, 0x80, 0x0d, 0xd0, 0x42, 0x00}; CHECK(callback.is_match(msg_match)); } TEST_CASE("callback matches message (typed message)", "[handler]") { - auto callback = msg::callback<"cb", msg_defn>(match::always, [] {}); + auto callback = msg::callback<"cb">(id_match, [] {}); auto const msg_match = msg::owning{"id"_field = 0x80}; CHECK(callback.is_match(msg_match)); } TEST_CASE("callback logs mismatch (raw)", "[handler]") { - auto callback = msg::callback<"cb", msg_defn>(match::always, [] {}); + auto callback = msg::callback<"cb">(id_match, [] {}); auto const msg_nomatch = std::array{0x8100ba11u, 0x0042d00du}; CHECK(not callback.is_match(msg_nomatch)); @@ -61,7 +65,7 @@ TEST_CASE("callback logs mismatch (raw)", "[handler]") { } TEST_CASE("callback logs mismatch (typed)", "[handler]") { - auto callback = msg::callback<"cb", msg_defn>(match::always, [] {}); + auto callback = msg::callback<"cb">(id_match, [] {}); auto const msg_nomatch = msg::owning{"id"_field = 0x81}; CHECK(not callback.is_match(msg_nomatch)); @@ -72,8 +76,8 @@ TEST_CASE("callback logs mismatch (typed)", "[handler]") { } TEST_CASE("callback handles message (raw)", "[handler]") { - auto callback = msg::callback<"cb", msg_defn>( - match::always, [](msg::const_view) { dispatched = true; }); + auto callback = msg::callback<"cb">( + id_match, [](msg::const_view) { dispatched = true; }); auto const msg_match = std::array{0x8000ba11u, 0x0042d00du}; dispatched = false; @@ -82,8 +86,8 @@ TEST_CASE("callback handles message (raw)", "[handler]") { } TEST_CASE("callback handles message (typed)", "[handler]") { - auto callback = msg::callback<"cb", msg_defn>( - match::always, [](msg::const_view) { dispatched = true; }); + auto callback = msg::callback<"cb">( + id_match, [](msg::const_view) { dispatched = true; }); auto const msg_match = msg::owning{"id"_field = 0x80}; dispatched = false; @@ -92,8 +96,8 @@ TEST_CASE("callback handles message (typed)", "[handler]") { } TEST_CASE("callback logs match", "[handler]") { - auto callback = msg::callback<"cb", msg_defn>( - match::always, [](msg::const_view) { dispatched = true; }); + auto callback = msg::callback<"cb">( + id_match, [](msg::const_view) { dispatched = true; }); auto const msg_match = std::array{0x8000ba11u, 0x0042d00du}; log_buffer.clear(); diff --git a/test/msg/field_matchers.cpp b/test/msg/field_matchers.cpp index 2ee8f95b..b1fee9d4 100644 --- a/test/msg/field_matchers.cpp +++ b/test/msg/field_matchers.cpp @@ -44,6 +44,22 @@ TEST_CASE("negate greater_than_or_equal_to", "[field matchers]") { msg::less_than_t const>); } +TEST_CASE("negate equal_to", "[field matchers]") { + constexpr auto m = msg::equal_to_t{}; + constexpr auto n = match::negate(m); + static_assert(std::is_same_v< + decltype(n), + msg::not_equal_to_t const>); +} + +TEST_CASE("negate not_equal_to", "[field matchers]") { + constexpr auto m = msg::not_equal_to_t{}; + constexpr auto n = match::negate(m); + static_assert( + std::is_same_v const>); +} + TEST_CASE("less_than X implies less_than Y (X <= Y)", "[field matchers]") { constexpr auto m = msg::less_than_t{}; constexpr auto n = msg::less_than_t{}; @@ -152,3 +168,40 @@ TEST_CASE("equal_to X and less_than Y is equal_to X (X < Y)", std::is_same_v const>); } + +TEST_CASE("less_than X implies not_equal_to X", "[field matchers]") { + constexpr auto m = msg::less_than_t{}; + constexpr auto n = msg::not_equal_to_t{}; + static_assert(match::implies(m, n)); +} + +TEST_CASE("greater_than X implies not_equal_to X", "[field matchers]") { + constexpr auto m = msg::greater_than_t{}; + constexpr auto n = msg::not_equal_to_t{}; + static_assert(match::implies(m, n)); +} + +TEST_CASE("less_than_or_equal_to X implies not_equal_to X", + "[field matchers]") { + constexpr auto m = + msg::less_than_or_equal_to_t{}; + constexpr auto n = msg::not_equal_to_t{}; + static_assert(match::implies(m, n)); +} + +TEST_CASE("greater_than_or_equal_to X implies not_equal_to X", + "[field matchers]") { + constexpr auto m = + msg::greater_than_or_equal_to_t{}; + constexpr auto n = msg::not_equal_to_t{}; + static_assert(match::implies(m, n)); +} + +TEST_CASE("not_equal_to X and less_than Y is less_than Y (X >= Y)", + "[field matchers]") { + constexpr auto m = msg::not_equal_to_t{} and + msg::less_than_t{}; + static_assert( + std::is_same_v const>); +} diff --git a/test/msg/handler.cpp b/test/msg/handler.cpp index 54c0c97b..4b4b769c 100644 --- a/test/msg/handler.cpp +++ b/test/msg/handler.cpp @@ -23,11 +23,11 @@ using field1 = field<"f1", std::uint32_t>::located; using field2 = field<"f2", std::uint32_t>::located; using field3 = field<"f3", std::uint32_t>::located; -using msg_defn = - message<"msg", id_field::WithRequired<0x80>, field1, field2, field3>; +using msg_defn = message<"msg", id_field, field1, field2, field3>; -using msg_defn_field_reqd = - message<"msg", id_field::WithRequired<0x44>, field1, field2, field3>; +template +constexpr auto id_match = + msg::msg_matcher>{}; std::string log_buffer{}; } // namespace @@ -37,8 +37,8 @@ inline auto logging::config<> = logging::fmt::config{std::back_inserter(log_buffer)}; TEST_CASE("is_match is true for a match", "[handler]") { - auto callback = msg::callback<"cb", msg_defn>( - match::always, [](msg::const_view) { dispatched = true; }); + auto callback = msg::callback<"cb">( + id_match<0x80>, [](msg::const_view) { dispatched = true; }); auto const msg = std::array{0x8000ba11u, 0x0042d00du}; auto callbacks = stdx::make_tuple(callback); @@ -47,8 +47,8 @@ TEST_CASE("is_match is true for a match", "[handler]") { } TEST_CASE("dispatch single callback (match, raw data)", "[handler]") { - auto callback = msg::callback<"cb", msg_defn>( - match::always, [](msg::const_view) { dispatched = true; }); + auto callback = msg::callback<"cb">( + id_match<0x80>, [](msg::const_view) { dispatched = true; }); auto const msg = std::array{0x8000ba11u, 0x0042d00du}; auto callbacks = stdx::make_tuple(callback); @@ -59,8 +59,8 @@ TEST_CASE("dispatch single callback (match, raw data)", "[handler]") { } TEST_CASE("dispatch single callback (match, typed data)", "[handler]") { - auto callback = msg::callback<"cb", msg_defn>( - match::always, [](msg::const_view) { dispatched = true; }); + auto callback = msg::callback<"cb">( + id_match<0x80>, [](msg::const_view) { dispatched = true; }); auto const msg = msg::owning{"id"_field = 0x80}; auto callbacks = stdx::make_tuple(callback); @@ -71,8 +71,8 @@ TEST_CASE("dispatch single callback (match, typed data)", "[handler]") { } TEST_CASE("dispatch single callback (no match)", "[handler]") { - auto callback = msg::callback<"cb", msg_defn>( - match::always, [](msg::const_view) { dispatched = true; }); + auto callback = msg::callback<"cb">( + id_match<0x80>, [](msg::const_view) { dispatched = true; }); auto const msg = std::array{0x8100ba11u, 0x0042d00du}; auto callbacks = stdx::make_tuple(callback); @@ -94,11 +94,10 @@ TEST_CASE("log mismatch when no match", "[handler]") { } TEST_CASE("match and dispatch only one callback", "[handler]") { - auto callback1 = msg::callback<"cb1", msg_defn>( - match::always, [&](msg::const_view) { CHECK(false); }); - auto callback2 = msg::callback<"cb2", msg_defn_field_reqd>( - match::always, - [](msg::const_view) { dispatched = true; }); + auto callback1 = msg::callback<"cb1">( + id_match<0x80>, [&](msg::const_view) { CHECK(false); }); + auto callback2 = msg::callback<"cb2">( + id_match<0x44>, [](msg::const_view) { dispatched = true; }); auto const msg = std::array{0x4400ba11u, 0x0042d00du}; auto callbacks = stdx::make_tuple(callback1, callback2); @@ -111,8 +110,8 @@ TEST_CASE("match and dispatch only one callback", "[handler]") { } TEST_CASE("dispatch with extra args", "[handler]") { - auto callback = msg::callback<"cb", msg_defn, int>( - match::always, [](msg::const_view, int value) { + auto callback = msg::callback<"cb", int>( + id_match<0x80>, [](msg::const_view, int value) { dispatched = true; CHECK(value == 0xcafe); }); diff --git a/test/msg/handler_builder.cpp b/test/msg/handler_builder.cpp index 5a7326fb..3da0c063 100644 --- a/test/msg/handler_builder.cpp +++ b/test/msg/handler_builder.cpp @@ -19,17 +19,20 @@ using field1 = field<"f1", std::uint32_t>::located; using field2 = field<"f2", std::uint32_t>::located; using field3 = field<"f3", std::uint32_t>::located; -using msg_defn = - message<"msg", id_field::WithRequired<0x80>, field1, field2, field3>; +using msg_defn = message<"msg", id_field, field1, field2, field3>; using test_msg_t = msg::owning; using msg_view_t = msg::const_view; +constexpr auto id_match = + msg::msg_matcher>{}; + struct test_service : msg::service {}; bool callback_success; -constexpr auto test_callback = msg::callback<"cb", msg_defn>( - match::always, [](msg_view_t) { callback_success = true; }); +constexpr auto test_callback = + msg::callback<"cb">(id_match, [](msg_view_t) { callback_success = true; }); struct test_project { constexpr static auto config = cib::config( diff --git a/test/msg/indexed_builder.cpp b/test/msg/indexed_builder.cpp index 86023a5d..b2827596 100644 --- a/test/msg/indexed_builder.cpp +++ b/test/msg/indexed_builder.cpp @@ -158,7 +158,7 @@ TEST_CASE("build handler multi fields", "[indexed_builder]") { CHECK(not callback_success); CHECK(not callback_success_single_field); - // make sure an unconstrained field in a callback doesn't cause a mismatch + // an unconstrained field in a callback doesn't cause a mismatch log_buffer.clear(); callback_success = false; callback_success_single_field = false; diff --git a/test/msg/rle_indexed_builder.cpp b/test/msg/rle_indexed_builder.cpp index 37b86fcd..b606e908 100644 --- a/test/msg/rle_indexed_builder.cpp +++ b/test/msg/rle_indexed_builder.cpp @@ -155,7 +155,7 @@ TEST_CASE("build rle handler multi fields", "[rle_indexed_builder]") { CHECK(not callback_success); CHECK(not callback_success_single_field); - // make sure an unconstrained field in a callback doesn't cause a mismatch + // an unconstrained field in a callback doesn't cause a mismatch callback_success = false; callback_success_single_field = false; cib::service->handle(test_msg_t{