首页 > 解决方案 > 使用预处理器生成消息类型

问题描述

我想在两个微控制器之间创建一个通信协议,我使用在系统之间合并消息类型的类逻辑。我想通过做一些非常简单的事情来开发一个更简单的过程来创建新消息,例如以下

BEGIN_MSG(LinearDriveMsg)
  ADD_2BIT(DriveSpeed, speed)
  ADD_16BIT(GMath::Position, position.x)
END_MSG

理想情况下,它将扩展到:

BEGIN_MSG(LinearDriveMsg)
  BEGIN_SERIALIZER
    ADD_SERIALIZER_2BIT(DriveSpeed, speed)
    ADD_SERIALIZER_16BIT(GMath::Position, position.x)
  END_SERIALIZER

  BEGIN_DESERIALIZER
    ADD_DESERIALIZER_2BIT(DriveSpeed, speed)
    ADD_DESERIALIZER_16BIT(GMath::Position, position.x)
  END_DESERIALIZER
END_MSG

然后展开为cpp代码

...

bool LinearDriveMsg::deserialize(const uint8_t *incoming_buffer, const uint8_t *incoming_size) override{
        if (*incoming_size != getMessageSize())
            return false;

        _speed = (DriveSpeed)((incoming_buffer[0] & SPEED_MASK) >> SPEED_OFFSET);
        weldTwoBytesToInt(&incoming_buffer[1], _position.x);
        return true;
}

int LinearDriveMsg::serialize(uint8_t *outgoing_buffer, uint8_t *outgoing_size) const override{
    if (*outgoing_size < getMessageSize())
        return -1;

    outgoing_buffer[0] |= ((uint8_t)_speed << SPEED_OFFSET) & SPEED_MASK;
    cutIntToTwoBytes(_position.x, outgoing_buffer + 1, 2);

    return getMessageSize();
}

...

我知道做一些高级预处理器的东西非常棘手,但也许有办法完成这项任务?也可以调整实际的cpp代码使其成为可能(这样效率还是可以的)

标签: c++serializationdeserializationmessagepreprocessor

解决方案


我会自己回答。实际上有一种“简单”的方法可以在预处理器中解决上述任务。boost 库提供了一些工具,可以像在普通语言中一样在预处理器中大致工作。他们有一个名为 preprocessor 的包作为 boost 套件的一部分阅读文档,您可以在此处下载它,它是主包的一部分。

帮助解决上述任务的主要特征是将数组元组转发到我们从函数式编程中知道的构造的想法。实际上是BOOST_PP_SEQ_FOR_EACH(macro, data, seq)。帮助解决该任务的另一个功能是这个家伙实现在每个元素周围创建双括号。

作为想法,它将像以下那样工作。我有上面的元组列表:

#define frame_composition (begin_byte(0), begin_bit(0), int8_t, speed)\
    (begin_byte(1), begin_bit(4), int16_t, direction)\

我有某种动作序列,因为我想先串行,然后再串行:

#define COMPOSE_FRAME(seq) \
    ADD_SERIAL(seq) \
    ADD_DESERIAL(seq)

现在每个添加类别的内容:

#define ADD_SERIAL(seq) \
    BOOST_PP_SEQ_FOR_EACH(ADD_SERIAL_ELEM, ~, GLK_PP_SEQ_DOUBLE_PARENS(seq))
#define ADD_DESERIAL(seq) \
    BOOST_PP_SEQ_FOR_EACH(ADD_DESERIAL_ELEM, ~, GLK_PP_SEQ_DOUBLE_PARENS(seq)) \

很酷的是,我们将相同的元组列表转发给不同的结构,以执行一些不同的算法。现在 X_ELEM 的内容定义了:

#define ADD_SERIAL_ELEM(seq) \
    outgoing_buffer[BOOST_PP_TUPLE_ELEM( 0, elem)] = (uint8_t) BOOST_PP_TUPLE_ELEM( 3, elem); 
#define ADD_DESERIAL_ELEM(seq) \
    BOOST_PP_TUPLE_ELEM(3,elem) = (BOOST_PP_TUPLE_ELEM(2,elem)) incoming_buffer[BOOST_PP_TUPLE_ELEM(0,elem)];

如果您需要,您可以区分元素“功能”中的不同用例

BOOST_PP_IF(cond, t, f)

例如对对象和简单变量做不同的事情

这现在不是完整的解决方案,但要看到这个想法,并且可以从 cpp 创建高度可移植的简单消息生成器框架。


推荐阅读