【问题标题】:boost serialization switching from binary_archive to polymorphic_archive增强从 binary_archive 到 polymorphic_archive 的序列化切换
【发布时间】:2016-03-21 14:46:24
【问题描述】:

是否可以使用polymorphic_binary_iarchive 反序列化使用binary_oarchive 序列化的对象?

我的库是使用二进制存档编写的,用于所有序列化方法(以及 EOS 可移植存档)。这导致到处都是重载的serialize 方法、显着的代码膨胀和链接时间成本。我想切换到多态存档方法,以减少暴露的serialize 方法的数量并促进其他存档(特别是 XML)的轻松使用。但是,我还想保持与已序列化数据的向后兼容性。

polymorphic_binary_iarchive 似乎工作通常,但至少有一种情况会失败:vector.hpp 根据use_array_optimization 更改序列化方法,对于二进制存档设置为is_bitwise_serializable 的任何类型。因此包含vector<size_t> 的类型无法反序列化。

我很想尝试将use_array_optimization 专门用于polymorphic_binary_iarchive(以及类似的oarchive)。是否有任何原因这不起作用,或者有任何其他原因导致这只是 A Bad Idea(TM)?

编辑

我尝试过那个专业:

struct use_array_optimization_in_polymorphic_archive {
  template <class T>
  struct apply : public boost::serialization::is_bitwise_serializable< T > {};
};

namespace boost {
namespace serialization {

template <>
struct use_array_optimization<boost::archive::polymorphic_iarchive> {
  template <class ValueType>
  struct apply : boost::mpl::apply1<
    use_array_optimization_in_polymorphic_archive,
    BOOST_DEDUCED_TYPENAME boost::remove_const<ValueType>::type>::type {};
};

}}

但它不起作用,因为 array.hpp 然后期望 polymorphic_iarchive 实现 load_array,这仅适用于 basic_binary_iprimitive

我还担心这会改变所有polymorphic_iarchive 实现的行为,而不仅仅是polymorphic_binary_iarchive。需要更多思考...

编辑2

以下是一些演示代码。切换 POLY_ON 以使用多态存档进行反序列化;这适用于双打。切换 VEC_ON 以使用向量,演示问题。 注意:我还没有仔细检查这是否是完全相同的问题,但我有理由确定它是。 NNB:这是使用 Boost 1.59。

#include <fstream>
#include <boost/archive/binary_oarchive.hpp>

// #define POLY_ON
#ifdef POLY_ON
#include <boost/archive/polymorphic_binary_iarchive.hpp>
#else
#include <boost/archive/binary_iarchive.hpp>
#endif

// #define VEC_ON
#ifdef VEC_ON
#include <vector>
#include <boost/serialization/vector.hpp>
#endif

class bank_balance {
 private:
  friend class boost::serialization::access;
  template <class archive>
  void serialize(archive& ar, const unsigned int version) {
    ar & date_;
    ar & rate_;
  }

#ifdef VEC_ON
  std::vector<double> date_;
  std::vector<double> rate_;
#else
  double date_;
  double rate_;
#endif

 public:
  bank_balance() : date_(0) {}
  bank_balance(
#ifdef VEC_ON
      std::vector<double> date, std::vector<double> rate
#else
      double date, double rate
#endif
  )
    : date_(date), rate_(rate)
  {}

  bool operator==(const bank_balance& other) const {
    return date_ == other.date_ && rate_ == other.rate_;
  }
};

int main() {
  std::ofstream ofs("bank_balance.ser");

#ifdef VEC_ON
  const bank_balance balance({45367, 45369}, {5.6, 2.43});
#else
  const bank_balance balance(45367, 5.6);
#endif

  {
    boost::archive::binary_oarchive oa(ofs);
    oa << balance;
  }

  bank_balance balance2;
  {
    std::ifstream ifs("bank_balance.ser");
#ifdef POLY_ON
    boost::archive::polymorphic_binary_iarchive ia(ifs);
#else
    boost::archive::binary_iarchive ia(ifs);
#endif
    ia >> balance2;
  }

  if (balance == balance2) std::cout << "ok\n";
  else std::cout << "dammit\n";

  return 0;
}

【问题讨论】:

    标签: c++ serialization boost


    【解决方案1】:

    是否可以使用 polymorphic_binary_iarchive 反序列化使用 binary_oarchive 序列化的对象?

    简短回答:是的。

    唯一的区别是这里的调用站点界面。

    编辑

    也许,当他们为 POD 容器引入优化序列化时,这个“承诺”无意中被打破了。

    这是我的分析,yours.cpp 来自您的问题,mine.cpp 编辑如下:

    { 
        std::ofstream ofs("bank_balance.ser");
    #ifdef POLY_ON
        boost::archive::polymorphic_binary_oarchive oa(ofs);
    #else
        boost::archive::binary_oarchive oa(ofs);
    #endif
        oa << balance;
    }
    

    我使用以下命令行编译所有口味:

    for src in yours mine; do for a in {,-DPOLY_ON}\ {,-DVEC_ON}; do time g++ $a -O2 -std=c++11 $src.cpp -lboost_{system,serialization} -o "$src${a//[D _]/}.exe"; done; done
    

    这会导致mine.exemine-POLYON.exemine-POLYON-VECON.exemine-VECON.exeyours.exeyours-POLYON.exeyours-POLYON-VECON.exeyours-VECON.exe。运行它们:

    (set -x; for a in ./*.exe; do $a; done)
    

    结果

    + ./mine.exe
    ok: true
    
    + ./mine-POLYON.exe
    ok: true
    
    + ./mine-POLYON-VECON.exe
    ok: true
    
    + ./mine-VECON.exe
    ok: true
    
    + ./yours.exe
    ok
    
    + ./yours-POLYON.exe
    ok
    
    + ./yours-POLYON-VECON.exe
    terminate called after throwing an instance of 'std::length_error'
      what():  vector::_M_default_append
    
    + ./yours-VECON.exe
    ok
    

    请注意,如果您使用与阅读时相同的存档实现进行编写,则所有组合都可以。你也是对的,可悲的是./yours-POLYON-VECON.exe 是唯一一个被打破的。我认为这是无意的,但您的预感可能是正确的:

    doc

    请注意,多态档案的概念与用户标记为“原始”的新类型的序列化从根本上不兼容:

      BOOST_CLASS_IMPLEMENTATION(my_primitive_type, boost::serialization::primitive_type)
    

    为这些类型实现序列化的代码在用户程序中“即时”实例化。但这与多态档案的整个目的相冲突。尝试序列化这种原始类型将导致编译错误,因为通用多态接口是静态的,无法实例化新类型的代码。

    看起来向量优化路径可能共享这些代码路径之一。

    推荐

    我建议制作一个转换工具来将旧格式文件转换为新格式。您可以使用非多态存档读取并使用多态存档写入。当然,这意味着您必须为此版本编译这两种方法,但是

    • 不必将其烘焙到主可执行文件中 - 只有转换工具才需要“旧”方法
    • 转换工具不必进行版本控制,它在所有未来版本中都保持不变;这意味着您可以停止构建新版本并删除所需的额外代码。

    【讨论】:

    • 谢谢。是的,我们也在使用 EOS 便携式存档(所以我尝试使用 eos::polymorphic_portable_iarchive,它似乎正在工作)。 Boost 版本是 1.51,编译器或标志没有变化。我所做的只是将采用具体归档类型的serialize(和相关方法)替换为仅采用polymorphic_iarchivepolymorphic_oarchive。你能链接到你提到的重大变化吗?
    • 有部分案例列表boost.org/doc/libs/1_46_1/libs/serialization/src/…。发行说明没有提及自 1.48 版以来看起来相关的任何内容
    • 经过进一步考虑(以及对我问题的 2 次后续编辑),我不相信“简短回答:是”是准确的。在我看来,它需要一个 Merry Old Jig(TM)。
    • 我已经分析了你的样本。我认为我的简短回答是“设计正确”。但在实践中,这种优化似乎有点挫败了设计承诺。我已将我的分析和建议添加到答案中。如果“Merry Old Jig™”是指“转换工具”,那么我认为我们同意 :)
    • 这个建议不错,但不适用于我的具体可交付成果。序列化代码散布在多个 DLL 中的类型中。这需要有效地将所有这些 DLL 烘焙到转换工具中,并将该工具与库捆绑在一起。但是,我发现我可以将 boost::archive::load 函数专门用于 polymorphic_iarchive 并跳转到一个实现,在那里我可以向下转换到 polymorphic_binary_iarchive 并从那里调用合适的函数。有缺点,但这些似乎是可控的。我会在它工作时更新。欣赏任何 cmets。
    【解决方案2】:

    我知道这是一个老话题,但我一直在与这个精确的问题作斗争,并认为我找到了一个很好的解决方案。

    问题的核心是多态档案永远不会被分派以使用 boost/serialization/vector.hpp 中优化的加载/保存版本。我解决这个问题的方法是为这样的多态存档类型重载那些调度函数:

    namespace boost::serialization
    {
       template<class U, class Allocator>
       inline void load(boost::archive::polymorphic_iarchive& ar, std::vector<U, Allocator>& t, const unsigned int file_version)
       {
          using use_optimized = boost::has_trivial_constructor<U>::type;
          load(ar, t, file_version, use_optimized());
       }
        
       template<class U, class Allocator>
       inline void save(boost::archive::polymorphic_oarchive& ar, const std::vector<U, Allocator>& t, const unsigned int file_version)
       {
          using use_optimized = boost::has_trivial_constructor<U>::type;
          save(ar, t, file_version, use_optimized());
       }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-06
      • 2023-03-22
      • 2013-11-26
      • 1970-01-01
      • 1970-01-01
      • 2010-10-14
      • 1970-01-01
      • 2010-10-20
      相关资源
      最近更新 更多