Skip to content

Commit 583cf0c

Browse files
committed
✨ Support tuple protocol on messages
Problem: - It's sometimes useful to destructure messages. Solution: - Support the tuple protocol on (owning or view) messages. Note: - The field order is the canonical ordering of fields by lsb; this is not necessarily the declaration order in the message alias.
1 parent da6cb26 commit 583cf0c

File tree

3 files changed

+60
-0
lines changed

3 files changed

+60
-0
lines changed

include/msg/message.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,9 @@ template <stdx::ct_string Name, typename Access, typename T> struct msg_base {
356356
template <stdx::ct_string Name, typename Env, typename... Fields>
357357
struct message {
358358
using fields_t = stdx::type_list<Fields...>;
359+
using num_fields_t = std::integral_constant<std::size_t, sizeof...(Fields)>;
360+
template <std::size_t I> using nth_field_t = stdx::nth_t<I, Fields...>;
361+
359362
using env_t = Env;
360363
using access_t = msg_access<Name, Fields...>;
361364
using default_storage_t = typename access_t::default_storage_t;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#pragma once
2+
3+
#include <msg/message.hpp>
4+
5+
#include <stdx/type_traits.hpp>
6+
7+
#include <cstddef>
8+
#include <tuple>
9+
#include <type_traits>
10+
#include <utility>
11+
12+
template <msg::messagelike M>
13+
struct std::tuple_size<M> : M::definition_t::num_fields_t {};
14+
15+
template <std::size_t I, msg::messagelike M>
16+
struct std::tuple_element<I, M>
17+
: std::type_identity<
18+
typename M::definition_t::template nth_field_t<I>::value_type> {};
19+
20+
namespace msg::detail {
21+
template <std::size_t I, msg::messagelike M>
22+
constexpr auto get(M &&m) -> decltype(auto) {
23+
return std::forward<M>(m).get(typename std::remove_cvref_t<
24+
M>::definition_t::template nth_field_t<I>{});
25+
}
26+
} // namespace msg::detail

test/msg/message.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <log/fmt/logger.hpp>
22
#include <msg/message.hpp>
3+
#include <msg/message_destructure.hpp>
34

45
#include <catch2/catch_test_macros.hpp>
56
#include <catch2/matchers/catch_matchers_templated.hpp>
@@ -73,6 +74,24 @@ TEST_CASE("construct with field values", "[message]") {
7374
CHECK(0x0042'd00d == data[1]);
7475
}
7576

77+
TEST_CASE("message supports tuple protocol", "[message]") {
78+
[[maybe_unused]] test_msg msg{"f1"_field = 0xba11, "f2"_field = 0x42,
79+
"f3"_field = 0xd00d};
80+
STATIC_REQUIRE(std::tuple_size_v<test_msg> == 4);
81+
STATIC_REQUIRE(
82+
std::is_same_v<std::tuple_element_t<0, test_msg>, std::uint32_t>);
83+
}
84+
85+
TEST_CASE("destructure owning message", "[message]") {
86+
test_msg msg{"f1"_field = 0xba11, "f2"_field = 0x42, "f3"_field = 0xd00d};
87+
STATIC_REQUIRE(std::tuple_size_v<test_msg> == 4);
88+
auto const [f1, id, f3, f2] = msg;
89+
CHECK(0x80 == id);
90+
CHECK(0xba11 == f1);
91+
CHECK(0x42 == f2);
92+
CHECK(0xd00d == f3);
93+
}
94+
7695
TEST_CASE("use field names as template args", "[message]") {
7796
auto msg = []<auto F>() {
7897
return test_msg{F = 0xba11};
@@ -123,6 +142,18 @@ TEST_CASE("view with read-only external storage", "[message]") {
123142
CHECK(0xd00d == msg.get("f3"_field));
124143
}
125144

145+
TEST_CASE("destructure message view", "[message]") {
146+
auto const arr =
147+
typename msg_defn::default_storage_t{0x8000'ba11, 0x0042'd00d};
148+
const_view<msg_defn> msg{arr};
149+
150+
auto const [f1, id, f3, f2] = msg;
151+
CHECK(0x80 == id);
152+
CHECK(0xba11 == f1);
153+
CHECK(0x42 == f2);
154+
CHECK(0xd00d == f3);
155+
}
156+
126157
TEST_CASE("view with external storage (oversized)", "[message]") {
127158
auto const arr = std::array<std::uint32_t, 8>{0x8000'ba11, 0x0042'd00d};
128159
msg_defn::view_t msg{arr};

0 commit comments

Comments
 (0)