BOOST_CLASS_EXPORT_ *宏无法像register_type()一样工作

时间:2018-09-27 21:14:31

标签: boost boost-serialization

当我将派生对象序列化到文件中,并在使用boost导出宏时从该文件序列化到基类指针时,出现unregistered_class异常。当我用存档的register_type()方法替换宏时,它可以工作。我需要使用宏,因为应用程序的大小和复杂性使得register_type()方法不切实际。

我正在使用RHEL 7.4,boost 1.53和C ++ 11。这些类在共享库中。这里是示例代码来演示此问题。照原样,它引发异常。如果我注释掉宏并取消注释功能,它将起作用。 testera.h

#ifndef testera_h
#define testera_h

#include <boost/serialization/serialization.hpp>
#include <boost/archive/polymorphic_oarchive.hpp>
#include <boost/archive/polymorphic_iarchive.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/string.hpp>

class A
{
private:
    friend class boost::serialization::access;

    template< class Arch >
    void
    serialize( Arch &ar, const unsigned int ver );

public:
    A(); 

    virtual ~A() {}

    void setStr( const std::string &s );
    std::string getStr();

protected:
    std::string myStr;
};

BOOST_CLASS_EXPORT_KEY(A)

#endif

testera.C

#include "testera.h"

BOOST_CLASS_EXPORT_IMPLEMENT(A)


template
void
A::serialize<boost::archive::polymorphic_oarchive>(  boost:archive::polymorphic_oarchive &ar, const unsigned int ver );

template
void
A::serialize<boost::archive::polymorphic_iarchive>( boost::archive::polymorphic_iarchive &ar, const unsigned int ver );

A::A() : myStr( "a" ) 
{
}

void
A::setStr( const std::string &s )
{
    myStr = s;
}

std::string
A::getStr()
{
    return myStr;
}


template<class Arch>
void
A::serialize( Arch &ar, const unsigned int ver )
{
    ar & myStr;
}

testerb.h

#ifndef testerb_h
#define testerb_h

#include "testera.h"

class B : public A
{
private:
    friend class boost::serialization::access;

    template< class Arch >
    void
    serialize( Arch &ar, const unsigned int ver );

public:
    B();

    virtual ~B() {}

    void set( const int i );
    int get();

protected:
    int myI;
};

BOOST_CLASS_EXPORT_KEY(B)
#endif

testerb.C

#include "testerb.h"

BOOST_CLASS_EXPORT_IMPLEMENT(B)

template
void 
B::serialize<boost::archive::polymorphic_oarchive>( boost::archive::polymorphic_oarchive &ar, const unsigned int ver );

template
void
B::serialize<boost::archive::polymorphic_iarchive>( boost::archive::polymorphic_iarchive &ar, const unsigned int ver );
B::B(): myI( 1 )
{ 
    myStr = "b"; 
}

void 
B::set( const int i ) 
{
    myI = i;
}

int 
B::get() 
{
    return myI;
}

template< class Arch >
void
B::serialize( Arch &ar, const unsigned int ver )
{
    // boost::serialization::void_cast_register< B, A >( static_cast< B *>( NULL ), static_cast< A * >( NULL ) );
    ar & boost::serialization::base_object<A>( *this );

    ar & myI;
}

tester_mn.C

#include "testerb.h"
#include <boost/archive/polymorphic_text_oarchive.hpp>
#include <boost/archive/polymorphic_text_iarchive.hpp>
#include <fstream>

int main( int argc, char *argv[] )
{
    int ret = 0;
    B obj;
    obj.set( 2 );

    A *ptr = NULL;

    if( argc > 1 )
    {
        try
        {
            std::string fl = argv[ 1 ];
            {
                std::ofstream ofl( fl.c_str() );

                /*
                oar->register_type(static_cast<A *>(NULL));
                oar->register_type(static_cast<B *>(NULL));
                */
                try
                {
                    boost::archive::polymorphic_text_oarchive toar( ofl );
                    toar & obj;
                }
                catch( std::exception &e )
                {
                    std::cerr << "Error: archive from B to file " << fl << " - " << e.what() << std::endl;
                    ret = 1;
                }
            }

            if( ! ret )
            {
                ptr = NULL;

                {
                    std::ifstream ifl( fl.c_str() );
                    /*
                    iar->register_type(static_cast<A *>(NULL));
                    iar->register_type(static_cast<B *>(NULL));
                    */

                    try
                    {
                        boost::archive::polymorphic_text_iarchive tiar( ifl );

                        tiar & ptr;
                    }
                    catch( std::exception &e )
                    {
                        std::cerr << "Error: archive from file " << fl << " to B * - " << e.what() << std::endl;
                        ret = 1;
                    }
                    catch( ... )
                    {
                        std::cerr << "Error: Caught excpeption" << std::endl;
                        ret = 1;
                    }
                }


                if( ! ret )
                {
                    std::cout << static_cast<B*>(ptr)->get() << std::endl;
                }
            }
        }
        catch( ... )
        {
            std::cerr << "Caught exception" << std::endl;
            ret = 1;
        }

    }


    return ret;
}

Makefile_lib

CXXFLAGS = -c -ggdb3  -fPIC -funswitch-loops -fgcse-after-reload -std=c++11 
LDFLAGS = -std=c++11 -lboost_serialization -lboost_thread -lboost_system -lboost_filesystem

OBJLOC = obj

OBJS = $(OBJLOC)/testera.o \
    $(OBJLOC)/testerb.o

LIBRARY_NAME = libtester.so

libs:$(LIBRARY_NAME)

$(LIBRARY_NAME): $(OBJS)
    ${CXX} -shared -o $@ $(OBJS)

$(OBJLOC)/%.o: ./%.C
    $(CXX) $(CXXFLAGS) ./$*.C -o $(OBJLOC)/$*.o

Makefile_mn

CXXFLAGS = -ggdb3  -funswitch-loops -fgcse-after-reload -std=c++11 
LDFLAGS = -lboost_serialization -lboost_thread -lboost_system -lboost_filesystem -L. -ltester


tester_mn: tester_mn.o
    ${CXX} $(CXXFLAGS) $(LDFLAGS) tester_mn.C  -o $@

1 个答案:

答案 0 :(得分:0)

从文档中:Exporting Class Serialization

  包含任何存档类标头的同一源模块中的

BOOST_CLASS_EXPORT 将实例化将指定类型的多态指针序列化为所有那些存档类所需的代码。 如果不包含任何存档类标题,则不会实例化任何代码。

在这种情况下,棘手的细节是您确实包含了/ some /归档头,但/ not /实际上是在进行序列化的具体类型。

另一个棘手的细节可能与以下问题有关:当您拆分_KEY / _IMPLEMENT部分并且仅在_IMPLEMENT部分之前包含了归档头时,它是否仍然有效。我想您可以轻松地对此进行测试。

其他问题

序列化/反序列化不平衡。如果您反序列化A*,则必须也要序列化该类型之一。

此外,您可以简化和组合makefile,并且可能不需要在标头中包含那么多的序列化。

此处是完整代码

在本地工作。只是make && ./tester_mn

  1. testera.h

    #pragma once
    
    #include <boost/serialization/export.hpp>
    #include <boost/serialization/serialization.hpp>
    
    class A {
      private:
        friend class boost::serialization::access;
    
        template <class Arch> void serialize(Arch &ar, const unsigned int ver);
    
      public:
        A();
    
        virtual ~A() {}
    
        void setStr(const std::string &s);
        std::string getStr();
    
      protected:
        std::string myStr;
    };
    
    BOOST_CLASS_EXPORT_KEY(A)
    
  2. testerb.h

    #pragma once
    #include "testera.h"
    
    class B : public A {
      private:
        friend class boost::serialization::access;
        template <class Arch> void serialize(Arch &, unsigned);
    
      public:
        B();
    
        virtual ~B() {}
        void set(int i);
        int get();
    
      protected:
        int myI;
    };
    
    BOOST_CLASS_EXPORT_KEY(B)
    
  3. testera.cpp

    #include "testera.h"
    #include <boost/archive/polymorphic_iarchive.hpp>
    #include <boost/archive/polymorphic_oarchive.hpp>
    #include <boost/serialization/string.hpp>
    
    BOOST_CLASS_EXPORT_IMPLEMENT(A)
    
    template void A::serialize<boost::archive::polymorphic_oarchive>(boost::archive::polymorphic_oarchive &, unsigned);
    template void A::serialize<boost::archive::polymorphic_iarchive>(boost::archive::polymorphic_iarchive &, unsigned);
    
    A::A() : myStr("a")                  {               } 
    void A::setStr(const std::string &s) { myStr = s;    } 
    std::string A::getStr()              { return myStr; } 
    
    template <class Arch> void A::serialize(Arch &ar, unsigned) { ar &myStr; }
    
  4. testerb.cpp

    #include "testerb.h"
    #include <boost/archive/polymorphic_iarchive.hpp>
    #include <boost/archive/polymorphic_oarchive.hpp>
    
    BOOST_CLASS_EXPORT_IMPLEMENT(B)
    
    template void B::serialize<boost::archive::polymorphic_oarchive>(boost::archive::polymorphic_oarchive &, unsigned);
    template void B::serialize<boost::archive::polymorphic_iarchive>(boost::archive::polymorphic_iarchive &, unsigned);
    
    B::B() : myI(1)          { myStr = "b"; } 
    void B::set(const int i) { myI = i;     } 
    int B::get()             { return myI;  } 
    
    template <class Arch> void B::serialize(Arch &ar, unsigned) {
        ar &boost::serialization::base_object<A>(*this);
        ar &myI;
    }
    
  5. test.cpp

    #include "testerb.h"
    #include <boost/archive/polymorphic_text_iarchive.hpp>
    #include <boost/archive/polymorphic_text_oarchive.hpp>
    #include <fstream>
    
    int main() try {
    
        std::string const fl = "test.data";
        {
            std::ofstream ofl(fl.c_str());
    
            boost::archive::polymorphic_text_oarchive toar(ofl);
            B obj;
            obj.set(42);
    
            {
                A *ptr = &obj;
                toar &ptr;
            }
        }
    
        {
            A *ptr = nullptr;
            std::ifstream ifl(fl.c_str());
    
            boost::archive::polymorphic_text_iarchive tiar(ifl);
    
            tiar &ptr;
    
            if (B *b = dynamic_cast<B *>(ptr)) {
                std::cout << b->get() << std::endl;
            }
        }
    } catch (std::exception &e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    } catch (...) {
        std::cerr << "Error: Caught excpeption" << std::endl;
        return 1;
    }
    
  6. Makefile

    all: libs tester_mn
    
    CXXFLAGS += -I ~/custom/boost
    LDFLAGS  += -L ~/custom/boost/stage/lib
    CXXFLAGS += -ggdb3  -fPIC -funswitch-loops -fgcse-after-reload -std=c++11 
    LDFLAGS  += -lboost_serialization -lboost_thread -lboost_system -lboost_filesystem
    
    OBJLOC = obj
    OBJS += $(OBJLOC)/testera.o
    OBJS += $(OBJLOC)/testerb.o
    
    LIBRARY_NAME = libtester.so
    
    libs:$(LIBRARY_NAME)
    
    $(LIBRARY_NAME): $(OBJS)
        ${CXX} -shared -o $@ $(OBJS)
    
    $(OBJLOC)/%.o: ./%.cpp
        mkdir -pv $(@D)
        $(CXX) -c $(CXXFLAGS) $< -o $@
    
    tester_mn: LDFLAGS += -L. -ltester
    tester_mn: test.o $(OBJS) | libs
        ${CXX} $(CXXFLAGS) $^ -o $@ $(LDFLAGS)