问题
I am wondering about this. I have a C++ program with a number of data structs which derive from a common root and I need to serialize them using Boost. Each one has an inline member function to accept a visitor (so I can visit the structure without a "switch" statement).
The objects look like this:
In the .h file:
// Graphic component.
struct GraphicComponent : public Component {
... data members ...
void accept(ComponentVisitor &vis) { vis.visitGraphicComponent(*this); }
private:
// Serialization routine.
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &a, const unsigned int v);
};
BOOST_CLASS_EXPORT_KEY(GraphicComponent)
// Position component.
struct PositionComponent : public Component {
... data members ...
void accept(ComponentVisitor &vis) { vis.visitPositionComponent(*this); }
private:
// Serialization routine.
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &a, const unsigned int v);
};
BOOST_CLASS_EXPORT_KEY(PositionComponent)
...
In the .cpp file, I declare the "serialize" routines:
BOOST_CLASS_EXPORT_IMPLEMENT(GraphicComponent)
BOOST_CLASS_EXPORT_IMPLEMENT(PositionComponent)
...
template<class Archive>
void GraphicComponent::serialize(Archive &a, const unsigned int v)
{
a & boost::serialization::base_object<Component>(*this);
... serialize data members ...
}
template<class Archive>
void PositionComponent::serialize(Archive &a, const unsigned int v)
{
a & boost::serialization::base_object<Component>(*this);
... serialize data members ...
}
...
I also include the Boost archive through a common header. As far as I can tell, everything looks right. There's also a "BOOST_SERIALIZATION_ASSUME_ABSTRACT" on the base Component, as "accept" is pure virtual.
When I run the program and get to the point where it serializes this stuff, I get
what(): unregistered class - derived class not registered or exported
Serialization occurs through a pointer to the base Component.
I've heard troubles involving Boost serialization and "libraries". The build system I was using, CMake, is set up to compile the program by assembling its subcomponents into libraries and then putting those together into a single executable to make the final program. Could that be the problem?
Also, Component derives from std::enable_shared_from_this (that's C++11 STL, not Boost) -- could this be the problem? If so, what can be done about it?
回答1:
In case it helps, here's a working SSCCE (or MCVE as the commenter said):
Live On Coliru
#include <boost/serialization/access.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
struct ComponentVisitor;
struct Component {
virtual ~Component() = default;
virtual void accept(ComponentVisitor &v) = 0;
private:
// Serialization routine.
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &, const unsigned int) {}
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(Component)
struct GraphicComponent;
struct PositionComponent;
struct ComponentVisitor {
virtual void visitGraphicComponent(GraphicComponent const &){};
virtual void visitPositionComponent(PositionComponent const &){};
};
// Graphic component.
struct GraphicComponent : public Component {
void accept(ComponentVisitor &vis) { vis.visitGraphicComponent(*this); }
private:
// Serialization routine.
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &a, const unsigned int v);
};
BOOST_CLASS_EXPORT_KEY(GraphicComponent)
// Position component.
struct PositionComponent : public Component {
void accept(ComponentVisitor &vis) { vis.visitPositionComponent(*this); }
private:
// Serialization routine.
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &a, const unsigned int v);
};
BOOST_CLASS_EXPORT_KEY(PositionComponent)
/////////////////////////////////////////////////////
BOOST_CLASS_EXPORT_IMPLEMENT(GraphicComponent)
BOOST_CLASS_EXPORT_IMPLEMENT(PositionComponent)
//...
template <class Archive>
void GraphicComponent::serialize(Archive &a, const unsigned int)
{
a &boost::serialization::base_object<Component>(*this);
//... serialize data members ...
}
template <class Archive>
void PositionComponent::serialize(Archive &a, const unsigned int)
{
a &boost::serialization::base_object<Component>(*this);
//... serialize data members ...
}
#include <boost/make_shared.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <sstream>
int main() {
std::stringstream ss;
{
std::vector<boost::shared_ptr<Component> > components {
boost::make_shared<GraphicComponent>(),
boost::make_shared<PositionComponent>(),
boost::make_shared<PositionComponent>(),
boost::make_shared<GraphicComponent>(),
};
boost::archive::text_oarchive oa(ss);
oa << components;
}
{
std::vector<boost::shared_ptr<Component> > deserialized;
boost::archive::text_iarchive ia(ss);
ia >> deserialized;
struct printer : ComponentVisitor {
void visitPositionComponent(PositionComponent const & /*pc*/){ std::cout << __PRETTY_FUNCTION__ << "\n"; }
void visitGraphicComponent(GraphicComponent const & /*gc*/){ std::cout << __PRETTY_FUNCTION__ << "\n"; }
} print;
for (auto c : deserialized)
c->accept(print);
}
}
Prints
virtual void main()::printer::visitGraphicComponent(const GraphicComponent&)
virtual void main()::printer::visitPositionComponent(const PositionComponent&)
virtual void main()::printer::visitPositionComponent(const PositionComponent&)
virtual void main()::printer::visitGraphicComponent(const GraphicComponent&)
as expected
Notes
Actually, since you only use the serialization in specific TUs, you could consider using non-intrusive serialization:
Live On Coliru
struct ComponentVisitor; struct Component { virtual ~Component() = default; virtual void accept(ComponentVisitor &v) = 0; }; struct GraphicComponent; struct PositionComponent; struct ComponentVisitor { virtual void visitGraphicComponent(GraphicComponent const &){}; virtual void visitPositionComponent(PositionComponent const &){}; }; struct GraphicComponent : public Component { void accept(ComponentVisitor &vis) { vis.visitGraphicComponent(*this); } }; struct PositionComponent : public Component { void accept(ComponentVisitor &vis) { vis.visitPositionComponent(*this); } }; ///////////////////////////////////////////////////// // in the CPP #include <boost/serialization/access.hpp> #include <boost/serialization/export.hpp> #include <boost/serialization/base_object.hpp> #include <boost/archive/text_iarchive.hpp> #include <boost/archive/text_oarchive.hpp> BOOST_SERIALIZATION_ASSUME_ABSTRACT(Component) BOOST_CLASS_EXPORT(GraphicComponent) BOOST_CLASS_EXPORT(PositionComponent) namespace boost { namespace serialization { template <class Archive> void serialize(Archive &, Component&, const unsigned int) {} template <class Archive> void serialize(Archive &a, GraphicComponent& obj, const unsigned int) { a &boost::serialization::base_object<Component>(obj); } template <class Archive> void serialize(Archive &a, PositionComponent& obj, const unsigned int) { a &boost::serialization::base_object<Component>(obj); } } }Which is considerably cleaner
If you still want to access private members from inside
serializecf. e.g.- Get private data members for non intrusive boost serialization C++
回答2:
This is a partial answer as it doesn't explain exactly why it failed. I have managed to solve the problem by compiling the program as a single program instead of a bunch of libraries that are then statically linked together, which is how I thought I had to do it with the build system I was using since the documentation that was available online for the system was terse and when I put together the makefiles, I wasn't sure exactly how to do it. I suspect it has something to do with Boost's trouble dealing with this kind of code in libraries.
来源:https://stackoverflow.com/questions/30970956/why-does-boostserialize-not-work-despite-everything-seeming-right-unregiste