Skip to content
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
3 changes: 2 additions & 1 deletion include/match/and.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ namespace match {
template <matcher, matcher> struct or_t;

template <matcher L, matcher R> struct and_t : bin_op_t<and_t, "and", L, R> {
[[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);
}

Expand Down
3 changes: 2 additions & 1 deletion include/match/not.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ template <matcher M> 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);
}

Expand Down
4 changes: 3 additions & 1 deletion include/match/or.hpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

#pragma once

#include <match/bin_op.hpp>
Expand All @@ -14,7 +15,8 @@ namespace match {
template <matcher, matcher> struct and_t;

template <matcher L, matcher R> struct or_t : bin_op_t<or_t, "or", L, R> {
[[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);
}

Expand Down
7 changes: 7 additions & 0 deletions include/msg/callback.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ template <stdx::ct_string Name, typename Msg> struct callback_construct_t {
std::forward<F>(f));
}

template <stdx::callable F>
[[nodiscard]] constexpr auto operator()(F &&f) const {
using matcher_t = typename Msg::matcher_t;
return callback<Name, Msg, matcher_t, std::remove_cvref_t<F>>{
matcher_t{}, std::forward<F>(f)};
}

private:
template <typename N> struct matching_name {
template <typename Field>
Expand Down
4 changes: 4 additions & 0 deletions include/msg/field.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,10 @@ class field_t : public field_spec_t<Name, T, detail::field_size<Ats...>>,
field_t<Name, T, Default, msg::less_than_or_equal_to_t<field_t, V>,
Ats...>;

template <auto P>
using with_predicate =
field_t<Name, T, Default, msg::pred_matcher_t<field_t, P>, Ats...>;

// ======================================================================
// "const value" for construction and matching
template <T V>
Expand Down
55 changes: 41 additions & 14 deletions include/msg/field_matchers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,15 @@ template <typename RelOp> constexpr auto to_string() {
return "!="_ctst;
}
}

template <typename Field, typename Msg>
[[nodiscard]] constexpr static auto extract_field(Msg const &msg) {
if constexpr (stdx::range<Msg>) {
return Field::extract(msg);
} else {
return msg.get(Field{});
}
}
} // namespace detail

template <typename RelOp, typename Field, typename Field::type ExpectedValue>
Expand All @@ -138,7 +147,7 @@ struct rel_matcher_t {

template <typename MsgType>
[[nodiscard]] constexpr auto operator()(MsgType const &msg) const -> bool {
return RelOp{}(extract_field(msg), ExpectedValue);
return RelOp{}(detail::extract_field<Field>(msg), ExpectedValue);
}

[[nodiscard]] constexpr auto describe() const {
Expand All @@ -157,12 +166,12 @@ struct rel_matcher_t {
[[nodiscard]] constexpr auto describe_match(MsgType const &msg) const {
if constexpr (std::integral<typename Field::type>) {
return stdx::ct_format<"{} (0x{:x}) {} 0x{:x}">(
Field::name, extract_field(msg), detail::to_string<RelOp>(),
stdx::ct<ExpectedValue>());
Field::name, detail::extract_field<Field>(msg),
detail::to_string<RelOp>(), stdx::ct<ExpectedValue>());
} else {
return stdx::ct_format<"{} ({}) {} {}">(
Field::name, extract_field(msg), detail::to_string<RelOp>(),
stdx::ct<ExpectedValue>());
Field::name, detail::extract_field<Field>(msg),
detail::to_string<RelOp>(), stdx::ct<ExpectedValue>());
}
}

Expand All @@ -180,15 +189,6 @@ struct rel_matcher_t {
return ExpectedValue == OtherValue or
RelOp{}(ExpectedValue, OtherValue);
}

template <typename Msg>
[[nodiscard]] constexpr static auto extract_field(Msg const &msg) {
if constexpr (stdx::range<Msg>) {
return Field::extract(msg);
} else {
return msg.get(Field{});
}
}
};

template <typename Field, auto ExpectedValue>
Expand Down Expand Up @@ -341,4 +341,31 @@ template <typename Field>
using equal_default_t = equal_to_t<Field, Field::default_value>;
template <typename Field>
constexpr auto equal_default = equal_default_t<Field>{};

template <typename Field, auto P> struct pred_matcher_t {
using is_matcher = void;

template <typename MsgType>
[[nodiscard]] constexpr auto operator()(MsgType const &msg) const -> bool {
return P(detail::extract_field<Field>(msg));
}

[[nodiscard]] constexpr auto describe() const {
return stdx::ct_format<"<predicate>({})">(Field::name);
}

template <typename MsgType>
[[nodiscard]] constexpr auto describe_match(MsgType const &msg) const {
if constexpr (std::integral<typename Field::type>) {
return stdx::ct_format<"<predicate>({}(0x{:x}))">(
Field::name, detail::extract_field<Field>(msg));
} else {
return stdx::ct_format<"<predicate>({}({}))">(
Field::name, detail::extract_field<Field>(msg));
}
}
};

template <typename Field, auto P>
constexpr auto pred_matcher = pred_matcher_t<Field, P>{};
} // namespace msg
27 changes: 27 additions & 0 deletions test/msg/callback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<extended_msg_defn>{"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<field_pred>, field1,
field2, field3>;
auto callback = msg::callback<"cb", defn>([] {});
auto const msg_match = msg::owning<defn>{"id"_field = 0x80};
CHECK(callback.is_match(msg_match));
}
31 changes: 31 additions & 0 deletions test/msg/field_matchers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<decltype(m), msg::less_than_t<test_field, 6> const>);
}

TEST_CASE("predicate matcher description", "[field matchers]") {
using namespace stdx::literals;
constexpr auto m =
msg::pred_matcher_t<test_field, [](std::uint32_t) { return true; }>{};
constexpr auto desc = m.describe();
STATIC_REQUIRE(desc.str == "<predicate>(test_field)"_ctst);
}

TEST_CASE("predicate matcher description of match", "[field matchers]") {
using namespace stdx::literals;
using msg_data = std::array<std::uint32_t, 1>;

constexpr auto m =
msg::pred_matcher_t<test_field, [](std::uint32_t) { return true; }>{};
constexpr auto desc = m.describe_match(msg_data{0x01ff'ffff});
STATIC_REQUIRE(desc.str == "<predicate>(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<std::uint32_t, 1>;

constexpr auto m =
msg::pred_matcher_t<test_enum_field, [](auto) { return true; }>{};
constexpr auto desc = m.describe_match(msg_data{0x01ff'ffff});
STATIC_REQUIRE(desc.str == "<predicate>(enum_field({}))"_ctst);
STATIC_REQUIRE(desc.args == stdx::tuple{E::B});
}
Loading