当前位置: 首页 > article >正文

Qt开发技术【C++ 实现类的二进制序列化与反序列化】

一、思考 Qt 本身的QByteArray 和QDataStream

QDataStream和QByteArray是Qt框架中用于数据序列化和反序列化的类。QDataStream可以将Qt数据类型(如QString、QByteArray等)序列化为二进制格式,并写入文件或网络流中。同时,也可以从文件或网络流中读取二进制数据并反序列化成相应的数据类型。

但是在嵌入式中使用代码比较冗余

二、实现一个比较简单的仅对部分类型进行序列化的类

例如常用的uint8_t uint16_t uint32_t 嵌入式通信常用的几种类型

想法是继承 class CBinarySerializer 实习序列化函数
virtual bool Serialize2Buffer(bool bIsSerialize)

/*
* @brief 二进制序列化类
*/
class CBinarySerializer
{
public:
    enum E_BYTE_ORDER
    {
        EBO_LITTLE_ENDIAN,
        EBO_BIG_ENDIAN,
    };
    CBinarySerializer()
    {
        m_eByteOrder = EBO_BIG_ENDIAN;
    }

    virtual ~CBinarySerializer() = default;

    virtual bool Serialize()
    {
        return Serialize2Buffer(true);
    }

    virtual bool Deserialize()
    {
        return Serialize2Buffer(false);
    }

    virtual bool Serialize2Buffer(bool bIsSerialize)
    {
        return false;
    }

    std::vector<uint8_t> GetBuffer() const
    {
        return m_vecBuffer;
    }

    void SetByteOrder(E_BYTE_ORDER eByteOrder)
    {
        m_eByteOrder = eByteOrder;
    }

    bool IsLittleEndian() const
    {
        return m_eByteOrder == EBO_LITTLE_ENDIAN;
    }

    void ClearBuffer()
    {
        m_vecBuffer.clear();
    }

    virtual size_t GetSize() const
    {
        return 0;
    }

    std::vector<uint8_t> m_vecBuffer;
    E_BYTE_ORDER m_eByteOrder;
};

三、 定义部分类型序列化的实现类

class CBinaryImplement
{
public:
    static bool UCharToBuffer(std::vector<uint8_t>& vecBuffer, uint8_t u8Value)
    {
        vecBuffer.push_back(u8Value);
        return true;
    }

    static bool BufferToUChar(std::vector<uint8_t>& vecBuffer, uint8_t& u8Value)
    {
        if (vecBuffer.size() < 1)
        {
            return false;
        }
        u8Value = vecBuffer.front();
        vecBuffer.erase(vecBuffer.begin());
        return true;
    }

    static void UShortToBuffer(std::vector<uint8_t>& vecBuffer, uint16_t u16Value, CBinarySerializer::E_BYTE_ORDER eByteOrder)
    {
        if (eByteOrder == CBinarySerializer::EBO_LITTLE_ENDIAN)
        {
            vecBuffer.push_back(u16Value & 0xFF);
            vecBuffer.push_back((u16Value >> 8) & 0xFF);
        }
        else
        {
            vecBuffer.push_back((u16Value >> 8) & 0xFF);
            vecBuffer.push_back(u16Value & 0xFF);
        }
    }

    static bool BufferToUShort(std::vector<uint8_t>& vecBuffer, uint16_t& u16Value, CBinarySerializer::E_BYTE_ORDER eByteOrder)
    {
        if (vecBuffer.size() < 2)
        {
            return false;
        }
        if (eByteOrder == CBinarySerializer::EBO_LITTLE_ENDIAN)
        {
            u16Value = (vecBuffer[0] & 0xFF) | ((vecBuffer[1] & 0xFF) << 8);
        }
        else
        {
            u16Value = (vecBuffer[1] & 0xFF) | ((vecBuffer[0] & 0xFF) << 8);
        }
        vecBuffer.erase(vecBuffer.begin(), vecBuffer.begin() + 2);
        return true;
    }

    static void UIntToBuffer(std::vector<uint8_t>& vecBuffer, uint32_t u32Value, CBinarySerializer::E_BYTE_ORDER eByteOrder)
    {
        if (eByteOrder == CBinarySerializer::EBO_LITTLE_ENDIAN)
        {
            vecBuffer.push_back(u32Value & 0xFF);
            vecBuffer.push_back((u32Value >> 8) & 0xFF);
            vecBuffer.push_back((u32Value >> 16) & 0xFF);
            vecBuffer.push_back((u32Value >> 24) & 0xFF);
        }
        else
        {
            vecBuffer.push_back((u32Value >> 24) & 0xFF);
            vecBuffer.push_back((u32Value >> 16) & 0xFF);
            vecBuffer.push_back((u32Value >> 8) & 0xFF);
            vecBuffer.push_back(u32Value & 0xFF);
        }
    }

    static bool BufferToUInt(std::vector<uint8_t>& vecBuffer, uint32_t& u32Value, CBinarySerializer::E_BYTE_ORDER eByteOrder)
    {
        if (vecBuffer.size() < 4)
        {
            return false;
        }
        if (eByteOrder == CBinarySerializer::EBO_LITTLE_ENDIAN)
        {
            u32Value = (vecBuffer[0] & 0xFF) | ((vecBuffer[1] & 0xFF) << 8) | ((vecBuffer[2] & 0xFF) << 16) | ((vecBuffer[3] & 0xFF) << 24);
        }
        else
        {
            u32Value = (vecBuffer[3] & 0xFF) | ((vecBuffer[2] & 0xFF) << 8) | ((vecBuffer[1] & 0xFF) << 16) | ((vecBuffer[0] & 0xFF) << 24);
        }
        vecBuffer.erase(vecBuffer.begin(), vecBuffer.begin() + 4);
        return true;
    }

    static void strToBuffer(std::vector<uint8_t>& vecBuffer, const std::string& strValue)
    {
        for (auto u8Char : strValue)
        {
            vecBuffer.push_back(u8Char);
        }
    }

    /**
     * @brief 将缓冲区中的数据转换为字符串
     * @param vecBuffer 输入的缓冲区
     * @param strValue 输出的字符串
     * @param nLen 要读取的字节数,默认为-1,表示读取整个缓冲区
     * @return bool 转换是否成功
     */
    static bool BufferToStr(std::vector<uint8_t>& vecBuffer, std::string& strValue, size_t nLen = -1)
    {
        if (vecBuffer.empty() && nLen > 0)
        {
            return false;
        }
        strValue.clear();
        if (nLen == -1)
        {
            for (auto u8Char : vecBuffer)
            {
                strValue.push_back(u8Char);
            }
            vecBuffer.clear();
        }
        else
        {
            if (vecBuffer.size() < nLen)
            {
                return false;
            }
            for (size_t i = 0; i < nLen; ++i)
            {
                strValue.push_back(vecBuffer[i]);
            }
            vecBuffer.erase(vecBuffer.begin(), vecBuffer.begin() + nLen);
        }
        return true;
    }

    static bool ObjToBuffer(std::vector<uint8_t>& vecBuffer, CBinarySerializer& obj)
    {
        if (!obj.Serialize2Buffer(true))
        {
            return false;
        }
        vecBuffer.insert(vecBuffer.end(), obj.m_vecBuffer.begin(), obj.m_vecBuffer.end());
        return true;
    }

    static bool BufferToObj(std::vector<uint8_t>& vecBuffer, CBinarySerializer& obj)
    {
        if (vecBuffer.empty() || obj.GetSize() > vecBuffer.size())
        {
            return false;
        }
        obj.m_vecBuffer.insert(obj.m_vecBuffer.begin(), vecBuffer.begin(), vecBuffer.begin() + obj.GetSize());
        if (!obj.Serialize2Buffer(false))
        {
            return false;
        }
        return true;
    }
};

四、定义便以使用的Serialize2Buffer函数实现宏

#define BINARY_SERIALIZE_BEGIN_1(ClassName) \
    public: \
        bool Serialize2Buffer(bool bIsSerialize) override \
        { 


#define BINARY_SERIALIZE_UCHAR(Var) \
    if (bIsSerialize) \
    { \
        if (!CBinaryImplement::UCharToBuffer(m_vecBuffer, Var)) \
        { \
            return false; \
        } \
    } \
    else \
    { \
        if (!CBinaryImplement::BufferToUChar(m_vecBuffer, Var)) \
        { \
            return false; \
        } \
    }


#define BINARY_SERIALIZE_USHORT(Var) \
    if (bIsSerialize) \
    { \
        CBinaryImplement::UShortToBuffer(m_vecBuffer, Var, m_eByteOrder); \
    } \
    else \
    { \
        if (!CBinaryImplement::BufferToUShort(m_vecBuffer, Var, m_eByteOrder)) \
        { \
            return false; \
        } \
    }

#define BINARY_SERIALIZE_UINT(Var) \
    if (bIsSerialize) \
    { \
        CBinaryImplement::UIntToBuffer(m_vecBuffer, Var, m_eByteOrder); \
    } \
    else \
    { \
        if (!CBinaryImplement::BufferToUInt(m_vecBuffer, Var, m_eByteOrder)) \
        { \
            return false; \
        } \
    }

#define BINARY_SERIALIZE_STR(Var, Len) \
    if (bIsSerialize) \
    { \
        CBinaryImplement::strToBuffer(m_vecBuffer, Var); \
    } \
    else \
    { \
        if (!CBinaryImplement::BufferToStr(m_vecBuffer, Var, Len)) \
        { \
            return false; \
        } \
    }


#define BINARY_SERIALIZE_OBJ(Obj)\
    if (bIsSerialize) \
    { \
        if (!CBinaryImplement::ObjToBuffer(m_vecBuffer, Obj)) \
        { \
            return false; \
        } \
    } \
    else \
    { \
        if (!CBinaryImplement::BufferToObj(m_vecBuffer, Obj)) \
        { \
            return false; \
        } \
    }

#define BINARY_SERIALIZE_END_1() \
        return true; \
    } \

需要序列化的类中 继承 CBinarySerializer 增加宏 就可以不用单独写序列化的代码

五、动物园测试类

class CIcdBird : public CBinarySerializer
{
public:
    enum E_BIRD_TYPE
    {
        EBT_EAGLE, // 老鹰
        EBT_FLAMINGO, // 灰狼
    };

    size_t GetSize() const override
    {
        return sizeof(m_u8BirdType) + sizeof(m_u16BirdAge) + sizeof(m_u32BirdId);
    }

    CIcdBird() = default;
    virtual ~CIcdBird() = default;

    BINARY_SERIALIZE_BEGIN_1(CIcdBird)
        BINARY_SERIALIZE_UCHAR(m_u8BirdType)
        BINARY_SERIALIZE_USHORT(m_u16BirdAge)
        BINARY_SERIALIZE_UINT(m_u32BirdId)
    BINARY_SERIALIZE_END_1()

    uint8_t m_u8BirdType;
    uint16_t m_u16BirdAge;
    uint32_t m_u32BirdId;
    std::string m_strBirdName;
};

class CIcdZoo : public CBinarySerializer
{
public:
    CIcdZoo() = default;
    virtual ~CIcdZoo() = default;

    BINARY_SERIALIZE_BEGIN_1(CIcdZoo)
        BINARY_SERIALIZE_UINT(m_u32Z00Id)
        BINARY_SERIALIZE_OBJ(m_iBird)
    BINARY_SERIALIZE_END_1()

    uint32_t m_u32Z00Id;
    CIcdBird m_iBird;
};


/**
 * @brief 测试二进制序列化功能
 *
 * 该函数用于测试二进制序列化的相关功能,包括创建和序列化鸟类和动物园对象。
 *
 * @return int 返回测试结果,通常为0表示成功,非0表示失败。
 */
void TestBinary()
{
    CIcdBird icdBird;
    icdBird.m_u8BirdType = CIcdBird::EBT_EAGLE;
    icdBird.m_u16BirdAge = 3;
    icdBird.m_u32BirdId = 33;
    icdBird.m_strBirdName = "Eagle";
    icdBird.Serialize();
    std::vector<uint8_t> vecBirdBuffer = icdBird.GetBuffer();
    for (auto u8BirdByte : vecBirdBuffer)
    {
        printf("%02X ", u8BirdByte);
    }
    printf("\n");
    CIcdBird icdBird2;
    icdBird2.m_vecBuffer = vecBirdBuffer;
    icdBird2.Deserialize();
    std::cout << icdBird2.m_u32BirdId << std::endl;


    CIcdZoo icdZoo;
    icdZoo.m_u32Z00Id = 55;
    icdZoo.m_iBird = icdBird2;
    icdZoo.Serialize();
    vecBirdBuffer = icdZoo.GetBuffer();
    for (auto u8BirdByte : vecBirdBuffer)
    {
        printf("%02X ", u8BirdByte);
    }
    printf("\n");
    std::cout << icdZoo.m_u32Z00Id << std::endl;
    std::cout << icdZoo.m_iBird.m_u32BirdId << std::endl;
}

CIcdBird 继承与 CBinarySerializer 增加宏后不需要额外代码就可以序列化

六、测试结果

在这里插入图片描述
序列化和反序列化都没有问题,继续完善类型


http://www.kler.cn/a/511134.html

相关文章:

  • 使用vcpkg安装c++库时出现git网络连接报错的解决方案
  • LeetCode:46.全排列
  • doris:Kafka 导入数据
  • 异地IP属地代理业务解析:如何改变IP属地
  • 日志技术-LogBack入门程序Log配置文件日志级别
  • 满足不同场景的需求的智慧物流开源了
  • 和鲸科技受邀出席 2024(第四届)“风电领跑者”技术创新论坛
  • @Bean 控制 Spring Bean 生命周期
  • JavaScript语言的正则表达式
  • VSCODE SSH远程连接报错或无法联网安装.vscode-server
  • 深度学习篇---数据集分类
  • 【Unity3D】利用Hinge Joint 2D组件制作绳索效果
  • “深入浅出”系列之数通篇:(3)负载均衡
  • 【Linux】进程间通信IPC
  • 1.19学习记录
  • Amazon MSK 开启 Public 访问 SASL 配置的方法
  • 如何将自己本地项目开源到github上?
  • 2.6 聚焦:Word Embedding
  • 【UNION与UNION ALL的区别?】
  • 基于Java的语音陪聊软件——支持聊天私聊-礼物系统-直播系统-缘分匹配-游戏陪玩