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

Cereal中支持QString、QVector、QList、QMap

在Cereal中支持Qt容器序列化


🌟 Cereal序列化库对Qt容器的完美支持

关键词Cereal Qt序列化 QString QMap 跨平台数据交换


核心代码

namespace cereal
{#if 0//序列化在xml下会多生成一个节点// 为 QString 定义序列化函数template <class Archive>void load(Archive& archive, QString& str) {// 转换为 std::string 以兼容 cerealstd::string utf8Str;archive(utf8Str);str = QString::fromStdString(utf8Str);}template <class Archive>void save(Archive& archive, const QString& str) {// 转换为 std::string 以兼容 cerealstd::string utf8Str = str.toStdString();archive(utf8Str);}
#else// 避免XML中生成<value0>节点//! saving string to xmlvoid CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive& ar, QString const& str){ar.saveValue(str.toStdString());}//! loading string from xmlvoid CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive& ar, QString& str){std::string temp;ar.loadValue(temp);str = QString::fromStdString(temp);}
#endif// 保存 QVectortemplate <class Archive, typename T>typename std::enable_if<!Archive::is_loading::value, void>::typeserialize(Archive& ar, QVector<T>& vec) {ar(make_size_tag(vec.size())); // number of elementsfor (auto&& item : vec) {ar(item);}}// 加载 QVectortemplate <class Archive, typename T>typename std::enable_if<Archive::is_loading::value, void>::typeserialize(Archive& ar, QVector<T>& vec) {size_type size;ar(make_size_tag(size));vec.resize(size);for (auto&& item : vec) {ar(item);}}// 保存 QListtemplate <class Archive, typename T>typename std::enable_if<!Archive::is_loading::value, void>::typeserialize(Archive& ar, QList<T>& list) {ar(make_size_tag(list.size())); // number of elementsfor (auto const& item : list) {ar(item);}}// 加载 QListtemplate <class Archive, typename T>typename std::enable_if<Archive::is_loading::value, void>::typeserialize(Archive& ar, QList<T>& list) {size_type size;ar(make_size_tag(size));std::list<T> list_temp;list_temp.resize(size);for (auto&& item : list_temp) {ar(item);}
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)list = QList<T>(list_temp.begin(), list_temp.end());
#elselist = QList<T>::fromStdList(list_temp);
#endif}// 保存 QMaptemplate <class Archive, typename K, typename V>void save(Archive& ar, const QMap<K, V>& map) {ar(make_size_tag(static_cast<size_type>(map.size()))); // 保存元素数量for (auto it = map.constBegin(); it != map.constEnd(); ++it) {// 显式构造键值对并命名ar(make_map_item(it.key(), it.value()));}}// 加载 QMaptemplate <class Archive, typename K, typename V>void load(Archive& ar, QMap<K, V>& map) {size_type size;ar(make_size_tag(size)); // 读取元素数量map.clear();for (size_t i = 0; i < size; ++i) {K key;V value;ar(make_map_item(key, value)); // 按顺序加载键值对map.insert(key, value); // 插入到 QMap}}
};

🔧 问题背景

Cereal作为轻量级C++11序列化库,默认支持STL容器但不直接兼容Qt容器(如QStringQVector等)。当序列化Qt类型时,开发者需手动实现适配逻辑,否则会导致:

  1. 冗余嵌套节点(如XML中的<value0>
  2. 编码错误(未处理UTF-8转换)
  3. 容器大小丢失(动态容器反序列化失败)

⚙️ Qt容器序列化实现方案

1. QString的序列化优化

通过特化XMLInputArchive/XMLOutputArchive避免冗余节点:

namespace cereal {// 避免XML中生成<value0>节点void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive& ar, QString const& str) {ar.saveValue(str.toStdString()); // 直接写入字符串值}void CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive& ar, QString& str) {std::string temp;ar.loadValue(temp);str = QString::fromStdString(temp); // UTF-8安全转换}
}

关键点

  • saveValue/loadValue直接操作原始字节流,跳过对象包装逻辑
  • 使用toStdString()而非toUtf8()保证跨平台兼容性(Qt6默认UTF-8)

2. 动态容器序列化(QVector/QList)

通过make_size_tag标记容器大小,SFINAE区分加载/保存逻辑:

// QVector序列化
template <class Archive, typename T>
typename std::enable_if<!Archive::is_loading::value>::type
serialize(Archive& ar, QVector<T>& vec) {ar(make_size_tag(vec.size())); for (auto& item : vec) ar(item); // 逐项序列化
}// QVector反序列化
template <class Archive, typename T>
typename std::enable_if<Archive::is_loading::value>::type
serialize(Archive& ar, QVector<T>& vec) {size_type size;ar(make_size_tag(size));vec.resize(size);for (auto& item : vec) ar(item); // 按大小逐项加载
}

QList特殊处理

// Qt5.14+使用迭代器构造(避免fromStdList废弃)
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)list = QList<T>(temp_list.begin(), temp_list.end());
#elselist = QList<T>::fromStdList(temp_list);
#endif

3. 关联容器序列化(QMap)

解决QMap迭代器返回值而非键值对的问题:

// 保存QMap
template <class Archive, typename K, typename V>
void save(Archive& ar, QMap<K, V>& map) {ar(make_size_tag(map.size()));for (auto it = map.constBegin(); it != map.constEnd(); ++it) {ar(make_map_item(it.key(), it.value())); // 显式分离键值}
}// 加载QMap
template <class Archive, typename K, typename V>
void load(Archive& ar, QMap<K, V>& map) {size_type size;ar(make_size_tag(size));map.clear();for (size_t i = 0; i < size; ++i) {K key; V value;ar(make_map_item(key, value)); // 按序加载键值map.insert(key, value);        // 插入唯一键}
}

对比STL实现差异

特性std::mapQMap适配方案
迭代方式直接获取std::pairconstBegin()/constEnd()
键唯一性处理自动排序insert()自动去重
性能优化emplace_hint线性插入

⚠️ 注意事项

  1. 类型安全

    • 键类型K必须实现operator<(QMap排序依赖)
    • 嵌套容器(如QMap<QString, QVector<int>>)需递归支持序列化
  2. 错误处理

    try {ar(map); 
    } catch (cereal::Exception& e) {qDebug() << "序列化失败:" << e.what();
    }
    
  3. 扩展建议

    • 支持QHash:仿照QMap实现,替换迭代器为constBegin/constEnd
    • 多态类型:通过CEREAL_REGISTER_TYPE注册派生类

💎 总结

本文提供了Cereal序列化库对Qt核心容器QStringQVectorQListQMap)的完整适配方案,重点解决:

  1. XML格式下冗余节点问题 → 特化XML存档函数
  2. 动态容器大小保存 → make_size_tag标签
  3. Qt-STL迭代器差异 → 显式键值分离处理
    通过标准C++模板特化机制,实现了Qt与Cereal的无缝协作,为跨平台数据交换提供轻量化解决方案。

扩展阅读:Cereal官方文档 | C++ Cereal序列化库的使用

Cereal宏 一行声明序列化函数

http://www.lryc.cn/news/578721.html

相关文章:

  • web开发,旅游景点管理系统推荐算法版本demo,基于asp.net,mvc,c#,sql server
  • 每日八股文7.1
  • Claude Code 全面指南:从安装到高效开发的实用教程
  • React安装使用教程
  • 「Java流程控制」do……while循环结构
  • 吸烟行为检测数据集介绍-2,108张图片 公共场所禁烟监控 健康行为研究
  • 【Java编程动手学】Java的“三体”世界:JVM、JRE、JDK的共生之道
  • CppCon 2018 学习:EMULATING THE NINTENDO 3DS
  • 手工部署与自动化部署场景模拟及参考项目
  • InnoDB索引
  • 胖喵安初 (azi) Android 应用初始化库 (类似 Termux)
  • android车载开发之HVAC
  • Redis-渐进式遍历
  • 算法-每日一题(DAY12)最长和谐子序列
  • 使用D435i运行ORB-SLAM3时,纯视觉模式与视觉-惯性模式的位姿矩阵定义问题探讨
  • CentOS系统新手指导手册
  • 半导体和PN结
  • JavaEE线程概念
  • 人工智能在人力资源领域的深度变革:从技术适配到价值重构
  • AngularJS 安装使用教程
  • 转录组分析流程(七):GSEA分析
  • CertiK《Hack3d:2025年第二季度及上半年Web3.0安全报告》(附报告全文链接)
  • eNSP实验一:IPv4编址及IPv4路由基础
  • 统计labelme标注的json文件中各类别的标注数量
  • 百度轮岗:任命新CFO,崔珊珊退居业务二线
  • 数据结构:递归:组合数(Combination formula)
  • sqlmap学习ing(2.[第一章 web入门]SQL注入-2(报错,时间,布尔))
  • 【C++】inline的作用
  • 《UE5_C++多人TPS完整教程》学习笔记40 ——《P41 装备(武器)姿势(Equipped Pose)》
  • SQL学习笔记6