惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
Scott Helme
Scott Helme
D
DataBreaches.Net
人人都是产品经理
人人都是产品经理
N
Netflix TechBlog - Medium
美团技术团队
T
Tailwind CSS Blog
博客园 - 叶小钗
小众软件
小众软件
云风的 BLOG
云风的 BLOG
S
SegmentFault 最新的问题
爱范儿
爱范儿
博客园 - Franky
量子位
I
InfoQ
博客园 - 司徒正美
Hugging Face - Blog
Hugging Face - Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 【当耐特】
博客园_首页
Recorded Future
Recorded Future
The Register - Security
The Register - Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Recent Announcements
Recent Announcements
Martin Fowler
Martin Fowler
C
CERT Recently Published Vulnerability Notes
Know Your Adversary
Know Your Adversary
P
Proofpoint News Feed
A
About on SuperTechFans
阮一峰的网络日志
阮一峰的网络日志
Cisco Talos Blog
Cisco Talos Blog
腾讯CDC
WordPress大学
WordPress大学
NISL@THU
NISL@THU
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
C
Cisco Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
酷 壳 – CoolShell
酷 壳 – CoolShell
Cyberwarzone
Cyberwarzone
The GitHub Blog
The GitHub Blog
T
The Blog of Author Tim Ferriss
The Hacker News
The Hacker News
N
News and Events Feed by Topic
Y
Y Combinator Blog
T
Troy Hunt's Blog
A
Arctic Wolf
V
V2EX
S
Secure Thoughts

博客园 - 于光远

泛型编程 分区理论知识整理 SPI协议及驱动开发框架 I2C协议及驱动开发框架 使用qemu 加载linux-6.18.1内核 linux 内核 策略模式 单例模式 Linux性能分析:从监控到优化的完整工具链 引用 bootchart linux 脚本打印时间 接口编程 不定参数解析,结合tuple 独立于main运行的程序 类模板继承 std::ostream & operator<< Flash HIDL --HelloWorld
C++模板元编程实现序列化与反序列化
于光远 · 2025-11-05 · via 博客园 - 于光远

https://blog.csdn.net/kpengk/article/details/118070178

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

一、什么是序列化?


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

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

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

二、技术原理

2.1 模板元编程基础


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

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

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

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 类型编码规则

image

这种编码规则确保了:

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

三、核心代码实现

// 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;
}