Skip to content

Commit aba9fc1

Browse files
committed
✨ Add environments for message definitions
1 parent 458337d commit aba9fc1

File tree

4 files changed

+150
-46
lines changed

4 files changed

+150
-46
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ include(cmake/string_catalog.cmake)
2525
add_versioned_package("gh:boostorg/mp11#boost-1.83.0")
2626
fmt_recipe(11.1.3)
2727
add_versioned_package("gh:intel/cpp-baremetal-concurrency#7c5b26c")
28-
add_versioned_package("gh:intel/cpp-std-extensions#ec95cd6")
28+
add_versioned_package("gh:intel/cpp-std-extensions#7725142")
2929
add_versioned_package("gh:intel/cpp-baremetal-senders-and-receivers#73d95bc")
3030

3131
set(GEN_STR_CATALOG

include/msg/message.hpp

Lines changed: 69 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
#include <stdx/compiler.hpp>
1111
#include <stdx/ct_string.hpp>
12-
#include <stdx/cx_vector.hpp>
12+
#include <stdx/env.hpp>
1313
#include <stdx/iterator.hpp>
1414
#include <stdx/ranges.hpp>
1515
#include <stdx/span.hpp>
@@ -197,7 +197,7 @@ template <typename... Fields> struct storage_size {
197197

198198
template <typename F> using name_for = typename F::name_t;
199199

200-
template <stdx::ct_string Name, typename... Fields> class message_access {
200+
template <stdx::ct_string Name, typename... Fields> class msg_access {
201201
using FieldsTuple =
202202
decltype(stdx::make_indexed_tuple<name_for>(Fields{}...));
203203

@@ -295,10 +295,11 @@ concept storage_like = stdx::range<T> and requires {
295295
typename T::value_type;
296296
};
297297

298-
template <stdx::ct_string Name, typename... Fields> struct message;
298+
template <stdx::ct_string Name, typename Env, typename... Fields>
299+
struct message;
299300

300-
template <stdx::ct_string Name> struct msg_q {
301-
template <typename... Fields> using fn = message<Name, Fields...>;
301+
template <stdx::ct_string Name, typename Env> struct msg_q {
302+
template <typename... Fields> using fn = message<Name, Env, Fields...>;
302303
};
303304

304305
template <typename F1, typename F2>
@@ -312,9 +313,9 @@ using unique_by_name = boost::mp11::mp_unique_if<
312313
boost::mp11::mp_sort<boost::mp11::mp_list<Fields...>, field_sort_fn>,
313314
name_equal_fn>;
314315

315-
template <stdx::ct_string Name, typename... Fields>
316+
template <stdx::ct_string Name, typename Env, typename... Fields>
316317
using message_without_unique_field_names =
317-
boost::mp11::mp_apply_q<detail::msg_q<Name>,
318+
boost::mp11::mp_apply_q<detail::msg_q<Name, Env>,
318319
detail::unique_by_name<Fields...>>;
319320

320321
template <stdx::ct_string Name, typename... Fields>
@@ -323,12 +324,45 @@ struct message_with_unique_field_names {
323324
name_for, boost::mp11::mp_list<Fields...>>>::value,
324325
"Message contains fields with duplicate names");
325326

326-
using type = message_without_unique_field_names<Name, Fields...>;
327+
using type =
328+
message_without_unique_field_names<Name, stdx::env<>, Fields...>;
329+
};
330+
331+
template <stdx::ct_string Name, stdx::envlike Env, typename... Fields>
332+
struct message_with_unique_field_names<Name, Env, Fields...> {
333+
static_assert(boost::mp11::mp_is_set<boost::mp11::mp_transform<
334+
name_for, boost::mp11::mp_list<Fields...>>>::value,
335+
"Message contains fields with duplicate names");
336+
337+
using type = message_without_unique_field_names<Name, Env, Fields...>;
327338
};
328339

329-
template <stdx::ct_string Name, typename... Fields> struct message {
340+
template <stdx::ct_string Name, typename Access, typename T> struct msg_base {
341+
constexpr static auto name = Name;
342+
343+
constexpr auto as_derived() const -> T const & {
344+
return static_cast<T const &>(*this);
345+
}
346+
constexpr auto as_derived() -> T & { return static_cast<T &>(*this); }
347+
348+
[[nodiscard]] constexpr auto get(auto f) const {
349+
return Access::get(as_derived().data(), f);
350+
}
351+
constexpr auto set(auto... fs) -> void {
352+
Access::set(as_derived().data(), fs...);
353+
}
354+
constexpr auto set() -> void {}
355+
356+
[[nodiscard]] constexpr auto describe() const {
357+
return Access::describe(as_derived().data());
358+
}
359+
};
360+
361+
template <stdx::ct_string Name, typename Env, typename... Fields>
362+
struct message {
330363
using fields_t = stdx::type_list<Fields...>;
331-
using access_t = detail::message_access<Name, Fields...>;
364+
using env_t = Env;
365+
using access_t = msg_access<Name, Fields...>;
332366
using default_storage_t = typename access_t::default_storage_t;
333367
using default_span_t = typename access_t::default_span_t;
334368
using default_const_span_t = typename access_t::default_const_span_t;
@@ -342,32 +376,13 @@ template <stdx::ct_string Name, typename... Fields> struct message {
342376

343377
template <auto N, typename Unit = bit_unit>
344378
using shifted_by =
345-
message<Name, typename Fields::template shifted_by<N, Unit>...>;
379+
message<Name, Env, typename Fields::template shifted_by<N, Unit>...>;
346380

347381
template <typename S>
348382
constexpr static auto fits_inside =
349383
(... and Fields::template fits_inside<S>());
350384

351-
template <typename T> struct base {
352-
constexpr static auto name = Name;
353-
354-
constexpr auto as_derived() const -> T const & {
355-
return static_cast<T const &>(*this);
356-
}
357-
constexpr auto as_derived() -> T & { return static_cast<T &>(*this); }
358-
359-
[[nodiscard]] constexpr auto get(auto f) const {
360-
return access_t::get(as_derived().data(), f);
361-
}
362-
constexpr auto set(auto... fs) -> void {
363-
access_t::set(as_derived().data(), fs...);
364-
}
365-
constexpr auto set() -> void {}
366-
367-
[[nodiscard]] constexpr auto describe() const {
368-
return access_t::describe(as_derived().data());
369-
}
370-
};
385+
template <typename T> using base = msg_base<Name, access_t, T>;
371386

372387
template <typename> struct owner_t;
373388

@@ -583,13 +598,17 @@ template <stdx::ct_string Name, typename... Fields> struct message {
583598
// name (see unique_by_name)
584599
template <stdx::ct_string NewName, typename... NewFields>
585600
using extension =
586-
message_without_unique_field_names<NewName, NewFields..., Fields...>;
601+
message_without_unique_field_names<NewName, Env, NewFields...,
602+
Fields...>;
603+
604+
template <stdx::envlike E>
605+
using with_env = message<Name, stdx::append_env_t<Env, E>, Fields...>;
587606
};
588607
} // namespace detail
589608

590-
template <stdx::ct_string Name, typename... Fields>
609+
template <stdx::ct_string Name, typename... Ts>
591610
using message =
592-
typename detail::message_with_unique_field_names<Name, Fields...>::type;
611+
typename detail::message_with_unique_field_names<Name, Ts...>::type;
593612

594613
template <typename T> using owning = typename T::template owner_t<>;
595614
template <typename T> using mutable_view = typename T::mutable_view_t;
@@ -681,15 +700,16 @@ using shifted_msgs =
681700
msg_offsets<AlignTo, Msgs...>,
682701
stdx::type_list<Msgs..., msg::message<"end">>>;
683702

684-
template <stdx::ct_string Name> struct combiner {
685-
template <typename... Fields> using fn = msg::message<Name, Fields...>;
703+
template <stdx::ct_string Name, stdx::envlike Env> struct combiner {
704+
template <typename... Fields> using fn = msg::message<Name, Env, Fields...>;
686705
};
687706

688707
template <stdx::ct_string Name> struct combine_q {
689708
template <typename... Msgs>
690709
requires(sizeof...(Msgs) > 0)
691710
using fn = boost::mp11::mp_apply_q<
692-
combiner<Name>, boost::mp11::mp_append<typename Msgs::fields_t...>>;
711+
combiner<Name, stdx::append_env_t<typename Msgs::env_t...>>,
712+
boost::mp11::mp_append<typename Msgs::fields_t...>>;
693713
};
694714
} // namespace detail
695715

@@ -709,32 +729,37 @@ using is_locatable = std::bool_constant<requires { F::sort_key; }>;
709729
template <typename F1, typename F2>
710730
using field_size_sort_fn = std::bool_constant < F2::bitsize<F1::bitsize>;
711731

712-
template <stdx::ct_string Name, typename... Fields> struct field_locator {
732+
template <stdx::ct_string Name, typename... Fields>
733+
struct field_locator : field_locator<Name, stdx::env<>, Fields...> {};
734+
735+
template <stdx::ct_string Name, stdx::envlike Env, typename... Fields>
736+
struct field_locator<Name, Env, Fields...> {
713737
using fields = boost::mp11::mp_partition<boost::mp11::mp_list<Fields...>,
714738
is_locatable>;
715739
using located_fields = boost::mp11::mp_first<fields>;
716740
using unlocated_fields = boost::mp11::mp_second<fields>;
717741

718-
using located_msg = boost::mp11::mp_apply_q<combiner<Name>, located_fields>;
742+
using located_msg =
743+
boost::mp11::mp_apply_q<combiner<Name, stdx::env<>>, located_fields>;
719744

720745
using auto_fields =
721746
boost::mp11::mp_sort<unlocated_fields, field_size_sort_fn>;
722747

723748
template <typename F>
724749
using as_singleton_message =
725-
msg::message<Name, typename F::default_located>;
750+
msg::message<Name, Env, typename F::default_located>;
726751
using auto_msgs =
727752
boost::mp11::mp_transform<as_singleton_message, auto_fields>;
728753

729754
using all_msgs = boost::mp11::mp_push_front<auto_msgs, located_msg>;
730755

731756
template <typename... Msgs>
732757
using pack = msg::pack<Name, std::uint8_t, Msgs...>;
733-
using msg_type = boost::mp11::mp_apply<pack, all_msgs>;
758+
using msg_type =
759+
typename boost::mp11::mp_apply<pack, all_msgs>::template with_env<Env>;
734760
};
735761
} // namespace detail
736762

737-
template <stdx::ct_string Name, typename... Fields>
738-
using relaxed_message =
739-
typename detail::field_locator<Name, Fields...>::msg_type;
763+
template <stdx::ct_string Name, typename... Ts>
764+
using relaxed_message = typename detail::field_locator<Name, Ts...>::msg_type;
740765
} // namespace msg

test/msg/message.cpp

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,6 @@ TEST_CASE("view_of concept", "[message]") {
428428
TEST_CASE("message fields are canonically sorted by lsb", "[message]") {
429429
using defn = message<"msg", field2, field1>;
430430
static_assert(std::is_same_v<defn, message<"msg", field1, field2>>);
431-
static_assert(std::is_same_v<defn, detail::message<"msg", field1, field2>>);
432431
}
433432

434433
TEST_CASE("extend message with more fields", "[message]") {
@@ -642,3 +641,58 @@ TEST_CASE("pack with empty messages", "[message]") {
642641
f4::shifted_by<m1::size<std::uint8_t>::value, std::uint8_t>>;
643642
static_assert(std::is_same_v<defn, expected_defn>);
644643
}
644+
645+
namespace {
646+
[[maybe_unused]] constexpr inline struct custom_t {
647+
template <typename T>
648+
requires true // more constrained
649+
CONSTEVAL auto operator()(T &&t) const
650+
noexcept(noexcept(std::forward<T>(t).query(std::declval<custom_t>())))
651+
-> decltype(std::forward<T>(t).query(*this)) {
652+
return std::forward<T>(t).query(*this);
653+
}
654+
655+
CONSTEVAL auto operator()(auto &&) const { return 42; }
656+
} custom;
657+
} // namespace
658+
659+
TEST_CASE("message with default empty environment", "[message]") {
660+
static_assert(custom(msg_defn::env_t{}) == 42);
661+
}
662+
663+
TEST_CASE("message with defined environment", "[message]") {
664+
using env_t = stdx::make_env_t<custom, 17>;
665+
using defn = message<"msg", env_t, id_field::with_required<0x80>, field1,
666+
field2, field3>;
667+
static_assert(custom(defn::env_t{}) == 17);
668+
}
669+
670+
TEST_CASE("supplement message environment", "[message]") {
671+
using env_t = stdx::make_env_t<custom, 17>;
672+
using defn = message<"msg", env_t, id_field::with_required<0x80>, field1,
673+
field2, field3>;
674+
using new_defn = defn::with_env<stdx::make_env_t<custom, 18>>;
675+
static_assert(custom(new_defn::env_t{}) == 18);
676+
}
677+
678+
TEST_CASE("combine appends environments", "[message]") {
679+
using env1_t = stdx::make_env_t<custom, 17>;
680+
using m1 = message<"m1", env1_t>;
681+
682+
using env2_t = stdx::make_env_t<custom, 18>;
683+
using m2 = message<"m2", env2_t>;
684+
685+
using defn = combine<"defn", m1, m2>;
686+
static_assert(custom(defn::env_t{}) == 18);
687+
}
688+
689+
TEST_CASE("pack appends environments", "[message]") {
690+
using env1_t = stdx::make_env_t<custom, 17>;
691+
using m1 = message<"m1", env1_t>;
692+
693+
using env2_t = stdx::make_env_t<custom, 18>;
694+
using m2 = message<"m2", env2_t>;
695+
696+
using defn = pack<"defn", std::uint8_t, m1, m2>;
697+
static_assert(custom(defn::env_t{}) == 18);
698+
}

test/msg/relaxed_message.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,28 @@ TEST_CASE("auto field without default value", "[relaxed_message]") {
6565
"msg", auto_f1::located<at{0_dw, 31_msb, 0_lsb}>::without_default>;
6666
static_assert(std::is_same_v<defn, expected_defn>);
6767
}
68+
69+
namespace {
70+
[[maybe_unused]] constexpr inline struct custom_t {
71+
template <typename T>
72+
requires true // more constrained
73+
CONSTEVAL auto operator()(T &&t) const
74+
noexcept(noexcept(std::forward<T>(t).query(std::declval<custom_t>())))
75+
-> decltype(std::forward<T>(t).query(*this)) {
76+
return std::forward<T>(t).query(*this);
77+
}
78+
79+
CONSTEVAL auto operator()(auto &&) const { return 42; }
80+
} custom;
81+
} // namespace
82+
83+
TEST_CASE("message with default empty environment", "[relaxed_message]") {
84+
using defn = relaxed_message<"msg", auto_f1, auto_f2>;
85+
static_assert(custom(defn::env_t{}) == 42);
86+
}
87+
88+
TEST_CASE("message with defined environment", "[message]") {
89+
using env_t = stdx::make_env_t<custom, 17>;
90+
using defn = relaxed_message<"msg", env_t, auto_f1, auto_f2>;
91+
static_assert(custom(defn::env_t{}) == 17);
92+
}

0 commit comments

Comments
 (0)