cc_tutorial/tutorials/tutorial25 at master · commschamp/cc_tutorial

Tutorial 25

Dealing with big protocols.

Most of the binary communication protocols use numeric message ID information reported in the message frame. The CommsDSL specification provides <id> framing layer for this purpose, while the generated code uses comms::frame::MsgIdLayer class to implement the required functionality. During the read operation the comms::frame::MsgIdLayer class is responsible to provide the functionality for reading the message ID and create appropriate message object before forwarding the request to the next framing layer. The default mapping of the numeric message ID to the actual message type is implemented using comms::MsgFactory class. The latter receives a tuple of all the message classes it is expected to support as its second template parameter and uses multiple meta-programming techniques to analyze the provided messages at compile-time and create proper mapping of numeric message IDs to their respective actual types. Such compile time analysis may incur a significant burden on compilation time as well as memory consumption of the compiler. Depending on the development machine characteristics, the compilation performance issues may get noticeable when number of the messages approaches 30 - 50. In some specific cases (like small RAM and/or compiler being 32 bit application) the compilation may report an error due to being out of heap space.

To mitigate this problem / annoyance the v6.1 of the commsdsl2comms code generator added extra switch based message factories in the <protocol_namespace>/factory folder.

The DynMem part of the name implies dynamic memory allocation.

Note that in this tutorial sender property hasn't been used, i.e. all the messages are sent both ways. It means that all the message factories mentioned above contain the same code. For the cases when uni-directional messages are present they'll differ.

The comms::frame::MsgIdLayer layer provided by the COMMS Library allows replacing the default message factory (comms::MsgFactory) using the comms::option::app::MsgFactory or comms::option::app::MsgFactoryTempl application specific customization options.

The v6.1 of the commsdsl2comms code generator also generates extra protocol options that can be used to apply the relevant option to the comms::frame::MsgIdLayer class:

Let's take a look how the protocol options above can be used to force usage of the more efficient message factories.

The ServerSession defines its protocol options like this:

using ServerProtocolOptions =
    tutorial25::options::ServerInputMessagesDynMemMsgFactoryDefaultOptionsT<
        tutorial25::options::ServerDefaultOptions
    >;

The definition used by the ClientSession is very similar:

using ClientProtocolOptions =
    tutorial25::options::ClientInputMessagesDynMemMsgFactoryDefaultOptionsT<
        tutorial25::options::ClientDefaultOptions
    >;

When it comes to aliasing of the message types, the v6.1 of the commsdsl2comms code generator added convenience macros to the definitions of the input messages:

Please take a look at the definition:

/// @brief Create type aliases for the all messages of the protocol.
/// @param prefix_ Prefix of the alias message type.
/// @param suffix_ Suffix of the alias message type.
/// @param interface_ Type of the common message interface.
/// @param opts_ Type of the used protocol definition options.
#define TUTORIAL25_ALIASES_FOR_ALL_MESSAGES(prefix_, suffix_, interface_, opts_) \
    using prefix_ ## Msg1 ## suffix_ = tutorial25::message::Msg1<interface_, opts_>; \
    using prefix_ ## Msg2 ## suffix_ = tutorial25::message::Msg2<interface_, opts_>; \
    ...

The ClientSession uses the TUTORIAL25_ALIASES_FOR_ALL_MESSAGES macro to alias all message types:

TUTORIAL25_ALIASES_FOR_ALL_MESSAGES(,,Message,ClientProtocolOptions);

Please note the empty prefix_ and suffix_ parameters.

It is equivalent of having the following alias types defined:

using Msg1 = tutorial25::message::Msg1<Message, ClientProtocolOptions>;
using Msg2 = tutorial25::message::Msg2<Message, ClientProtocolOptions>;
using Msg3 = tutorial25::message::Msg3<Message, ClientProtocolOptions>;
...

These alias types are used when sending messages to the server:

void ClientSession::sendMsg1()
{
    Msg1 msg;
    sendMessage(msg); // Should get received and echoed back
}

void ClientSession::sendMsg2()
{
    Msg2 msg;
    sendMessage(msg); // Should get received and echoed back
}

...

Summary

  • As the number of messages in the protocol grow, the burden on the compilation time and memory consumption can grow exponentially.
  • When number of the messages exceeds 30-50 messages, it is recommended to use more optimized generated message factories.
  • The message factory classes reside in the <protocol_namespace>/factory folder.
  • The <protocol_namespace>/options folder will also contain new relevant option classes which can be used to force usage of the new factories.
  • The files containing definitions of relevant input messages in the <protocol_namespace>/input folder also contain definition of the macros helping with message type aliasing.

Read Previous Tutorial <-----------------------> Read Next Tutorial