提升前向声明类的序列化

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了提升前向声明类的序列化相关的知识,希望对你有一定的参考价值。

我想将具有彼此的std :: shared_ptr的类序列化为成员,并在不同的文件中声明和定义。我的代码的最小例子是:

    //auxiliary.h
#include <memory>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>



class A;
class B;
typedef std::shared_ptr< A > A_ptr;
typedef std::shared_ptr< B > B_ptr;


//A.h
#include "auxiliary.h"

class A
  B_ptr bpt;
  void foo();
  //...
;


//A.cpp
#include "A.h"
void A::foo()

    //implementation



//B.h
#include "auxiliary.h"

class B
  A_ptr apt;
  void bar();
  //...   
;


//B.cpp
#include "B.h"
void B::bar()

    //implementation

当我尝试通过写作来尝试序列化这两个类时

//A.h
#include "auxiliary.h"

class A
  template<class Archive>
  void serialize(Archive & ar, const unsigned int version)
  
    ar & B_ptr;
       
  B_ptr bpt;
  void foo();
  //...
;

//B.h
#include "auxiliary.h"

class B
  template<class Archive>
  void serialize(Archive & ar, const unsigned int version)
  
    ar & A_ptr;
  
  A_ptr apt;
  void bar();
  //...   
;

我得到错误C2139“A”:不允许将未定义的类作为编译器内部类型的参数__is_base_of

我知道编译器宁愿看到相应的cpp文件中定义的序列化函数,但由于它们是模板,因此无法以通常的方式工作。

我怎么可能修复整件事?

PS。:我也读过,它在类似的情况下帮助实例化模板,其中包含一个使用的所有变体并在手册中提升状态,这应该通过某个地方来完成

template void serialize<boost::archive::text_iarchive>(
    boost::archive::text_iarchive & ar, 
    const unsigned int file_version
    );
template void serialize<boost::archive::text_oarchive>(
    boost::archive::text_oarchive & ar, 
    const unsigned int file_version

我尝试了各种方法,但没有成功。

答案

Boost Serialization Archives是编译时多态的。这意味着它们结合了类和功能模板中的功能,这些模板定义了各个类型和行为。

由于C ++中模板的性质,这意味着您需要在POI(实例化点)处完整定义这些模板(及其依赖类型)。 (参见Why can templates only be implemented in the header file?的底漆)。

解?

您可以在包含所有定义的TU(转换单元)中隐藏序列化实现。

或者,您可以使用Polymorphic Archives。在这种情况下,serialize方法不再需要编译时通用。

注意:文档示例将serialize成员显示为模板函数,该函数在单个TU中显式实例化。这可能是出于技术原因需要¹,虽然从逻辑上说它完全等同于简单地声明两个重载,在标题中使用polymorphic_[io]archive&并在同一个TU中实现它们


¹库内部是否依赖T::serialize<>作为模板,而不仅仅是让C ++重载解析完成其工作

BONUS

一个结合了一些想法的演示:Live On Wandbox

  1. auxiliary.h #pragma once class A; class B; #include <memory> typedef std::shared_ptr<A> A_ptr; typedef std::shared_ptr<B> B_ptr; namespace boost namespace serialization class access;
  2. #pragma once #include "auxiliary.h" class A public: B_ptr bpt; void foo(); private: friend class boost::serialization::access; template <class Archive> void serialize(Archive&, unsigned); ;
  3. B.h #pragma once #include "auxiliary.h" class B public: A_ptr apt; void bar(); //... private: friend class boost::serialization::access; template <class Archive> void serialize(Archive&, unsigned); ;
  4. A.cpp // A.cpp #include "A.h" #include "B.h" void A::foo() // implementation bpt = std::make_shared<B>(); template <class Archive> void A::serialize(Archive &ar, unsigned) ar & bpt; #include <boost/archive/text_iarchive.hpp> #include <boost/archive/text_oarchive.hpp> #include <boost/serialization/shared_ptr.hpp> #include "B.h" // required at point of instatiation template void A::serialize(boost::archive::text_iarchive&, unsigned); template void A::serialize(boost::archive::text_oarchive&, unsigned);
  5. B.cpp // B.cpp #include "B.h" #include <iostream> void B::bar() // implementation std::cout << "Hello from B::bar()\n"; template <class Archive> void B::serialize(Archive &ar, unsigned) ar & apt; #include <boost/archive/text_iarchive.hpp> #include <boost/archive/text_oarchive.hpp> #include <boost/serialization/shared_ptr.hpp> #include "A.h" // required at point of instatiation template void B::serialize(boost::archive::text_iarchive&, unsigned); template void B::serialize(boost::archive::text_oarchive&, unsigned);
  6. TEST.CPP #include <iostream> #include <sstream> void test_serialize(std::ostream&); void test_deserialize(std::istream&); int main() std::stringstream ss; test_serialize(ss); std::cout << ss.str() << std::flush; test_deserialize(ss); #include "auxiliary.h" #include <boost/archive/text_oarchive.hpp> #include "A.h" //#include "B.h" // optional, see below void test_serialize(std::ostream& os) boost::archive::text_oarchive oa(os); A a1, a2; a1.foo(); // a1.bpt->bar(); // only works if B.h included A a3 = a1; // copy, should alias a1.bpt and a3.bpt oa << a1 << a2 << a3; #include <boost/archive/text_iarchive.hpp> #include "B.h" // optional, see below void test_deserialize(std::istream& is) boost::archive::text_iarchive ia(is); A a1, a2, a3; ia >> a1 >> a2 >> a3; std::cout << std::boolalpha; std::cout << "B correctly deserialized: " << (a1.bpt && !a2.bpt) << "\n"; std::cout << "Correctly aliased a1.bpt == a3.bpt: " << (a1.bpt == a3.bpt) << "\n"; a3.bpt->bar(); // only works if B.h included

打印

/home/sehe/custom/boost_1_65_0/boost/archive/detail/oserializer.hpp:467:22: runtime error: reference binding to null pointer of type 'const struct A'
/home/sehe/custom/boost_1_65_0/boost/archive/detail/oserializer.hpp:467:22: runtime error: reference binding to null pointer of type 'const struct B'
22 serialization::archive 15 1 0
0 0 1 2 1 0
1 0 1 -1
2 -1
3 2 1
/home/sehe/custom/boost_1_65_0/boost/archive/detail/iserializer.hpp:540:19: runtime error: reference binding to null pointer of type 'struct B'
/home/sehe/custom/boost_1_65_0/boost/archive/detail/iserializer.hpp:541:67: runtime error: reference binding to null pointer of type 'const struct B'
B correctly deserialized: true
Correctly aliased a1.bpt == a3.bpt: true
Hello from B::bar()

以上是关于提升前向声明类的序列化的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能只用前向声明 C++ 声明一个类的静态成员?

C++ 中嵌套类型/类的前向声明

不允许嵌套类的前向声明的原因?

boost 变体可以安全地与指向前向声明类的指针一起使用吗?

C++ 类的前向声明的用法

C++:如何前向声明出现在基类的静态方法中的派生类?