问题
I am getting an unregistered_class exception when I serialize out a derived object to a file and serialize in from that file to a base class pointer when I use the boost export macros. When I replace the macros with the archive register_type() methods, it works. I need to use the macros because the application size and complexity makes the register_type() methods impractical.
I am using RHEL 7.4, boost 1.53, and C++11. The classes are in shared libraries. Here is sample code to demonstrate the problem. As is, it throws the exception. If I comment out the macros and uncomment the functions, it works. 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:
From the docs: Exporting Class Serialization
BOOST_CLASS_EXPORT in the same source module that includes any of the archive class headers will instantiate code required to serialize polymorphic pointers of the indicated type to the all those archive classes. If no archive class headers are included, then no code will be instantiated.
In this case the tricky detail is that you did include /some/ archive headers, but /not/ the concrete type that's actually doing the serialization.
Another tricky detail might be related to the question whether it still works when you have split the _KEY/_IMPLEMENT parts and you have the archive headers included before the _IMPLEMENT parts only. I suppose you could easily test this.
Other Issues
The serialization/deserialization is not in balance. If you deserialize a A*, you MUST also serialize one of that type.
Also, you can simplify and combine the makefile, and probably don't need quite as many serialization include in your headers.
Here The Full Code
As working locally. Just make && ./tester_mn:
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)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)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; }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; }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; }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)
来源:https://stackoverflow.com/questions/52545233/boost-class-export-macros-are-not-working-like-register-type