C++模板元编程实现序列化与反序列化

这篇博客介绍了如何利用C++的模板元编程技术来实现序列化和反序列化功能,支持的基本类型包括char、short、int、long、float、double及无符号整数,同时支持std::string和std::vector,对于std::vector则支持存储基本类型和字符串。内容涵盖序列化的简介、具体实现以及通过多个使用示例展示了其基本和嵌套的用法。

序列化是编程中一个非常实用的技术,它能够将对象转换为可以存储或传输的格式,而反序列化则是将这种格式重新转换为对象的过程。今天,我将分享如何利用C++的模板元编程技术来实现一个简单而强大的序列化库。

一、什么是序列化?

想象一下,你需要将一个C++对象保存到文件中,或者通过网络发送给另一台计算机。序列化就是将对象转换为字节流的过程,而反序列化则是将这些字节流重新构建为原始对象。

本文实现的序列化库支持以下数据类型:

  • 基本类型:char、short、int、long、float、double以及无符号整数;
  • 字符串:std::string
  • 向量:std::vector,支持vector<基本类型>vector<字符串>vector<struct>
  • 嵌套结构:支持复杂的嵌套数据结构。

二、技术原理

2.1 模板元编程基础

模板元编程(Template Metaprogramming,TMP)是C++中一种在编译期执行计算的技术。它利用模板特化、递归实例化等机制,在编译期间生成代码,从而实现类型安全的通用编程。

在我们的序列化库中,模板元编程主要体现在以下几个方面:

  1. 编译期类型判断​​:使用 std::is_class_v在编译期判断类型是否为类类型。
  2. ​模板特化​​:为不同类型提供特定的序列化实现。
  3. 递归模板​​:处理可变参数和嵌套结构。

2.2 类型分发机制

类型分发是序列化库的核心技术,它根据不同的类型选择不同的序列化策略:

template <bool cond, typename T>
class IF {
    // 类类型的序列化策略
};

template <typename T>
class IF<false, T> {
    // 基本类型的序列化策略
};

template <typename T>
inline size_t valsize(const T& val) {
    return IF<std::is_class_v<T>, T>::_val_size(val);
}

这种设计实现了:

  • 零运行时开销​​:所有类型判断在编译期完成。
  • 类型安全​​:错误的类型使用会在编译期报错。
  • 可扩展性​​:添加新类型只需提供相应的特化版本。

2.3 序列化协议设计

2.3.1 数据包格式

我们的序列化数据包采用简单的结构:

----------------------
|  包长度  |    数据   |
|---------------------|
|   int    |   char[] |
----------------------

这种设计具有以下优点:

  • 快速长度检查​​:通过包长度可以快速验证数据完整性。
  • 内存预分配​​:知道总长度后可以一次性分配足够内存。
  • 错误检测​​:反序列化时可以通过长度验证数据有效性。

2.3.2 类型编码规则

数据类型符号表示排列方式(len(x)为unsigned int)
基本类型TT
字符串Slen(S) + S
基本类型向量V<T>len(V) + T1 + T2 + ... + Tn
字符串向量V<S>len(V) + len(S1) + S1 + len(S2) + S2 + ... + len(Sn) + Sn

这种编码规则确保了:

  • 自描述性​​:数据本身包含了解析所需的信息。
  • 可扩展性​​:支持嵌套和复杂数据结构。
  • 平台无关性​​:使用固定字节序和明确的大小表示。

三、核心代码实现

// Serialization.hpp

/* 序列化说明
 * 1.包组成:
 *    -----------------------
 *   |  包长度  |    数据    |
 *   |-----------------------|
 *   |   int    |   char[]   |
 *    -----------------------
 * 2.数据组成
 *    -------------------------------------------------------------------------------------
 *   |     类型     | 符号表示 | 排列方式,其中len(x)的类型为unsigned int                    |
 *   |-------------------------------------------------------------------------------------|
 *   |   基本类型   |    T     | T                                                         |
 *   |    字符串    |    S     | len(S) + S                                                |
 *   | 基本类型向量 |   V<T>   | len(V) + T1 + T2 + T3 + ... + Tn                          |
 *   |  字符串向量  |   V<S>   | len(V) + len(S1) + S1 + len(S2) + S2 + ... + len(Sn) + Sn |
 *    -------------------------------------------------------------------------------------
 */
#ifndef SERIALLIZATION_H_
#define SERIALLIZATION_H_
#include <string>
#include <vector>

#define DEFINE_PACK(...)                                                \
public:                                                                 \
    size_t pack_size() const {                                          \
        return sizeof(int) + Ser::all_size(__VA_ARGS__);                \
    }                                                                   \
    std::string to_string() const {                                     \
        const size_t _count = sizeof(int) + Ser::all_size(__VA_ARGS__); \
        std::string _result;                                            \
        _result.reserve(_count);                                        \
        Ser::push(_result, (int) _count);                               \
        Ser::pack(_result, __VA_ARGS__);                                \
        return _result;                                                 \
    }                                                                   \
    bool from_string(const char* const _buf, const int _len) {          \
        if (_len < sizeof(int))                                         \
            return false;                                               \
        const char* _ptr = _buf;                                        \
        int _count = 0;                                                 \
        Ser::take(_count, _ptr);                                        \
        if (_count != _len)                                             \
            return false;                                               \
        Ser::unpack(_ptr, __VA_ARGS__);                                 \
        return true;                                                    \
    }                                                                   \
    bool from_string(const std::string& _buf) {                         \
        return from_string(_buf.data(), _buf.size());                   \
    }


namespace Ser {
    template <bool cond, typename T>
    class IF {
    public:
        static inline size_t _val_size(const T& val) {
            return val.pack_size();
        }
        static inline void _push(std::string& buf, const T& val) {
            buf.append(val.to_string());
        }
        static inline void _take(T& val, const char*& buf) {
            const int count = *(reinterpret_cast<const int*>(buf));
            val.from_string(buf, count);
            buf += count;
        }
    };

    template <typename T>
    class IF<false, T> {
    public:
        static inline size_t _val_size(const T& val) {
            return sizeof(T);
        }
        static inline void _push(std::string& buf, const T& val) {
            const char* ptr = reinterpret_cast<const char*>(&val);
            buf.append(ptr, sizeof(T));
        }
        static inline void _take(T& val, const char*& buf) {
            val = *(reinterpret_cast<const T*>(buf));
            buf += sizeof(T);
        }
    };

    /* 计算单个元素序列化后的长度 */
    template <typename T>
    inline size_t valsize(const T& val) {
        return IF<std::is_class_v<T>, T>::_val_size(val);
    }

    template <>
    inline size_t valsize<std::string>(const std::string& val) {
        return sizeof(unsigned int) + val.size();
    }

    template <typename T>
    size_t valsize(const std::vector<T>& val) {
        size_t len = sizeof(unsigned int);
        for (size_t i = 0; i < val.size(); ++i) {
            len += valsize(val[i]);
        }
        return len;
    }

    /* 序列化单个元素 */
    template <typename T>
    inline void push(std::string& buf, const T& val) {
        IF<std::is_class_v<T>, T>::_push(buf, val);
    }

    template <>
    inline void push<std::string>(std::string& buf, const std::string& val) {
        const unsigned int len = static_cast<unsigned int>(val.size());
        buf.append(reinterpret_cast<const char*>(&len), sizeof(unsigned int));
        buf.append(val);
    }

    template <typename T>
    void push(std::string& buf, const std::vector<T>& val) {
        const unsigned int len = static_cast<unsigned int>(val.size());
        push(buf, len);
        for (unsigned int i = 0; i < len; ++i) {
            push(buf, val[i]);
        }
    }

    /* 反序列化单个元素 */
    template <typename T>
    inline void take(T& val, const char*& buf) {
        IF<std::is_class_v<T>, T>::_take(val, buf);
    }

    template <>
    inline void take<std::string>(std::string& val, const char*& buf) {
        unsigned int len = 0;
        take(len, buf);
        val.clear();
        val.append(buf, len);
        buf += len;
    }

    template <typename T>
    void take(std::vector<T>& val, const char*& buf) {
        unsigned int len = 0;
        take(len, buf);
        val.resize(len);
        for (unsigned int i = 0; i < len; ++i) {
            take(val[i], buf);
        }
    }

    /* 计算不定参序列化后总长度 */
    template <typename T>
    size_t all_size(const T& elem) {
        return valsize(elem);
    }

    template <typename T, typename... Args>
    size_t all_size(const T& elem, const Args&... args) {
        return valsize(elem) + all_size(args...);
    }

    /* 不定参序列化 */
    template <typename T>
    void pack(std::string& buf, const T& elem) {
        push(buf, elem);
    }

    template <typename T, typename... Args>
    void pack(std::string& buf, const T& elem, const Args&... args) {
        push(buf, elem);
        pack(buf, args...);
    }

    /* 不定参反序列化 */
    template <typename T>
    void unpack(const char*& buf, T& elem) {
        take(elem, buf);
    }

    template <typename T, typename... Args>
    void unpack(const char*& buf, T& elem, Args&... args) {
        take(elem, buf);
        unpack(buf, args...);
    }
} // namespace Ser

#endif /* SERIALLIZATION_HPP_ */

四、使用示例

4.1基本使用

#include "Serialization.hpp"

class Message {
public:
	std::string type;      // 类型
	std::string msg;       // 内容
	int         priority;  // 优先级
	long long   id;        // 序号
	std::string ip;        // IP

	DEFINE_PACK(type, msg, priority, id, ip)
};

 
int main() {
    Message m = { "type", "test", 2, 56, "192.168.0.2" };
    // 序列化
    std::string buf = m.to_string();
    // 反序列化
    Message res;
    res.from_string(buf);

	return 0;
}

4.2嵌套使用

#include "Serialization.hpp"

class Base {
public:
    std::string name;
    int id;
    std::vector<double> values;

    DEFINE_PACK(name, id, values)
};

class FullInfo {
public:
    std::string name;
    float val;
    std::vector<std::vector<Base>> vec;

    DEFINE_PACK(name, val, vec)
};


int main() {
    FullInfo obj;
    obj.name = "Hello";
    obj.val = 3.14F;
    obj.vec = {
        {
            {"elem 1", 1, {1.1, 1.2, 1.3, 1.4}},
            {"elem 2", 2, {2.1, 2.2}},
            {"elem 3", 3, {3.1, 3.2, 3.3}}
        },
        {
            {"elem 4", 4, {4.1, 4.2, 4.3, 4.4}},
            {"elem 5", 5, {5.1, 5.2}},
            {"elem 6", 6, {6.1, 6.2, 6.3, 6.4, 6.5}}
        }
    };
    // 序列化
    const std::string buf = obj.to_string();
    // 反序列化
    FullInfo res{};
    res.from_string(buf);

    return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值