Skip to content

Commit

Permalink
Merge pull request #447 from elbeno/callback-matcher
Browse files Browse the repository at this point in the history
Match callback and indexed_callback interface
  • Loading branch information
lukevalenty authored Dec 15, 2023
2 parents 50012d6 + 6fcff48 commit 822b06d
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 110 deletions.
15 changes: 7 additions & 8 deletions include/msg/callback.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ template <typename...> struct extra_callback_args {};
* A Class that defines a message callback and provides methods for validating
* and handling incoming messages.
*/
template <stdx::ct_string Name, typename MsgDefn, typename... ExtraCallbackArgs,
match::matcher M, detail::not_nullptr... Callables>
struct callback<Name, MsgDefn, extra_callback_args<ExtraCallbackArgs...>, M,
template <stdx::ct_string Name, typename... ExtraCallbackArgs, match::matcher M,
detail::not_nullptr... Callables>
struct callback<Name, extra_callback_args<ExtraCallbackArgs...>, M,
Callables...> {
private:
decltype(std::declval<M>() and typename MsgDefn::matcher_t{}) matcher;
M matcher;
stdx::tuple<Callables...> callbacks;

template <typename Msg>
Expand All @@ -59,8 +59,7 @@ struct callback<Name, MsgDefn, extra_callback_args<ExtraCallbackArgs...>, M,
public:
template <typename... CBs>
constexpr explicit callback(M const &m, CBs &&...cbs)
: matcher{m and typename MsgDefn::matcher_t{}},
callbacks{std::forward<CBs>(cbs)...} {}
: matcher{m}, callbacks{std::forward<CBs>(cbs)...} {}

template <typename Msg>
[[nodiscard]] auto is_match(Msg const &m) const -> bool {
Expand Down Expand Up @@ -89,10 +88,10 @@ struct callback<Name, MsgDefn, extra_callback_args<ExtraCallbackArgs...>, M,
};
} // namespace detail

template <stdx::ct_string Name, typename MsgDefn, typename... ExtraCallbackArgs>
template <stdx::ct_string Name, typename... ExtraCallbackArgs>
constexpr auto callback = []<match::matcher M, typename... CBs>(
M const &m, CBs &&...callbacks) {
return detail::callback<Name, MsgDefn,
return detail::callback<Name,
detail::extra_callback_args<ExtraCallbackArgs...>,
M, std::remove_cvref_t<CBs>...>{
m, std::forward<CBs>(callbacks)...};
Expand Down
144 changes: 79 additions & 65 deletions include/msg/field_matchers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ template <typename RelOp> constexpr auto inverse_op() {
return std::less_equal{};
} else if constexpr (std::same_as<RelOp, std::greater_equal<>>) {
return std::less{};
} else if constexpr (std::same_as<RelOp, std::equal_to<>>) {
return std::not_equal_to{};
} else if constexpr (std::same_as<RelOp, std::not_equal_to<>>) {
return std::equal_to{};
}
}

Expand All @@ -115,6 +119,10 @@ template <typename RelOp> constexpr auto to_string() {
return ">"_sc;
} else if constexpr (std::same_as<RelOp, std::greater_equal<>>) {
return ">="_sc;
} else if constexpr (std::same_as<RelOp, std::equal_to<>>) {
return "=="_sc;
} else if constexpr (std::same_as<RelOp, std::not_equal_to<>>) {
return "!="_sc;
}
}
} // namespace detail
Expand Down Expand Up @@ -198,82 +206,88 @@ tag_invoke(match::implies_t, greater_than_t<Field, T, X> const &,
return X + ++inc >= Y;
}

template <typename Field, typename T, T ExpectedValue> struct equal_to_t {
using is_matcher = void;
template <typename Field, typename T, T ExpectedValue>
using equal_to_t = rel_matcher_t<std::equal_to<>, Field, T, ExpectedValue>;

using field_type = Field;
constexpr static auto expected_values = stdx::make_tuple(ExpectedValue);
template <typename Field, typename T, T X, typename RelOp, T Y>
[[nodiscard]] constexpr auto
tag_invoke(match::implies_t, equal_to_t<Field, T, X> const &,
rel_matcher_t<RelOp, Field, T, Y> const &) -> bool {
return RelOp{}(X, Y);
}

template <typename MsgType>
[[nodiscard]] constexpr auto operator()(MsgType const &msg) const -> bool {
return ExpectedValue == msg.get(Field{});
}
template <typename Field, typename T, T X>
constexpr auto tag_invoke(index_terms_t, equal_to_t<Field, T, X> const &,
stdx::callable auto const &f, std::size_t idx)
-> void {
f.template operator()<Field>(idx, X);
}

[[nodiscard]] constexpr auto describe() const {
if constexpr (std::is_integral_v<T>) {
return format("{} == 0x{:x}"_sc, Field::name,
sc::int_<static_cast<std::uint32_t>(ExpectedValue)>);
} else {
return format("{} == {} (0x{:x})"_sc, Field::name,
sc::enum_<ExpectedValue>,
sc::int_<static_cast<std::uint32_t>(ExpectedValue)>);
}
}
template <typename Field, typename T, T ExpectedValue>
using not_equal_to_t =
rel_matcher_t<std::not_equal_to<>, Field, T, ExpectedValue>;

template <typename Field, typename T, T X>
constexpr auto tag_invoke(index_not_terms_t,
not_equal_to_t<Field, T, X> const &,
stdx::callable auto const &f, std::size_t idx,
bool = false) -> void {
f.template operator()<Field>(idx, X);
}

template <typename MsgType>
[[nodiscard]] constexpr auto describe_match(MsgType const &msg) const {
if constexpr (std::is_integral_v<T>) {
return format("{} (0x{:x}) == 0x{:x}"_sc, Field::name,
static_cast<std::uint32_t>(msg.get(Field{})),
sc::int_<static_cast<std::uint32_t>(ExpectedValue)>);
} else {
return format("{} (0x{:x}) == {} (0x{:x})"_sc, Field::name,
static_cast<std::uint32_t>(msg.get(Field{})),
sc::enum_<ExpectedValue>,
sc::int_<static_cast<std::uint32_t>(ExpectedValue)>);
}
template <typename Field, typename T, T X, typename... Fields>
[[nodiscard]] constexpr auto tag_invoke(remove_terms_t,
equal_to_t<Field, T, X> const &m,
std::type_identity<Fields>...)
-> match::matcher auto {
if constexpr ((std::is_same_v<Field, Fields> 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()<Field>(idx, ExpectedValue);
template <typename Field, typename T, T X, typename... Fields>
[[nodiscard]] constexpr auto tag_invoke(remove_terms_t,
not_equal_to_t<Field, T, X> const &m,
std::type_identity<Fields>...)
-> match::matcher auto {
if constexpr ((std::is_same_v<Field, Fields> 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()<Field>(idx, ExpectedValue);
}
}
template <typename Field, typename T, T X, T Y>
[[nodiscard]] constexpr auto tag_invoke(match::implies_t,
less_than_t<Field, T, X> const &,
not_equal_to_t<Field, T, Y> const &)
-> bool {
return X <= Y;
}

template <typename... Fields>
[[nodiscard]] friend constexpr auto
tag_invoke(remove_terms_t, equal_to_t const &m,
std::type_identity<Fields>...) -> match::matcher auto {
if constexpr ((std::is_same_v<Field, Fields> or ...)) {
return match::always;
} else {
return m;
}
}
template <typename Field, typename T, T X, T Y>
[[nodiscard]] constexpr auto tag_invoke(match::implies_t,
greater_than_t<Field, T, X> const &,
not_equal_to_t<Field, T, Y> const &)
-> bool {
return X >= Y;
}

template <typename RelOp, T OtherValue>
[[nodiscard]] friend constexpr auto
tag_invoke(match::implies_t, equal_to_t,
rel_matcher_t<RelOp, Field, T, OtherValue>) -> bool {
return RelOp{}(ExpectedValue, OtherValue);
}
template <typename Field, typename T, T X, T Y>
[[nodiscard]] constexpr auto
tag_invoke(match::implies_t, less_than_or_equal_to_t<Field, T, X> const &,
not_equal_to_t<Field, T, Y> const &) -> bool {
return X < Y;
}

template <T OtherValue>
[[nodiscard]] friend constexpr auto
tag_invoke(match::implies_t, equal_to_t,
match::not_t<equal_to_t<Field, T, OtherValue>>) -> bool {
return ExpectedValue != OtherValue;
}
};
template <typename Field, typename T, T X, T Y>
[[nodiscard]] constexpr auto
tag_invoke(match::implies_t, greater_than_or_equal_to_t<Field, T, X> const &,
not_equal_to_t<Field, T, Y> const &) -> bool {
return X > Y;
}

template <typename Field, typename T, T... ExpectedValues>
using in_t =
Expand Down
1 change: 0 additions & 1 deletion include/msg/message.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

namespace msg {
template <typename Msg, match::matcher M> struct msg_matcher : M {

template <typename Data>
[[nodiscard]] constexpr auto operator()(Data const &d) const -> bool {
auto view = typename Msg::view_t{d};
Expand Down
26 changes: 15 additions & 11 deletions test/msg/callback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ using field3 = field<"f3", std::uint32_t>::located<at{1_dw, 15_msb, 0_lsb}>;
using msg_defn =
message<"msg", id_field::WithRequired<0x80>, field1, field2, field3>;

constexpr auto id_match =
msg::msg_matcher<msg_defn,
msg::equal_to_t<id_field, std::uint32_t, 0x80>>{};

std::string log_buffer{};
} // namespace

Expand All @@ -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<std::uint8_t, 8>{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<msg_defn>{"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));

Expand All @@ -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<msg_defn>{"id"_field = 0x81};
CHECK(not callback.is_match(msg_nomatch));

Expand All @@ -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<msg_defn>) { dispatched = true; });
auto callback = msg::callback<"cb">(
id_match, [](msg::const_view<msg_defn>) { dispatched = true; });
auto const msg_match = std::array{0x8000ba11u, 0x0042d00du};

dispatched = false;
Expand All @@ -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<msg_defn>) { dispatched = true; });
auto callback = msg::callback<"cb">(
id_match, [](msg::const_view<msg_defn>) { dispatched = true; });
auto const msg_match = msg::owning<msg_defn>{"id"_field = 0x80};

dispatched = false;
Expand All @@ -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<msg_defn>) { dispatched = true; });
auto callback = msg::callback<"cb">(
id_match, [](msg::const_view<msg_defn>) { dispatched = true; });
auto const msg_match = std::array{0x8000ba11u, 0x0042d00du};

log_buffer.clear();
Expand Down
53 changes: 53 additions & 0 deletions test/msg/field_matchers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,22 @@ TEST_CASE("negate greater_than_or_equal_to", "[field matchers]") {
msg::less_than_t<test_field, std::uint32_t, 5> const>);
}

TEST_CASE("negate equal_to", "[field matchers]") {
constexpr auto m = msg::equal_to_t<test_field, std::uint32_t, 5>{};
constexpr auto n = match::negate(m);
static_assert(std::is_same_v<
decltype(n),
msg::not_equal_to_t<test_field, std::uint32_t, 5> const>);
}

TEST_CASE("negate not_equal_to", "[field matchers]") {
constexpr auto m = msg::not_equal_to_t<test_field, std::uint32_t, 5>{};
constexpr auto n = match::negate(m);
static_assert(
std::is_same_v<decltype(n),
msg::equal_to_t<test_field, std::uint32_t, 5> const>);
}

TEST_CASE("less_than X implies less_than Y (X <= Y)", "[field matchers]") {
constexpr auto m = msg::less_than_t<test_field, std::uint32_t, 5>{};
constexpr auto n = msg::less_than_t<test_field, std::uint32_t, 6>{};
Expand Down Expand Up @@ -152,3 +168,40 @@ TEST_CASE("equal_to X and less_than Y is equal_to X (X < Y)",
std::is_same_v<decltype(m),
msg::equal_to_t<test_field, std::uint32_t, 5> const>);
}

TEST_CASE("less_than X implies not_equal_to X", "[field matchers]") {
constexpr auto m = msg::less_than_t<test_field, std::uint32_t, 5>{};
constexpr auto n = msg::not_equal_to_t<test_field, std::uint32_t, 5>{};
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<test_field, std::uint32_t, 5>{};
constexpr auto n = msg::not_equal_to_t<test_field, std::uint32_t, 5>{};
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<test_field, std::uint32_t, 5>{};
constexpr auto n = msg::not_equal_to_t<test_field, std::uint32_t, 6>{};
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<test_field, std::uint32_t, 6>{};
constexpr auto n = msg::not_equal_to_t<test_field, std::uint32_t, 5>{};
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<test_field, std::uint32_t, 6>{} and
msg::less_than_t<test_field, std::uint32_t, 6>{};
static_assert(
std::is_same_v<decltype(m),
msg::less_than_t<test_field, std::uint32_t, 6> const>);
}
Loading

0 comments on commit 822b06d

Please sign in to comment.