使用C++ cereal库时派生类序列化未触发,仅基类序列化被调用
C++ cereal库多态序列化问题:派生类serialize未触发
问题描述
使用C++ cereal库时,期望实现基类自行序列化,派生类同时序列化自身及基类的功能。代码参考库文档示例,但实际运行仅基类的serialize方法被调用,派生类的未触发。
示例代码
#include <cereal/archives/json.hpp> #include <cereal/types/polymorphic.hpp> #include <fstream> struct EmptyBase { public: virtual void foo() = 0; std::string name; template <class Archive> void serialize( Archive & ar ) { std::cout << "Base serialize" << std::endl; ar(name); } void SaveToFile(const std::string& filename) const { std::ofstream os(filename); cereal::JSONOutputArchive ar(os); ar(cereal::make_nvp("settings", *this)); } void LoadFromFile(const std::string& filename) { std::ifstream is(filename); cereal::JSONInputArchive ar(is); ar(cereal::make_nvp("settings", *this)); } }; struct DerivedTwo: EmptyBase { public: void foo() {} std::string why; template <class Archive> void serialize( Archive & ar ) { std::cout << "Child serialize" << std::endl; ar(cereal::base_class<EmptyBase>(this)); ar( why ); } }; CEREAL_REGISTER_TYPE(DerivedTwo) CEREAL_REGISTER_POLYMORPHIC_RELATION(EmptyBase, DerivedTwo) int main() { DerivedTwo myclass; myclass.name = "test"; myclass.why = "because"; myclass.SaveToFile("settings.json"); }
预期输出
Child serialize Base serialize
实际输出
Base serialize
补充需求:基类包含Save()、Load()和serialize()方法,Save()/Load()负责将类序列化/反序列化为JSON文件;派生类继承基类后,可通过调用Save()/Load()同时序列化派生类与基类的成员。
问题原因与解决方案
问题出在基类的SaveToFile和LoadFromFile方法中:直接传递*this时,由于函数是基类成员函数,*this的静态类型为EmptyBase,cereal默认按静态类型处理,不会触发多态序列化逻辑,因此只会调用基类的serialize。
要触发多态序列化,需要明确告知cereal处理多态类型,有两种可行修改方式:
方式1:使用cereal::polymorphic_cast修改序列化调用
修改基类的SaveToFile和LoadFromFile方法:
void SaveToFile(const std::string& filename) const { std::ofstream os(filename); cereal::JSONOutputArchive ar(os); // 用polymorphic_cast明确告知多态类型 ar(cereal::make_nvp("settings", cereal::polymorphic_cast<const EmptyBase*>(this))); } void LoadFromFile(const std::string& filename) { std::ifstream is(filename); cereal::JSONInputArchive ar(is); ar(cereal::make_nvp("settings", cereal::polymorphic_cast<EmptyBase*>(this))); }
方式2:使用cereal::polymorphic_wrapper包装对象
这是cereal推荐的多态序列化方式,代码修改如下:
void SaveToFile(const std::string& filename) const { std::ofstream os(filename); cereal::JSONOutputArchive ar(os); // 用polymorphic_wrapper包装多态对象 cereal::polymorphic_wrapper<const EmptyBase> wrapper(this); ar(cereal::make_nvp("settings", wrapper)); } void LoadFromFile(const std::string& filename) { std::ifstream is(filename); cereal::JSONInputArchive ar(is); cereal::polymorphic_wrapper<EmptyBase> wrapper(this); ar(cereal::make_nvp("settings", wrapper)); }
修改后运行代码,即可得到预期输出,派生类和基类的serialize方法都会被调用,同时满足基类提供Save/Load、派生类继承后直接使用的需求。
内容的提问来源于stack exchange,提问作者OOgaBooga
相关产品推荐
相关产品推荐

