Skip to content

Commit f02501d

Browse files
committed
✨ Add relaxed_message
Problem: - Developing message formats is onerous; we'd like the compiler to take care of packing messages until we know exactly where to put fields. Solution: - Add `relaxed_message` as in #664 Note: - All auto-located fields are put after the fixed spec fields. - Auto-located fields are sorted from largest to smallest to ease alignment.
1 parent e3f99e9 commit f02501d

File tree

5 files changed

+117
-11
lines changed

5 files changed

+117
-11
lines changed

include/msg/field.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,5 +532,11 @@ struct field {
532532
using located = detail::field_t<
533533
decltype(stdx::ct_string_to_type<Name, sc::string_constant>()), T,
534534
Default, M, Ats...>;
535+
536+
constexpr static auto bitsize = sizeof(T) * CHAR_BIT - 1;
537+
538+
using default_located = detail::field_t<
539+
decltype(stdx::ct_string_to_type<Name, sc::string_constant>()), T,
540+
Default, M, at{msb_t{bitsize}, lsb_t{}}>;
535541
};
536542
} // namespace msg

include/msg/message.hpp

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -664,25 +664,23 @@ namespace detail {
664664
template <typename AlignTo, typename... Msgs>
665665
using msg_sizes = stdx::type_list<typename Msgs::template size<AlignTo>...>;
666666

667-
template <typename S>
668-
using negated_size = std::integral_constant<std::size_t, -S::value>;
669-
670667
template <typename AlignTo, typename... Msgs>
671-
using msg_offsets = boost::mp11::mp_partial_sum<
672-
msg_sizes<AlignTo, Msgs...>,
673-
negated_size<typename boost::mp11::mp_first<
674-
stdx::type_list<Msgs...>>::template size<AlignTo>>,
675-
boost::mp11::mp_plus>;
668+
using msg_offsets = boost::mp11::mp_push_front<
669+
boost::mp11::mp_partial_sum<msg_sizes<AlignTo, Msgs...>,
670+
std::integral_constant<std::size_t, 0>,
671+
boost::mp11::mp_plus>,
672+
std::integral_constant<std::size_t, 0>>;
676673

677674
template <typename AlignTo> struct shift_msg_q {
678675
template <typename Offset, typename Msg>
679676
using fn = typename Msg::template shifted_by<Offset::value, AlignTo>;
680677
};
681678

682679
template <typename AlignTo, typename... Msgs>
683-
using shifted_msgs = boost::mp11::mp_transform_q<shift_msg_q<AlignTo>,
684-
msg_offsets<AlignTo, Msgs...>,
685-
stdx::type_list<Msgs...>>;
680+
using shifted_msgs =
681+
boost::mp11::mp_transform_q<shift_msg_q<AlignTo>,
682+
msg_offsets<AlignTo, Msgs...>,
683+
stdx::type_list<Msgs..., msg::message<"end">>>;
686684

687685
template <stdx::ct_string Name> struct combiner {
688686
template <typename... Fields> using fn = msg::message<Name, Fields...>;
@@ -704,4 +702,39 @@ template <stdx::ct_string Name, typename AlignTo, typename... Msgs>
704702
requires(sizeof...(Msgs) > 0)
705703
using pack = boost::mp11::mp_apply_q<detail::combine_q<Name>,
706704
detail::shifted_msgs<AlignTo, Msgs...>>;
705+
706+
namespace detail {
707+
template <typename F>
708+
using is_locatable = std::bool_constant<requires { F::sort_key; }>;
709+
710+
template <typename F1, typename F2>
711+
using field_size_sort_fn = std::bool_constant < F2::bitsize<F1::bitsize>;
712+
713+
template <stdx::ct_string Name, typename... Fields> struct field_locator {
714+
using fields = boost::mp11::mp_partition<boost::mp11::mp_list<Fields...>,
715+
is_locatable>;
716+
using located_fields = boost::mp11::mp_first<fields>;
717+
using unlocated_fields = boost::mp11::mp_second<fields>;
718+
719+
using located_msg = boost::mp11::mp_apply_q<combiner<Name>, located_fields>;
720+
721+
using auto_fields =
722+
boost::mp11::mp_sort<unlocated_fields, field_size_sort_fn>;
723+
724+
template <typename F>
725+
using as_singleton_message =
726+
msg::message<Name, typename F::default_located>;
727+
using auto_msgs =
728+
boost::mp11::mp_transform<as_singleton_message, auto_fields>;
729+
730+
using all_msgs = boost::mp11::mp_push_front<auto_msgs, located_msg>;
731+
732+
template <typename... Msgs>
733+
using pack = msg::pack<Name, std::uint8_t, Msgs...>;
734+
using msg_type = boost::mp11::mp_apply<pack, all_msgs>;
735+
};
736+
} // namespace detail
737+
738+
template <stdx::ct_string Name, typename... Fields>
739+
using relaxed_message = detail::field_locator<Name, Fields...>::msg_type;
707740
} // namespace msg

test/msg/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ add_tests(
1212
indexed_handler
1313
indexed_handler_uninit
1414
message
15+
relaxed_message
1516
send
1617
LIBRARIES
1718
cib)

test/msg/message.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,3 +621,24 @@ TEST_CASE("pack 1 message", "[message]") {
621621
using expected_defn = message<"defn", f1, f2>;
622622
static_assert(std::is_same_v<defn, expected_defn>);
623623
}
624+
625+
TEST_CASE("pack with empty messages", "[message]") {
626+
using m0 = message<"m0">;
627+
628+
using f1 = field<"f1", std::uint32_t>::located<at{15_msb, 0_lsb}>;
629+
using f2 = field<"f2", std::uint32_t>::located<at{23_msb, 16_lsb}>;
630+
using m1 = message<"m1", f1, f2>;
631+
632+
using m2 = message<"m2">;
633+
634+
using f3 = field<"f3", std::uint32_t>::located<at{15_msb, 0_lsb}>;
635+
using f4 = field<"f4", std::uint32_t>::located<at{23_msb, 16_lsb}>;
636+
using m3 = message<"m3", f3, f4>;
637+
638+
using defn = pack<"defn", std::uint8_t, m0, m1, m2, m3>;
639+
using expected_defn =
640+
message<"defn", f1, f2,
641+
f3::shifted_by<m1::size<std::uint8_t>::value, std::uint8_t>,
642+
f4::shifted_by<m1::size<std::uint8_t>::value, std::uint8_t>>;
643+
static_assert(std::is_same_v<defn, expected_defn>);
644+
}

test/msg/relaxed_message.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include <msg/message.hpp>
2+
3+
#include <catch2/catch_test_macros.hpp>
4+
5+
namespace {
6+
using namespace msg;
7+
8+
using fixed_f =
9+
field<"fixed", std::uint32_t>::located<at{0_dw, 31_msb, 24_lsb}>;
10+
using auto_f1 = field<"auto1", std::uint32_t>;
11+
using auto_f2 = field<"auto2", std::uint8_t>;
12+
13+
} // namespace
14+
15+
TEST_CASE("message with automatically packed fields", "[relaxed_message]") {
16+
using defn = relaxed_message<"msg", auto_f1, auto_f2>;
17+
using expected_defn =
18+
msg::message<"msg", auto_f1::located<at{0_dw, 31_msb, 0_lsb}>,
19+
auto_f2::located<at{1_dw, 7_msb, 0_lsb}>>;
20+
static_assert(std::is_same_v<defn, expected_defn>);
21+
}
22+
23+
TEST_CASE("automatically packed fields are sorted by size",
24+
"[relaxed_message]") {
25+
using defn = relaxed_message<"msg", auto_f2, auto_f1>;
26+
using expected_defn =
27+
msg::message<"msg", auto_f1::located<at{0_dw, 31_msb, 0_lsb}>,
28+
auto_f2::located<at{1_dw, 7_msb, 0_lsb}>>;
29+
static_assert(std::is_same_v<defn, expected_defn>);
30+
}
31+
32+
TEST_CASE("message with mixed fixed and automatically packed fields",
33+
"[relaxed_message]") {
34+
using defn = relaxed_message<"msg", auto_f2, fixed_f, auto_f1>;
35+
using expected_defn =
36+
msg::message<"msg", fixed_f, auto_f1::located<at{1_dw, 31_msb, 0_lsb}>,
37+
auto_f2::located<at{2_dw, 7_msb, 0_lsb}>>;
38+
static_assert(std::is_same_v<defn, expected_defn>);
39+
}
40+
41+
TEST_CASE("message with no automatically packed fields", "[relaxed_message]") {
42+
using defn = relaxed_message<"msg", fixed_f>;
43+
using expected_defn = msg::message<"msg", fixed_f>;
44+
static_assert(std::is_same_v<defn, expected_defn>);
45+
}

0 commit comments

Comments
 (0)