Skip to content

Commit 4bb55c6

Browse files
authored
Merge pull request #762 from elbeno/test-msg
🐛 Match fields by name after packing
2 parents 3e3f3a0 + 42983a0 commit 4bb55c6

File tree

4 files changed

+95
-11
lines changed

4 files changed

+95
-11
lines changed

docs/message.adoc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,34 @@ The second parameter to `pack` (`std::uint8_t` in the example above) defines how
210210
the messages are packed together - in this case, each subsequent message is
211211
byte-aligned.
212212

213+
CAUTION: After combining or packing messages, the fields inside them may have
214+
moved!
215+
216+
Any matchers defined on the original fields may cause problems when matching
217+
against raw data, because they will be looking in the wrong place. (Matching
218+
when the message type is known is OK, because the field is resolved by its
219+
name.)
220+
221+
To avoid matcher problems, define matchers after combining or packing messages.
222+
To help with this, use the `field_t` alias on the message definition if needed.
223+
224+
[source,cpp]
225+
----
226+
using type_f = field<"type", std::uint32_t>::located<at{0_dw, 5_msb, 0_lsb}>;
227+
using header_defn = message<"header", type_f>;
228+
229+
using data_f = field<"data">, std::uint32_t>::located<at{0_dw, 7_msb, 0_lsb}>;
230+
using payload_defn = message<"payload", data_f>;
231+
232+
using msg_defn = extend<
233+
pack<"msg", std::uint8_t, header_defn, payload_defn>,
234+
type_f::with_required<1>>;
235+
236+
// msg_defn does not contain data_f because packing moved it
237+
// but we can get the actual data field by name, if we need it
238+
using new_data_f = msg_defn::field_t<"data">;
239+
----
240+
213241
==== Owning vs view types
214242

215243
An owning message uses underlying storage: by default, this is a `std::array` of

include/msg/field_matchers.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ struct rel_matcher_t {
186186
if constexpr (stdx::range<Msg>) {
187187
return Field::extract(msg);
188188
} else {
189-
return Field::extract(std::data(msg));
189+
return msg.get(Field{});
190190
}
191191
}
192192
};

include/msg/message.hpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -209,29 +209,27 @@ template <stdx::ct_string Name, typename... Fields> class msg_access {
209209

210210
template <stdx::range R, some_field_value V>
211211
constexpr static auto set1(R &&r, V v) -> void {
212-
using Field = std::remove_cvref_t<decltype(stdx::get<name_for<V>>(
213-
FieldsTuple{}))>;
212+
using Field = field_t<name_for<V>>;
214213
check<Field, std::remove_cvref_t<R>>();
215214
Field::insert(std::forward<R>(r),
216215
static_cast<typename Field::value_type>(v.value));
217216
}
218217

219218
template <typename N, stdx::range R>
220219
constexpr static auto set_default(R &&r) -> void {
221-
using Field =
222-
std::remove_cvref_t<decltype(stdx::get<N>(FieldsTuple{}))>;
223-
check<Field, std::remove_cvref_t<R>>();
224-
Field::insert_default(std::forward<R>(r));
220+
check<field_t<N>, std::remove_cvref_t<R>>();
221+
field_t<N>::insert_default(std::forward<R>(r));
225222
}
226223

227224
template <typename N, stdx::range R> constexpr static auto get(R &&r) {
228-
using Field =
229-
std::remove_cvref_t<decltype(stdx::get<N>(FieldsTuple{}))>;
230-
check<Field, std::remove_cvref_t<R>>();
231-
return Field::extract(std::forward<R>(r));
225+
check<field_t<N>, std::remove_cvref_t<R>>();
226+
return field_t<N>::extract(std::forward<R>(r));
232227
}
233228

234229
public:
230+
template <typename N>
231+
using field_t = std::remove_cvref_t<decltype(stdx::get<N>(FieldsTuple{}))>;
232+
235233
template <stdx::range R, stdx::ct_string... Ns>
236234
constexpr static auto set(R &&r, field_name<Ns>...) -> void {
237235
(set_default<Ns>(r), ...);
@@ -369,6 +367,9 @@ struct message {
369367
std::integral_constant<std::size_t,
370368
detail::storage_size<Fields...>::template in<T>>;
371369

370+
template <stdx::ct_string N>
371+
using field_t = typename access_t::template field_t<stdx::cts_t<N>>;
372+
372373
template <template <typename, std::size_t> typename C, typename T>
373374
using custom_storage_t = typename access_t::template storage_t<C, T>;
374375

test/msg/message.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ template <>
3333
inline auto logging::config<> =
3434
logging::fmt::config{std::back_inserter(log_buffer)};
3535

36+
TEST_CASE("get a field from a message", "[message]") {
37+
using F = msg_defn::field_t<"f1">;
38+
STATIC_REQUIRE(std::is_same_v<F, field1>);
39+
}
40+
3641
TEST_CASE("message with automatic storage", "[message]") {
3742
test_msg msg{};
3843
auto data = msg.data();
@@ -683,6 +688,56 @@ TEST_CASE("pack with empty messages", "[message]") {
683688
STATIC_REQUIRE(std::is_same_v<defn, expected_defn>);
684689
}
685690

691+
TEST_CASE("field matchers work with packed messages", "[message]") {
692+
using f1 = field<"f1", std::uint32_t>::located<at{15_msb, 0_lsb}>;
693+
using f2 = field<"f2", std::uint32_t>::located<at{23_msb, 16_lsb}>;
694+
using m1 = message<"m1", f1, f2>;
695+
696+
using f3 = field<"f3", std::uint32_t>::located<at{15_msb, 0_lsb}>;
697+
using f4 = field<"f4", std::uint32_t>::located<at{23_msb, 16_lsb}>;
698+
using m2 = message<"m2", f3, f4>;
699+
700+
constexpr auto m = msg::equal_to_t<f3, 3>{};
701+
using defn = pack<"defn", std::uint8_t, m1, m2>;
702+
constexpr owning<defn> msg{"f1"_field = 1, "f2"_field = 2, "f3"_field = 3,
703+
"f4"_field = 4};
704+
STATIC_REQUIRE(m(msg));
705+
}
706+
707+
TEST_CASE("a packed message matches itself", "[message]") {
708+
using f1 = field<"f1", std::uint32_t>::located<at{15_msb, 0_lsb}>;
709+
using f2 = field<"f2", std::uint32_t>::located<at{23_msb, 16_lsb}>;
710+
using m1 = message<"m1", f1, f2>;
711+
712+
using f3 = field<"f3", std::uint32_t>::located<at{15_msb, 0_lsb}>;
713+
using f4 = field<"f4", std::uint32_t>::located<at{23_msb, 16_lsb}>;
714+
using m2 = message<"m2", f3::with_equal_to<3>, f4>;
715+
716+
using defn = pack<"defn", std::uint8_t, m1, m2>;
717+
constexpr owning<defn> msg{"f1"_field = 1, "f2"_field = 2, "f3"_field = 3,
718+
"f4"_field = 4};
719+
STATIC_REQUIRE(defn::matcher_t{}(msg));
720+
}
721+
722+
TEST_CASE("correct field matchers can be post-defined", "[message]") {
723+
using f1 = field<"f1", std::uint32_t>::located<at{15_msb, 0_lsb}>;
724+
using f2 = field<"f2", std::uint32_t>::located<at{23_msb, 16_lsb}>;
725+
using m1 = message<"m1", f1, f2>;
726+
727+
using f3 = field<"f3", std::uint32_t>::located<at{15_msb, 0_lsb}>;
728+
using f4 = field<"f4", std::uint32_t>::located<at{23_msb, 16_lsb}>;
729+
using m2 = message<"m2", f3, f4>;
730+
731+
using defn = pack<"defn", std::uint8_t, m1, m2>;
732+
constexpr owning<defn> msg{"f1"_field = 1, "f2"_field = 2, "f3"_field = 3,
733+
"f4"_field = 4};
734+
735+
constexpr auto m_wrong = msg::equal_to_t<f3, 3>{};
736+
STATIC_REQUIRE(not m_wrong(msg.data()));
737+
constexpr auto m_right = msg::equal_to_t<defn::field_t<"f3">, 3>{};
738+
STATIC_REQUIRE(m_right(msg.data()));
739+
}
740+
686741
namespace {
687742
[[maybe_unused]] constexpr inline struct custom_t {
688743
template <typename T>

0 commit comments

Comments
 (0)