Serialization with boost for polymorphic classes does not seem to work.
I have a virtual class (cf. Base) and a child class (cf. Derived), I have tried several methods to register them (cf. BOOST_CLASS_EXPORT_GUID, BOOST_CLASS_EXPORT, BOOST_SERIALIZATION_ASSUME_ABSTRACT, BOOST_CLASS_EXPORT_KEY and BOOST_CLASS_EXPORT_IMPLEMENT) but none of them seem to work because I get the error unregistered class - derived class not registered or exported every time.
This only works if I register the classes in the .hpp files. However I can't do this because otherwise it only works if my classes are only included once.
Minimal code example :
base.hpp
#pragma once
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
struct Base
{
virtual ~Base() = default;
template <class Archive>
void serialize(Archive & ar, unsigned /*version*/)
{
ar & base_value;
}
int base_value;
};
base.cpp
#include "base.hpp"
BOOST_CLASS_EXPORT(Base)
derived.hpp
#include "base.hpp"
struct Derived : public Base
{
template <class Archive>
void serialize(Archive & ar, unsigned /*version*/)
{
ar & boost::serialization::base_object<Base>(*this);
ar & derived_value;
}
int derived_value;
};
derived.cpp
#include "derived.hpp"
BOOST_CLASS_EXPORT(Derived)
main.cpp
#include <boost/archive/text_oarchive.hpp>
#include "base.hpp"
#include "derived.hpp"
int main()
{
std::shared_ptr<Base> base = std::make_shared<Derived>();
std::ostringstream output_stream;
boost::archive::text_oarchive archive(output_stream);
archive << base; // unregistered class - derived class not registered or exported
return 0;
}
UPDATE 1 :
It seems to work when I compile my code as shared library, linked statically against boost. And it don’t work when I compile my code as static library.
PS : My library is then linked dynamically against my test executable.
CMakeLists.txt (library)
set(mylib ${CMAKE_PROJECT_NAME}_lib)
file(GLOB_RECURSE sources LIST_DIRECTORIES true *.hpp *.cpp)
set(sources ${sources})
include_directories(${CMAKE_SOURCE_DIR}/include)
add_library(${mylib} SHARED ${sources}) # don't work when set as STATIC
target_link_libraries(${mylib}
boost_system
boost_serialization
)
# more target_link_libraries calls
CMakeLists.txt (test)
set(mytest ${CMAKE_PROJECT_NAME}_test)
file(GLOB_RECURSE sources LIST_DIRECTORIES true *.hpp *.cpp)
set(sources ${sources})
include_directories(${CMAKE_SOURCE_DIR}/include)
add_executable(${mytest} ${sources})
add_test(NAME ${mytest} COMMAND ${mytest})
target_link_libraries(${mytest} PRIVATE ${CMAKE_PROJECT_NAME}_lib)
target_link_libraries(${mytest} PRIVATE
gtest
)
You claim the classes are registered:
However, they are not, in fact, registered. The only place where they are is inside the TU's
base.cppandderived.hpp, and they have no external visibility, like inmain.cpp.There's a chance that it will "work" in certain situations due to SIOF. But why rely on that. It doesn't work on my system: https://i.stack.imgur.com/EKFb0.jpg
You should split the EXPORT declaration and definitions, as intended:
and
Additionally, heed the documentation warning:
Finally (and this took me some sweet time to figure out...) you need to help CMake realize that your STATIC library is a collection of object files to make it also consider weak symbols for linkage:
With
these link okay!
Fixed Version
File
CMakeLists.txtFile
include/base.hppFile
include/derived.hppFile
lib/CMakeLists.txtFile
lib/base.cppFile
lib/derived.cppFile
test/CMakeLists.txtFile
test/main.cppSee it on Github: https://github.com/sehe/so-q75452526
Summary / Notes
The linked documentation page contains advanced examples and recommendations for shipping serialization support in DLL/static libraries. They come with examples and tests in the library repository, so they may be of help (although likely not CMake specific)