Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Match callback and indexed_callback interface #447

Merged
merged 2 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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