Skip to content

Commit 41a8f29

Browse files
committed
Added tutorial21.
1 parent 20be525 commit 41a8f29

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2838
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ $> nmake install
164164
- [tutorial18](../../tree/master/tutorials/tutorial18) - How to access transport framing fields.
165165
- [tutorial19](../../tree/master/tutorials/tutorial19) - Introduction to protocol version support.
166166
- [tutorial20](../../tree/master/tutorials/tutorial20) - Reporting protocol version in one of the messages.
167+
- [tutorial21](../../tree/master/tutorials/tutorial21) - Cast between different field types.
167168

168169

169170
# How-Tos

script/env_dev_clang.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/bash
2+
3+
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
4+
ROOT_DIR=$( dirname ${SCRIPT_DIR} )
5+
BUILD_DIR="${ROOT_DIR}/build.clang"
6+
mkdir -p ${BUILD_DIR}
7+
cd ${BUILD_DIR}
8+
9+
CC=clang CXX=clang++ cmake .. -DCMAKE_INSTALL_PREFIX=install -DCMAKE_BUILD_TYPE=Debug -DUSE_SANITIZERS=ON

tutorials/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ add_subdirectory (tutorial17)
3030
add_subdirectory (tutorial18)
3131
add_subdirectory (tutorial19)
3232
add_subdirectory (tutorial20)
33+
add_subdirectory (tutorial21)

tutorials/tutorial20/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,4 @@ operation.
170170
right version. It will result in all subsequent incoming messages to be read as if having
171171
the reported version.
172172

173-
[Read Previous Tutorial](../tutorial19) <-----------------------
173+
[Read Previous Tutorial](../tutorial19) <-----------------------> [Read Next Tutorial](../tutorial21)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
get_filename_component(name ${CMAKE_CURRENT_SOURCE_DIR} NAME)
2+
3+
set (schema_files
4+
${CMAKE_CURRENT_SOURCE_DIR}/dsl/schema.xml
5+
)
6+
7+
set (server_src
8+
src/ServerSession.cpp
9+
)
10+
11+
set (client_src
12+
src/ClientSession.cpp
13+
)
14+
15+
dsl_tutorial_parse(${name} SCHEMAS ${schema_files})
16+
17+
bin_tutorial_server(${name} ${server_src})
18+
bin_tutorial_client(${name} ${client_src})
19+

tutorials/tutorial21/README.md

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# Tutorial 21
2+
Cast between different field types.
3+
4+
There maybe a case when the the same serialized bytes need to be interepreted in different ways. This tutorial
5+
implements the following framing:
6+
7+
```
8+
SIZE (2 bytes) | ID (1 byte) | FLAGS (1 byte) | PAYLOAD
9+
```
10+
11+
The [schema](dsl/schema.xml) file defines two different messages `Msg1` and `Msg2`. The contents
12+
of the messages are not important. The important part is that the `FLAGS` byte in
13+
the framing needs to be interpreted differently when handling the `Msg1` and `Msg2`.
14+
15+
As was already described in one of the previous tutorials the value which resides in the framing, but needs
16+
to be available when message object is handled has to be propagated to the message **<interface>**.
17+
18+
```xml
19+
<fields>
20+
<int name="BasicFlags" type="uint8" displayName="Flags" />
21+
</fields>
22+
23+
<interface name="Interface" description="Common Interface for all the messages.">
24+
<ref field="BasicFlags" name="Flags" />
25+
</interface>
26+
27+
<frame name="Frame">
28+
<size name="Size">
29+
<int name="Field" type="uint16" displayName="Size" />
30+
</size>
31+
<id name="Id" field="MsgId" />
32+
<value name="Flags" field="BasicFlags" interfaceFieldName="Flags" />
33+
<payload name="Data" />
34+
</frame>
35+
```
36+
37+
The interpretation of the flags for each message is defined as separate fields:
38+
```xml
39+
<set name="Msg1Flags" length="1" forceGen="true">
40+
<bit name="B0" idx="0" />
41+
<bit name="B1" idx="1" />
42+
<bit name="B2" idx="2" />
43+
</set>
44+
45+
<bitfield name="Msg2Flags" forceGen="true">
46+
<enum name="EnumMem" type="uint8" bitLength="2">
47+
<validValue name="V0" val="0" />
48+
<validValue name="V1" val="1" />
49+
<validValue name="V2" val="2" />
50+
<validValue name="V3" val="3" />
51+
</enum>
52+
<int name="IntMem" type="uint8" bitLength="6" />
53+
</bitfield>
54+
```
55+
Note the presence of **forceGen** property. It forces the code generator to
56+
generate the definition of the field when it's not referenced anywhere in the
57+
schema.
58+
59+
When sending `Msg1` and `Msg2` the [ClientSession](src/ClientSession.cpp) uses
60+
[comms::field_cast()](https://commschamp.github.io/comms_doc/cast_8h.html) stand alone function
61+
to cast between the fields values:
62+
```cpp
63+
void ClientSession::sendMsg1()
64+
{
65+
Msg1 msg;
66+
67+
// prepare flags of Msg1
68+
using Msg1Flags = tutorial21::field::Msg1Flags<ClientProtocolOptions>;
69+
Msg1Flags flags;
70+
flags.setBitValue_B1(true);
71+
72+
// Assign the flags
73+
msg.transportField_flags() = comms::field_cast<Message::TransportField_flags>(flags);
74+
75+
sendMessage(msg);
76+
}
77+
78+
void ClientSession::sendMsg2()
79+
{
80+
Msg2 msg;
81+
82+
// prepare flags of Msg2
83+
using Msg2Flags = tutorial21::field::Msg2Flags<ClientProtocolOptions>;
84+
Msg2Flags flags;
85+
flags.field_enumMem().value() = Msg2Flags::Field_enumMem::ValueType::V1;
86+
flags.field_intMem().value() = 111;
87+
88+
// Assign the flags
89+
msg.transportField_flags() = comms::field_cast<Message::TransportField_flags>(flags);
90+
91+
sendMessage(msg);
92+
}
93+
```
94+
95+
When the same messages are received back from the echo server, the cast is performed in opposite
96+
direction:
97+
```cpp
98+
void ClientSession::handle(Msg1& msg)
99+
{
100+
...
101+
using Msg1Flags = tutorial21::field::Msg1Flags<ClientProtocolOptions>;
102+
auto flags = comms::field_cast<Msg1Flags>(msg.transportField_flags());
103+
printSetField(flags);
104+
...
105+
}
106+
107+
void ClientSession::handle(Msg2& msg)
108+
{
109+
...
110+
using Msg2Flags = tutorial21::field::Msg2Flags<ClientProtocolOptions>;
111+
auto flags = comms::field_cast<Msg2Flags>(msg.transportField_flags());
112+
printEnumField(flags.field_enumMem());
113+
printIntField(flags.field_intMem());
114+
...
115+
}
116+
```
117+
118+
The **comms::field_cast()** function is defined in [comms/cast.h](https://commschamp.github.io/comms_doc/cast_8h.html)
119+
file. Relevant include statement may be required:
120+
```cpp
121+
#include "comms/cast.h"
122+
```
123+
124+
**IMPORTANT**: For the cast operation to be successful the field types must have the
125+
same length.
126+
127+
According to the function documentation, the [COMMS Library](https://github.com/commschamp/comms)
128+
performs some compile time analysis and will do a simple `static_cast` between the contained values
129+
if their value types are convertible. Otherwise the write + read operations will be performed, i.e. the source field
130+
will be written into a temporary buffer, and the target field will perform a read operation from that buffer.
131+
132+
In case of this tutorial the conversion between `BasicFlags` and `Msg1Flags` flags is simple `static_cast`
133+
(because their inner `ValueType` types are convertible),
134+
while conversion between `BasicFlags` and `Msg2Flags` will be performed using write to and read from
135+
a temporary buffer (because their inner `ValueType` types are NOT convertible).
136+
137+
## Summary
138+
139+
- Cast between fields of different types is performed using **comms::field_cast()**
140+
stand alone function defined in [comms/cast.h](https://commschamp.github.io/comms_doc/cast_8h.html)
141+
- For the cast to be successful the fields must be of the same length.
142+
- The [COMMS Library](https://github.com/commschamp/comms) implements the field cast using
143+
`static_cast` when field's inner `ValueType` types are convertible and uses temporary buffer
144+
with write / read operations when such conversion is not possible.
145+
146+
[Read Previous Tutorial](../tutorial20) &lt;-----------------------
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<schema name="tutorial21" endian="big">
3+
<fields>
4+
<string name="Msg1Name" defaultValue="Message 1" />
5+
<string name="Msg2Name" defaultValue="Message 2" />
6+
<string name="Msg3Name" defaultValue="Message 3" />
7+
8+
<enum name="MsgId" type="uint8" semanticType="messageId">
9+
<validValue name="M1" val="1" displayName="^Msg1Name" />
10+
<validValue name="M2" val="2" displayName="^Msg2Name" />
11+
<validValue name="M3" val="3" displayName="^Msg3Name" />
12+
</enum>
13+
14+
<int name="BasicFlags" type="uint8" displayName="Flags" />
15+
16+
<set name="Msg1Flags" length="1" forceGen="true">
17+
<bit name="B0" idx="0" />
18+
<bit name="B1" idx="1" />
19+
<bit name="B2" idx="2" />
20+
</set>
21+
22+
<bitfield name="Msg2Flags" forceGen="true">
23+
<enum name="EnumMem" type="uint8" bitLength="2">
24+
<validValue name="V0" val="0" />
25+
<validValue name="V1" val="1" />
26+
<validValue name="V2" val="2" />
27+
<validValue name="V3" val="3" />
28+
</enum>
29+
<int name="IntMem" type="uint8" bitLength="6" />
30+
</bitfield>
31+
32+
</fields>
33+
34+
<interface name="Interface" description="Common Interface for all the messages.">
35+
<ref field="BasicFlags" name="Flags" />
36+
</interface>
37+
38+
<frame name="Frame">
39+
<size name="Size">
40+
<int name="Field" type="uint16" displayName="Size" />
41+
</size>
42+
<id name="Id" field="MsgId" />
43+
<value name="Flags" field="BasicFlags" interfaceFieldName="Flags" />
44+
<payload name="Data" />
45+
</frame>
46+
47+
<message name="Msg1" id="MsgId.M1" displayName="^Msg1Name" />
48+
49+
<message name="Msg2" id="MsgId.M2" displayName="^Msg2Name" />
50+
51+
</schema>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Generated by commsdsl2comms v4.0.0
2+
3+
/// @file
4+
/// @brief Contains definition of <b>"Interface"</b> interface class.
5+
6+
#pragma once
7+
8+
#include <tuple>
9+
#include "comms/Message.h"
10+
#include "comms/options.h"
11+
#include "tutorial21/InterfaceCommon.h"
12+
#include "tutorial21/MsgId.h"
13+
#include "tutorial21/field/BasicFlags.h"
14+
#include "tutorial21/field/FieldBase.h"
15+
16+
namespace tutorial21
17+
{
18+
19+
20+
/// @brief Extra transport fields of @ref Interface interface class.
21+
/// @see @ref Interface
22+
/// @headerfile tutorial21/Interface.h
23+
struct InterfaceFields
24+
{
25+
/// @brief Definition of <b>"Flags"</b> field.
26+
using Flags =
27+
tutorial21::field::BasicFlags<
28+
tutorial21::options::DefaultOptions
29+
>;
30+
31+
32+
/// @brief All the fields bundled in std::tuple.
33+
using All = std::tuple<
34+
Flags
35+
>;
36+
};
37+
38+
/// @brief Definition of <b>"Interface"</b> common interface class.
39+
/// .incCommon Interface for all the messages. @n
40+
/// @tparam TOpt Interface definition options
41+
/// @headerfile tutorial21/Interface.h
42+
template <typename... TOpt>
43+
class Interface : public
44+
comms::Message<
45+
TOpt...,
46+
comms::option::def::BigEndian,
47+
comms::option::def::MsgIdType<tutorial21::MsgId>,
48+
comms::option::def::ExtraTransportFields<InterfaceFields::All>
49+
>
50+
{
51+
using Base =
52+
comms::Message<
53+
TOpt...,
54+
comms::option::def::BigEndian,
55+
comms::option::def::MsgIdType<tutorial21::MsgId>,
56+
comms::option::def::ExtraTransportFields<InterfaceFields::All>
57+
>;
58+
public:
59+
/// @brief Allow access to extra transport fields.
60+
/// @details See definition of @b COMMS_MSG_TRANSPORT_FIELDS_NAMES macro
61+
/// related to @b comms::Message class from COMMS library
62+
/// for details.
63+
///
64+
/// The generated values, types and functions are:
65+
/// @li @b TransportFieldIdx_flags index, @b TransportField_flags type
66+
/// and @b transportField_flags() access fuction for @ref InterfaceFields::Flags field.
67+
COMMS_MSG_TRANSPORT_FIELDS_NAMES(
68+
flags
69+
);
70+
71+
72+
};
73+
74+
} // namespace tutorial21
75+
76+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Generated by commsdsl2comms v4.0.0
2+
3+
/// @file
4+
/// @brief Contains common template parameters independent functionality of
5+
/// @ref tutorial21::Interface interface fields.
6+
7+
#pragma once
8+
9+
#include "tutorial21/field/BasicFlagsCommon.h"
10+
11+
namespace tutorial21
12+
{
13+
14+
15+
/// @brief Common types and functions for fields of
16+
/// @ref tutorial21::Interface interface.
17+
/// @see tutorial21::InterfaceFields
18+
struct InterfaceFieldsCommon
19+
{
20+
/// @brief Common types and functions for
21+
/// @ref tutorial21::InterfaceFields::Flags field.
22+
using FlagsCommon = tutorial21::field::BasicFlagsCommon;
23+
24+
};
25+
} // namespace tutorial21
26+
27+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Generated by commsdsl2comms v4.0.0
2+
3+
/// @file
4+
/// @brief Contains definition of message ids enumeration.
5+
6+
#pragma once
7+
8+
#include <cstdint>
9+
#include "tutorial21/Version.h"
10+
11+
namespace tutorial21
12+
{
13+
/// @brief Message ids enumeration.
14+
enum MsgId : std::uint8_t
15+
{
16+
MsgId_M1 = 1, ///< message id of <b>Message 1</b> message. ,
17+
MsgId_M2 = 2, ///< message id of <b>Message 2</b> message. ,
18+
MsgId_M3 = 3, ///< message id of <b>Message 3</b> message. ,
19+
20+
// --- Extra values generated for convenience ---,
21+
MsgId_FirstValue = 1, ///< First defined value.,
22+
MsgId_LastValue = 3, ///< Last defined value.,
23+
MsgId_ValuesLimit = 4, ///< Upper limit for defined values.
24+
};
25+
26+
} // namespace tutorial21
27+

0 commit comments

Comments
 (0)