You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Boost序列化:无默认构造函数的派生类序列化/导出问题

问题与解决方案:Boost序列化无默认构造函数的派生类

问题描述

需要序列化串口包装类RS485CommunicationLayer的配置项用于生成初始化文件,该类继承自纯虚基类ICommunicationLayer且无默认构造函数。首次运行因未注册派生类报错,添加导出后又出现找不到save/load函数的异常:

no matching function for call to ‘save(boost_1_69_0::archive::xml_oarchive&, const amp::communication::RS485CommunicationLayer&, const boost_1_69_0::serialization::version_type&)’
         save(ar, t, v);

由于没有默认构造函数,必须使用save_construct_data和load_construct_data,需要明确如何让Boost序列化使用这两个函数,以及是否需要SPLIT_FREE宏。

解决方案

核心结论

必须使用BOOST_SERIALIZATION_SPLIT_FREE宏,同时修正代码中的几个关键错误:

1. 修正命名空间问题

save_construct_data和load_construct_data必须定义在boost::serialization命名空间下,而非硬编码的boost_1_69_0::serialization(版本号命名的内部命名空间不应直接使用)。

2. 移除不必要的基类序列化操作

基类ICommunicationLayer是纯虚类且无需要序列化的成员(其serialize函数为空),构造派生类时会自动初始化基类部分,因此要移除save_construct_data和load_construct_data中对base_object的操作,避免错误。

3. 修正后的完整代码示例

基类代码(保持不变)

class ICommunicationLayer {
    friend class boost::serialization::access;
    template<class Archive>
    inline void serialize(Archive & ar, const unsigned int file_version) {};
public:
    virtual ~ICommunicationLayer();
    virtual std::size_t write(const char* const buffer, std::size_t buffSize) = 0;
    virtual std::size_t readUntil(std::vector<char>& buffer, char delim, std::chrono::microseconds timeout) = 0;
};

派生类修正版

#include "ICommunicationLayer.h"

#include <boost/asio.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/nvp.hpp>

namespace amp {
namespace communication {

class RS485CommunicationLayer final : public ICommunicationLayer {
public:
    RS485CommunicationLayer(
            const std::string& path,
            unsigned int baud_rate
            // 其他配置参数
    );

    ~RS485CommunicationLayer();

    std::size_t write(const char* const buffer, const size_t size) override;
    std::size_t readUntil(std::vector<char>& buffer, char delim, std::chrono::microseconds timeout) override;
    
    // 提供序列化所需的配置项访问接口
    std::string getPath() const { return path; }
    unsigned int getBaudRate() const { return baud_rate; }

private:
    friend class boost::serialization::access;
    std::string path;
    unsigned int baud_rate;
    // 其他私有成员
};

} /* namespace communication */
} /* namespace amp */

// 先声明SPLIT_FREE,再导出类
BOOST_SERIALIZATION_SPLIT_FREE(amp::communication::RS485CommunicationLayer)
BOOST_CLASS_EXPORT(amp::communication::RS485CommunicationLayer)

// 正确的命名空间:boost::serialization
namespace boost {
namespace serialization {

template<class Archive>
inline void save_construct_data(Archive& ar, const amp::communication::RS485CommunicationLayer* comLayer, const unsigned int version) {
    // 仅序列化构造对象所需的配置项
    ar << make_nvp("path", comLayer->getPath());
    ar << make_nvp("baud_rate", comLayer->getBaudRate());
    // 序列化其他配置参数
}

template<class Archive>
inline void load_construct_data(Archive& ar, amp::communication::RS485CommunicationLayer* comLayer, const unsigned int version) {
    // 读取构造所需的配置项
    std::string path;
    unsigned int baud_rate;
    ar >> make_nvp("path", path);
    ar >> make_nvp("baud_rate", baud_rate);
    // 读取其他配置参数

    // 使用placement new构造对象
    new(comLayer) amp::communication::RS485CommunicationLayer(path, baud_rate /*, 其他参数*/);
}

} // namespace serialization
} // namespace boost

4. 关键注意事项

  • BOOST_SERIALIZATION_SPLIT_FREE的作用:该宏告知Boost序列化框架,不使用类内部的serialize函数,转而使用全局的save_construct_data和load_construct_data处理无默认构造函数的对象。
  • 宏调用顺序:必须先调用BOOST_SERIALIZATION_SPLIT_FREE,再调用BOOST_CLASS_EXPORT,确保框架优先识别拆分的序列化逻辑。
  • 配置项访问:可通过getter方法或直接让boost::serialization::access访问私有成员,确保能获取构造所需的所有参数。
  • 版本管理:后续修改配置项时,需更新序列化版本号,避免反序列化兼容性问题。

内容的提问来源于stack exchange,提问作者Typhaon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 12:05:22