Skip to content

Commit 3b1f881

Browse files
committed
Release v1.2
2 parents fb2badc + 680faa9 commit 3b1f881

17 files changed

+1781
-106
lines changed

comms/doc/page_custom_id_layer.dox

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/// @page page_custom_id_layer Defining Custom Message ID Protocol Stack Layer
2+
/// @tableofcontents
3+
/// The @b COMMS library provides default @ref comms::protocol::MsgIdLayer protocol
4+
/// stack layer to manage message ID information in the protocol framing.
5+
/// However, it may be insufficient (or incorrect) for some particular use cases, such as
6+
/// using @b bitfield field to store both numeric message ID and some extra flags
7+
/// (like <a href="http://mqtt.org">MQTT</a> protocol does).
8+
/// The @ref page_prot_stack_tutorial_new_layers section of the
9+
/// @ref page_prot_stack_tutorial page explains how to define new (custom)
10+
/// protocol layer.
11+
///
12+
/// @b NOTE, that @ref comms::protocol::MsgIdLayer class
13+
/// contains significant amount of compile time logic of choosing the best
14+
/// code based on provided options as well as available polymorphic interface
15+
/// messages being read and/or written. It is very impractical to try to
16+
/// implement something similar from scratch or copy-paste the existing
17+
/// definition and introduce required changes.
18+
///
19+
/// Since @b v1.2 COMMS library provides an ability to extend the existing
20+
/// definition of @ref comms::protocol::MsgIdLayer and customize some bits
21+
/// and pieces. Let's implement the mentioned example of sharing the same byte
22+
/// for numeric ID and some flags.
23+
///
24+
/// First of all let's define the @ref page_define_prot_interface, which
25+
/// holds the @b flags information as data member of every message object.
26+
/// @code
27+
/// namespace my_prot
28+
/// {
29+
///
30+
/// // Enum used for numeric message IDs
31+
/// enum MsgId
32+
/// {
33+
/// MsgId_Message1,
34+
/// MsgId_Message2,
35+
/// ...
36+
/// };
37+
///
38+
/// // Base class for all the fields defining serialization endian
39+
/// using FieldBase = comms::field::Field<comms::option::BigEndian>;
40+
///
41+
/// // Definition of the message flags
42+
/// class MessageFlags : public
43+
/// comms::field::BitmaskValue<
44+
/// FieldBase,
45+
/// comms::option::BitmaskReservedBits<0xf0>
46+
/// >
47+
/// {
48+
/// public:
49+
/// // Provides names and generates access functions for internal bits.
50+
/// COMMS_BITMASK_BITS_SEQ(bit0, bit1, bit2, bit3);
51+
/// };
52+
///
53+
/// // Definition of the extensible common message interface
54+
/// template <typename... TOptions>
55+
/// class Message : public
56+
/// comms::Message<
57+
/// TOptions...,
58+
/// comms::option::BigEndian,
59+
/// comms::option::MsgIdType<MsgId>,
60+
/// comms::option::ExtraTransportFields<std::tuple<MessageFlags> >
61+
/// >
62+
/// {
63+
/// public:
64+
/// // Allow access to extra transport fields.
65+
/// COMMS_MSG_TRANSPORT_FIELDS_ACCESS(flags);
66+
/// };
67+
///
68+
/// } // namespace my_prot
69+
/// @endcode
70+
/// Just to refresh the reader's memory: the usage of @ref COMMS_MSG_TRANSPORT_FIELDS_ACCESS()
71+
/// macro for the interface definition will generate @b transportField_flags()
72+
/// convenience member function to access the stored @b flags field, while
73+
/// usage of @ref COMMS_BITMASK_BITS_SEQ() in the flags field definition will
74+
/// genereate @b getBitValue_X() and @b setBitValue_X() convenience member
75+
/// functions to get / set values of the bits (where X is one of the defined
76+
/// names: @b bit0, @b bit1, @b bit2, and @b bit3).
77+
///
78+
/// Now, let's define the @b bitfield field, that splits one byte in half to
79+
/// store numeric message ID (in lower 4 bits) as well as extra flags (in upper 4 bits)
80+
/// @code
81+
/// namespace my_prot
82+
/// {
83+
///
84+
/// class IdAndFlagsField : public
85+
/// comms::field::Bitfield<
86+
/// FieldBase,
87+
/// std::tuple<
88+
/// comms::field::EnumValue<FieldBase, MsgId, comms::option::FixedBitLength<4> >,
89+
/// comms::field::IntValue<FieldBase, std::uint8_t, comms::option::FixedBitLength<4> >
90+
/// >
91+
/// >
92+
/// {
93+
/// public:
94+
/// // Allow access to internal member fields.
95+
/// COMMS_FIELD_MEMBERS_ACCESS(id, flags);
96+
/// };
97+
///
98+
/// } // my_prot
99+
/// @endcode
100+
/// Again, just to refresh the reader's memory: the usage of @ref COMMS_FIELD_MEMBERS_ACCESS()
101+
/// macro for the bitfield definition will generate @b field_X()
102+
/// convenience access member functions for the listed names.
103+
///
104+
/// Now it's time to actually extend the provided definition of
105+
/// the @ref comms::protocol::MsgIdLayer and support usage of the defined
106+
/// earlier @b IdAndFlagsField field.
107+
/// @code
108+
/// namespace my_prot
109+
/// {
110+
///
111+
/// template <typename TMessage, typename TAllMessages, typename TNextLayer, typename... TOptions>
112+
/// class MsgIdAndFlagsLayer : public
113+
/// comms::protocol::MsgIdLayer<
114+
/// IdAndFlagsField, // Used field that contains message ID
115+
/// TMessage, // Common interface class
116+
/// TAllMessages, // std::tuple of all the supported input messages
117+
/// TNextLayer, // Next layer in the protocol stack
118+
/// TOptions..., // Application specific extension options
119+
/// comms::option::ExtendingClass<MsgIdAndFlagsLayer<TMessage, TAllMessages, TNextLayer, TOptions...> >
120+
/// // Make the comms::protocol::MsgIdLayer aware of it being extended
121+
/// >
122+
/// {
123+
/// // Repeat definition of the base class
124+
/// using Base = comms::protocol::MsgIdLayer<...>;
125+
///
126+
/// public:
127+
/// // Repeat types defined in the base class (not visible by default)
128+
/// using MsgIdType = typename Base::MsgIdType;
129+
/// using MsgIdParamType = typename Base::MsgIdParamType;
130+
/// using Field = typename Base::Field; // same as IdAndFlagsField
131+
///
132+
/// // Retrieve message ID value from the given IdAndFlagsField field
133+
/// static MsgIdType getMsgIdFromField(const Field& field)
134+
/// {
135+
/// return field.field_id().value();
136+
/// }
137+
///
138+
/// // Set flags value for the message object before proceeding to the next layer read
139+
/// // The message object is passed by reference
140+
/// template <typename TMsg>
141+
/// static void beforeRead(const Field& field, TMsg& msg)
142+
/// {
143+
/// msg.transportField_flags().value() = field.field_flags().value();
144+
/// }
145+
///
146+
/// // Assemble the field's value before its write given message ID as well
147+
/// // as message object itself.
148+
/// template <typename TMsg>
149+
/// static void prepareFieldForWrite(MsgIdParamType id, const TMsg& msg, Field& field)
150+
/// {
151+
/// field.field_id().value() = id;
152+
/// field.field_flags().value() = msg.transportField_flags().value();
153+
/// }
154+
/// };
155+
///
156+
/// } // namespace my_prot
157+
/// @endcode
158+
/// The @ref comms::protocol::MsgIdLayer doesn't have any virtual functions and
159+
/// as the result not able to provide any polymorphic behavior. In order to be
160+
/// able to extend its default functionality there is a need to use
161+
/// <a href="https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern">Curiously Recurring Template Pattern</a>.
162+
/// It is done by passing @ref comms::option::ExtendingClass extension option with
163+
/// the type of the layer class being defined to the @ref comms::protocol::MsgIdLayer.
164+
///
165+
/// The extending class is expected to define the listed below functions. They do not
166+
/// necessarily need to be @b static, accessing inner private state of the layer object
167+
/// is also acceptable.
168+
/// @li @b getMsgIdFromField() - Member function that is invoked to retrieve the
169+
/// numeric message ID out of the provided field object.
170+
/// @li @b beforeRead() - Member function that is invoked after appropriate
171+
/// message object has been created but the read operation has @b NOT yet been
172+
/// forwarded to the next protocol layer. It gives the developer a chance to
173+
/// update some extra transport fields accessible via message interface class.
174+
/// @li @b prepareFieldForWrite() - Member function that is invoked to prepare
175+
/// the field value before its write (serialization). After the function returns,
176+
/// the @ref comms::protocol::MsgIdLayer will invoke @b write member function
177+
/// of the passed field in order to serialize it.
178+
///
179+
/// The newly defined custom protocol stack layer can be used instead of
180+
/// @ref comms::protocol::MsgIdLayer when defining
181+
/// @ref page_prot_stack_tutorial "protocol stack" (framing) of the protocol.
182+
///

0 commit comments

Comments
 (0)