QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
WireFields.h
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <concepts>
5#include <cstddef>
6#include <cstdint>
7#include <span>
8#include <tuple>
9#include <type_traits>
10
11#include "LittleEndian.h"
12
16namespace Wire {
17
18template <typename T>
19struct Layout;
20
21template <typename T>
22concept Record = requires {
23 { Layout<T>::SIZE } -> std::convertible_to<std::size_t>;
24 Layout<T>::FIELDS;
25};
26
27template <Record T>
28inline constexpr std::size_t SIZE = Layout<T>::SIZE;
29
30namespace detail {
31template <typename>
33
34template <typename Class, typename Value>
35struct MemberPointer<Value Class::*>
36{
37 using ClassType = Class;
38 using ValueType = Value;
39};
40
41template <typename T>
42constexpr std::size_t wireSize()
43{
44 if constexpr (std::is_array_v<T>) {
45 return std::extent_v<T> * wireSize<std::remove_extent_t<T>>();
46 } else if constexpr (Record<T>) {
47 return SIZE<T>;
48 } else {
49 static_assert(LittleEndian::Scalar<T>, "wire fields are scalars, records or fixed arrays of them");
50 return sizeof(T);
51 }
52}
53} // namespace detail
54
55template <auto Member, std::size_t Offset>
56struct Field
57{
58 using Class = typename detail::MemberPointer<decltype(Member)>::ClassType;
59 using Value = typename detail::MemberPointer<decltype(Member)>::ValueType;
60 static constexpr auto MEMBER = Member;
61 static constexpr std::size_t OFFSET = Offset;
62 static constexpr std::size_t SIZE = detail::wireSize<Value>();
63};
64
66template <Record T>
67inline constexpr bool VALID_LAYOUT = std::apply(
68 [](auto... field) {
69 const std::array<std::size_t, sizeof...(field)> begin{decltype(field)::OFFSET...};
70 const std::array<std::size_t, sizeof...(field)> end{(decltype(field)::OFFSET + decltype(field)::SIZE)...};
71 for (std::size_t i = 0; i < begin.size(); ++i) {
72 if (end[i] > SIZE<T>) {
73 return false;
74 }
75 for (std::size_t j = 0; j < i; ++j) {
76 if (begin[i] < end[j] && begin[j] < end[i]) {
77 return false;
78 }
79 }
80 }
81 return (std::same_as<typename decltype(field)::Class, T> && ...);
82 },
83 Layout<T>::FIELDS);
84
85namespace detail {
86template <typename Value>
87constexpr void read(Value& value, std::span<const uint8_t> bytes, std::size_t offset)
88{
89 if constexpr (std::is_array_v<Value>) {
90 for (std::size_t i = 0; i < std::extent_v<Value>; ++i) {
91 detail::read(value[i], bytes, offset + i * wireSize<std::remove_extent_t<Value>>());
92 }
93 } else if constexpr (Record<Value>) {
94 static_assert(VALID_LAYOUT<Value>, "Layout<T> fields must belong to T, fit in SIZE and not overlap");
95 const auto record = offset <= bytes.size() ? bytes.subspan(offset) : std::span<const uint8_t>{};
96 std::apply(
97 [&](auto... field) {
98 (detail::read(value.*decltype(field)::MEMBER, record, decltype(field)::OFFSET), ...);
99 },
101 } else {
102 value = LittleEndian::read<Value>(bytes, offset).value_or(Value{});
103 }
104}
105
106template <typename Value>
107constexpr void write(std::span<uint8_t> bytes, std::size_t offset, const Value& value)
108{
109 if constexpr (std::is_array_v<Value>) {
110 for (std::size_t i = 0; i < std::extent_v<Value>; ++i) {
111 detail::write(bytes, offset + i * wireSize<std::remove_extent_t<Value>>(), value[i]);
112 }
113 } else if constexpr (Record<Value>) {
114 static_assert(VALID_LAYOUT<Value>, "Layout<T> fields must belong to T, fit in SIZE and not overlap");
115 std::apply(
116 [&](auto... field) {
117 (detail::write(bytes, offset + decltype(field)::OFFSET, value.*decltype(field)::MEMBER), ...);
118 },
120 } else {
121 (void) LittleEndian::write(bytes, offset, value);
122 }
123}
124} // namespace detail
125
127template <Record T>
128constexpr T decode(std::span<const uint8_t> bytes, std::size_t offset = 0)
129{
130 T value{};
131 detail::read(value, bytes, offset);
132 return value;
133}
134
135template <Record T>
136constexpr std::array<uint8_t, SIZE<T>> encode(const T& value)
137{
138 std::array<uint8_t, SIZE<T>> bytes{};
139 detail::write(bytes, 0, value);
140 return bytes;
141}
142
143} // namespace Wire
constexpr bool write(std::span< uint8_t > bytes, std::size_t offset, T value)
Encode an unaligned little-endian scalar. Insufficient space leaves the destination unchanged.
constexpr std::size_t wireSize()
Definition WireFields.h:42
constexpr void read(Value &value, std::span< const uint8_t > bytes, std::size_t offset)
Definition WireFields.h:87
constexpr void write(std::span< uint8_t > bytes, std::size_t offset, const Value &value)
Definition WireFields.h:107
constexpr T decode(std::span< const uint8_t > bytes, std::size_t offset=0)
Decodes the record at offset. Fields whose bytes are unavailable decode as zero.
Definition WireFields.h:128
constexpr bool VALID_LAYOUT
Every field belongs to T, fits within SIZE<T> and overlaps no other field.
Definition WireFields.h:67
constexpr std::array< uint8_t, SIZE< T > > encode(const T &value)
Definition WireFields.h:136
constexpr std::size_t SIZE
Definition WireFields.h:28
typename detail::MemberPointer< decltype(Member)>::ClassType Class
Definition WireFields.h:58
static constexpr auto MEMBER
Definition WireFields.h:60
static constexpr std::size_t SIZE
Definition WireFields.h:62
typename detail::MemberPointer< decltype(Member)>::ValueType Value
Definition WireFields.h:59
static constexpr std::size_t OFFSET
Definition WireFields.h:61