From aba9fc19f46aad19063d886e275cc2cfc8fd1875 Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Wed, 5 Mar 2025 09:08:03 -0700 Subject: [PATCH 1/2] :sparkles: Add environments for message definitions --- CMakeLists.txt | 2 +- include/msg/message.hpp | 113 +++++++++++++++++++++-------------- test/msg/message.cpp | 56 ++++++++++++++++- test/msg/relaxed_message.cpp | 25 ++++++++ 4 files changed, 150 insertions(+), 46 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 660d660a..c21291e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ include(cmake/string_catalog.cmake) add_versioned_package("gh:boostorg/mp11#boost-1.83.0") fmt_recipe(11.1.3) add_versioned_package("gh:intel/cpp-baremetal-concurrency#7c5b26c") -add_versioned_package("gh:intel/cpp-std-extensions#ec95cd6") +add_versioned_package("gh:intel/cpp-std-extensions#7725142") add_versioned_package("gh:intel/cpp-baremetal-senders-and-receivers#73d95bc") set(GEN_STR_CATALOG diff --git a/include/msg/message.hpp b/include/msg/message.hpp index e4da17f0..ac8704fe 100644 --- a/include/msg/message.hpp +++ b/include/msg/message.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include @@ -197,7 +197,7 @@ template struct storage_size { template using name_for = typename F::name_t; -template class message_access { +template class msg_access { using FieldsTuple = decltype(stdx::make_indexed_tuple(Fields{}...)); @@ -295,10 +295,11 @@ concept storage_like = stdx::range and requires { typename T::value_type; }; -template struct message; +template +struct message; -template struct msg_q { - template using fn = message; +template struct msg_q { + template using fn = message; }; template @@ -312,9 +313,9 @@ using unique_by_name = boost::mp11::mp_unique_if< boost::mp11::mp_sort, field_sort_fn>, name_equal_fn>; -template +template using message_without_unique_field_names = - boost::mp11::mp_apply_q, + boost::mp11::mp_apply_q, detail::unique_by_name>; template @@ -323,12 +324,45 @@ struct message_with_unique_field_names { name_for, boost::mp11::mp_list>>::value, "Message contains fields with duplicate names"); - using type = message_without_unique_field_names; + using type = + message_without_unique_field_names, Fields...>; +}; + +template +struct message_with_unique_field_names { + static_assert(boost::mp11::mp_is_set>>::value, + "Message contains fields with duplicate names"); + + using type = message_without_unique_field_names; }; -template struct message { +template struct msg_base { + constexpr static auto name = Name; + + constexpr auto as_derived() const -> T const & { + return static_cast(*this); + } + constexpr auto as_derived() -> T & { return static_cast(*this); } + + [[nodiscard]] constexpr auto get(auto f) const { + return Access::get(as_derived().data(), f); + } + constexpr auto set(auto... fs) -> void { + Access::set(as_derived().data(), fs...); + } + constexpr auto set() -> void {} + + [[nodiscard]] constexpr auto describe() const { + return Access::describe(as_derived().data()); + } +}; + +template +struct message { using fields_t = stdx::type_list; - using access_t = detail::message_access; + using env_t = Env; + using access_t = msg_access; using default_storage_t = typename access_t::default_storage_t; using default_span_t = typename access_t::default_span_t; using default_const_span_t = typename access_t::default_const_span_t; @@ -342,32 +376,13 @@ template struct message { template using shifted_by = - message...>; + message...>; template constexpr static auto fits_inside = (... and Fields::template fits_inside()); - template struct base { - constexpr static auto name = Name; - - constexpr auto as_derived() const -> T const & { - return static_cast(*this); - } - constexpr auto as_derived() -> T & { return static_cast(*this); } - - [[nodiscard]] constexpr auto get(auto f) const { - return access_t::get(as_derived().data(), f); - } - constexpr auto set(auto... fs) -> void { - access_t::set(as_derived().data(), fs...); - } - constexpr auto set() -> void {} - - [[nodiscard]] constexpr auto describe() const { - return access_t::describe(as_derived().data()); - } - }; + template using base = msg_base; template struct owner_t; @@ -583,13 +598,17 @@ template struct message { // name (see unique_by_name) template using extension = - message_without_unique_field_names; + message_without_unique_field_names; + + template + using with_env = message, Fields...>; }; } // namespace detail -template +template using message = - typename detail::message_with_unique_field_names::type; + typename detail::message_with_unique_field_names::type; template using owning = typename T::template owner_t<>; template using mutable_view = typename T::mutable_view_t; @@ -681,15 +700,16 @@ using shifted_msgs = msg_offsets, stdx::type_list>>; -template struct combiner { - template using fn = msg::message; +template struct combiner { + template using fn = msg::message; }; template struct combine_q { template requires(sizeof...(Msgs) > 0) using fn = boost::mp11::mp_apply_q< - combiner, boost::mp11::mp_append>; + combiner>, + boost::mp11::mp_append>; }; } // namespace detail @@ -709,20 +729,25 @@ using is_locatable = std::bool_constant; template using field_size_sort_fn = std::bool_constant < F2::bitsize; -template struct field_locator { +template +struct field_locator : field_locator, Fields...> {}; + +template +struct field_locator { using fields = boost::mp11::mp_partition, is_locatable>; using located_fields = boost::mp11::mp_first; using unlocated_fields = boost::mp11::mp_second; - using located_msg = boost::mp11::mp_apply_q, located_fields>; + using located_msg = + boost::mp11::mp_apply_q>, located_fields>; using auto_fields = boost::mp11::mp_sort; template using as_singleton_message = - msg::message; + msg::message; using auto_msgs = boost::mp11::mp_transform; @@ -730,11 +755,11 @@ template struct field_locator { template using pack = msg::pack; - using msg_type = boost::mp11::mp_apply; + using msg_type = + typename boost::mp11::mp_apply::template with_env; }; } // namespace detail -template -using relaxed_message = - typename detail::field_locator::msg_type; +template +using relaxed_message = typename detail::field_locator::msg_type; } // namespace msg diff --git a/test/msg/message.cpp b/test/msg/message.cpp index 9958de90..f81044b7 100644 --- a/test/msg/message.cpp +++ b/test/msg/message.cpp @@ -428,7 +428,6 @@ TEST_CASE("view_of concept", "[message]") { TEST_CASE("message fields are canonically sorted by lsb", "[message]") { using defn = message<"msg", field2, field1>; static_assert(std::is_same_v>); - static_assert(std::is_same_v>); } TEST_CASE("extend message with more fields", "[message]") { @@ -642,3 +641,58 @@ TEST_CASE("pack with empty messages", "[message]") { f4::shifted_by::value, std::uint8_t>>; static_assert(std::is_same_v); } + +namespace { +[[maybe_unused]] constexpr inline struct custom_t { + template + requires true // more constrained + CONSTEVAL auto operator()(T &&t) const + noexcept(noexcept(std::forward(t).query(std::declval()))) + -> decltype(std::forward(t).query(*this)) { + return std::forward(t).query(*this); + } + + CONSTEVAL auto operator()(auto &&) const { return 42; } +} custom; +} // namespace + +TEST_CASE("message with default empty environment", "[message]") { + static_assert(custom(msg_defn::env_t{}) == 42); +} + +TEST_CASE("message with defined environment", "[message]") { + using env_t = stdx::make_env_t; + using defn = message<"msg", env_t, id_field::with_required<0x80>, field1, + field2, field3>; + static_assert(custom(defn::env_t{}) == 17); +} + +TEST_CASE("supplement message environment", "[message]") { + using env_t = stdx::make_env_t; + using defn = message<"msg", env_t, id_field::with_required<0x80>, field1, + field2, field3>; + using new_defn = defn::with_env>; + static_assert(custom(new_defn::env_t{}) == 18); +} + +TEST_CASE("combine appends environments", "[message]") { + using env1_t = stdx::make_env_t; + using m1 = message<"m1", env1_t>; + + using env2_t = stdx::make_env_t; + using m2 = message<"m2", env2_t>; + + using defn = combine<"defn", m1, m2>; + static_assert(custom(defn::env_t{}) == 18); +} + +TEST_CASE("pack appends environments", "[message]") { + using env1_t = stdx::make_env_t; + using m1 = message<"m1", env1_t>; + + using env2_t = stdx::make_env_t; + using m2 = message<"m2", env2_t>; + + using defn = pack<"defn", std::uint8_t, m1, m2>; + static_assert(custom(defn::env_t{}) == 18); +} diff --git a/test/msg/relaxed_message.cpp b/test/msg/relaxed_message.cpp index 0a36dd69..2581f441 100644 --- a/test/msg/relaxed_message.cpp +++ b/test/msg/relaxed_message.cpp @@ -65,3 +65,28 @@ TEST_CASE("auto field without default value", "[relaxed_message]") { "msg", auto_f1::located::without_default>; static_assert(std::is_same_v); } + +namespace { +[[maybe_unused]] constexpr inline struct custom_t { + template + requires true // more constrained + CONSTEVAL auto operator()(T &&t) const + noexcept(noexcept(std::forward(t).query(std::declval()))) + -> decltype(std::forward(t).query(*this)) { + return std::forward(t).query(*this); + } + + CONSTEVAL auto operator()(auto &&) const { return 42; } +} custom; +} // namespace + +TEST_CASE("message with default empty environment", "[relaxed_message]") { + using defn = relaxed_message<"msg", auto_f1, auto_f2>; + static_assert(custom(defn::env_t{}) == 42); +} + +TEST_CASE("message with defined environment", "[message]") { + using env_t = stdx::make_env_t; + using defn = relaxed_message<"msg", env_t, auto_f1, auto_f2>; + static_assert(custom(defn::env_t{}) == 17); +} From 0858891c30434242aed885a1a737faf1fd44bd60 Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Wed, 5 Mar 2025 16:59:51 -0700 Subject: [PATCH 2/2] :sparkles: Use message environment when logging callback matches --- include/log/env.hpp | 12 +++++++ include/msg/callback.hpp | 31 ++++++++++++------- include/msg/detail/indexed_builder_common.hpp | 11 ++++--- test/msg/callback.cpp | 18 ++++++++++- 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/include/log/env.hpp b/include/log/env.hpp index 6bb047f0..3f631885 100644 --- a/include/log/env.hpp +++ b/include/log/env.hpp @@ -18,6 +18,11 @@ using cib_log_env_t = stdx::env<>; return stdx::extend_env_t{}; \ }()) cib_log_env_t +#define CIB_APPEND_LOG_ENV_DECL(E) \ + [[maybe_unused]] typedef decltype([] { \ + return stdx::append_env_t{}; \ + }()) cib_log_env_t + #define CIB_LOG_ENV(...) \ STDX_PRAGMA(diagnostic push) \ STDX_PRAGMA(diagnostic ignored "-Wshadow") \ @@ -25,6 +30,13 @@ using cib_log_env_t = stdx::env<>; CIB_PRAGMA_SEMI \ STDX_PRAGMA(diagnostic pop) +#define CIB_APPEND_LOG_ENV(E) \ + STDX_PRAGMA(diagnostic push) \ + STDX_PRAGMA(diagnostic ignored "-Wshadow") \ + CIB_APPEND_LOG_ENV_DECL(E) \ + CIB_PRAGMA_SEMI \ + STDX_PRAGMA(diagnostic pop) + #define CIB_WITH_LOG_ENV(...) \ STDX_PRAGMA(diagnostic push) \ STDX_PRAGMA(diagnostic ignored "-Wshadow") \ diff --git a/include/msg/callback.hpp b/include/msg/callback.hpp index e6f3a1b4..5c4c11b1 100644 --- a/include/msg/callback.hpp +++ b/include/msg/callback.hpp @@ -29,11 +29,13 @@ struct callback { template [[nodiscard]] auto handle(auto const &data, Args &&...args) const -> bool { + CIB_LOG_ENV(logging::get_level, logging::level::INFO); if (msg::call_with_message(matcher, data)) { - CIB_INFO("Incoming message matched [{}], because [{}], executing " - "callback", - stdx::ct_string_to_type(), - matcher.describe()); + CIB_APPEND_LOG_ENV(typename Msg::env_t); + CIB_LOG("Incoming message matched [{}], because [{}], executing " + "callback", + stdx::ct_string_to_type(), + matcher.describe()); msg::call_with_message(callable, data, std::forward(args)...); return true; @@ -42,14 +44,19 @@ struct callback { } auto log_mismatch(auto const &data) const -> void { - CIB_INFO(" {} - F:({})", - stdx::ct_string_to_type(), - msg::call_with_message( - [&](T &&t) -> decltype(matcher.describe_match( - std::forward(t))) { - return matcher.describe_match(std::forward(t)); - }, - data)); + CIB_LOG_ENV(logging::get_level, logging::level::INFO); + { + CIB_APPEND_LOG_ENV(typename Msg::env_t); + CIB_LOG( + " {} - F:({})", + stdx::ct_string_to_type(), + msg::call_with_message( + [&](T &&t) -> decltype(matcher.describe_match( + std::forward(t))) { + return matcher.describe_match(std::forward(t)); + }, + data)); + } } using msg_t = Msg; diff --git a/include/msg/detail/indexed_builder_common.hpp b/include/msg/detail/indexed_builder_common.hpp index a7dc0f3d..99d63ab5 100644 --- a/include/msg/detail/indexed_builder_common.hpp +++ b/include/msg/detail/indexed_builder_common.hpp @@ -163,12 +163,13 @@ struct indexed_builder_base { } using msg_t = typename CB::msg_t; + CIB_LOG_ENV(logging::get_level, logging::level::INFO); if (msg::call_with_message(cb.matcher, data)) { - CIB_INFO( - "Incoming message matched [{}], because [{}] (collapsed to " - "[{}]), executing callback", - stdx::ct_string_to_type(), - orig_cb.matcher.describe(), cb.matcher.describe()); + CIB_APPEND_LOG_ENV(typename msg_t::env_t); + CIB_LOG("Incoming message matched [{}], because [{}] (collapsed to " + "[{}]), executing callback", + stdx::ct_string_to_type(), + orig_cb.matcher.describe(), cb.matcher.describe()); msg::call_with_message(cb.callable, data, args...); return true; } diff --git a/test/msg/callback.cpp b/test/msg/callback.cpp index e23761e2..9df176ba 100644 --- a/test/msg/callback.cpp +++ b/test/msg/callback.cpp @@ -152,7 +152,7 @@ TEST_CASE("callback handles message (typed)", "[callback]") { CHECK(dispatched); } -TEST_CASE("callback logs match", "[callback]") { +TEST_CASE("callback logs match as INFO", "[callback]") { auto callback = msg::callback<"cb", msg_defn>( id_match, [](msg::const_view) { dispatched = true; }); auto const msg_match = std::array{0x8000ba11u, 0x0042d00du}; @@ -162,6 +162,22 @@ TEST_CASE("callback logs match", "[callback]") { CAPTURE(log_buffer); CHECK(log_buffer.find("matched [cb], because [id == 0x80]") != std::string::npos); + CHECK(log_buffer.find("INFO") != std::string::npos); +} + +TEST_CASE("callback logs match using message environment", "[callback]") { + using trace_msg_defn = msg_defn::with_env< + stdx::make_env_t>; + auto callback = msg::callback<"cb", trace_msg_defn>( + id_match, [](msg::const_view) { dispatched = true; }); + auto const msg_match = std::array{0x8000ba11u, 0x0042d00du}; + + log_buffer.clear(); + CHECK(callback.handle(msg_match)); + CAPTURE(log_buffer); + CHECK(log_buffer.find("matched [cb], because [id == 0x80]") != + std::string::npos); + CHECK(log_buffer.find("TRACE") != std::string::npos); } TEST_CASE("callback with convenience matcher", "[callback]") {