[Serialization] Serialization of a class in a DLL

Hi everybody, I have just tried a simple example of a class serialization in a DLL and did not manage to make it work. I have been using Boost serialization since a while but until now I was putting all the serialization code in the .hpp. By the way thanks Robert (and Boost) for this great library. I am using MSVC 8.0 and Boost 1.34.1. I have tried the same with Boost Serialization 1.36 (head of Boost) without any success. My test is the following. The class MyClass in declared in a DLL boost-example.dll built from Example.cpp. A test program Example.t.cpp linked with boost-example.dll try to serialize MyClass to an XML archive. The files are the following: //////// Example.hpp #if _MSC_VER > 1000 #pragma once #endif // Do NOT include any headers after this point #if (defined(__COMPILING_Example_CPP__) && defined(WIN32)) #define _LIBSPEC __declspec(dllexport) #else #define _LIBSPEC #endif class _LIBSPEC MyClass { public: MyClass(); private: friend class boost::serialization::access; template <class Archive> void serialize(Archive & ar, const unsigned int version); std::string m_str; }; //////// Example.cpp #include <string> #include "boost/archive/xml_oarchive.hpp" #include "boost/archive/xml_iarchive.hpp" #include "boost/serialization/nvp.hpp" #include "boost/serialization/export.hpp" #include "Example.hpp" using namespace std; BOOST_CLASS_EXPORT(MyClass); // should explicity instantiate template for // xml_oarchive and xml_iarchive MyClass::MyClass() : m_str("MyClass") {} template <class Archive> void MyClass::serialize(Archive & ar, const unsigned int version) { ar & BOOST_SERIALIZATION_NVP(m_str); } //////// Example.t.cpp #include <iostream> #include <fstream> #include "boost/archive/xml_oarchive.hpp" #include "boost/serialization/nvp.hpp" #include "Example.hpp" using namespace std; int main(int argc, char* argv[]) { MyClass myClass; // try to serialize MyClass using an XML archive ofstream ofs("myClass.xml"); if( !ofs ) { cout << "ERROR: Could not open myClass.xml for writing"; return EXIT_FAILURE; } try { boost::archive::xml_oarchive oa(ofs); oa << BOOST_SERIALIZATION_NVP(myClass); } catch (const std::exception& e) { cout << "failed to serialize MyClass with xml_oarchive: " << e.what(); ofs.setstate(ios::failbit); } return EXIT_SUCCESS; } When compiling this, I got an error at link time for Example.t: Example.t.obj : error LNK2019: unresolved external symbol "private: void __thisc all MyClass::serialize<class boost::archive::xml_oarchive>(class boost::archive: :xml_oarchive &,unsigned int)" (??$serialize@Vxml_oarchive@archive@boost@@@MyCla ss@@AAEXAAVxml_oarchive@archive@boost@@I@Z) referenced in function "public: stat ic void __cdecl boost::serialization::access::serialize<class boost::archive::xm l_oarchive,class MyClass>(class boost::archive::xml_oarchive &,class MyClass &,u nsigned int)" (??$serialize@Vxml_oarchive@archive@boost@@VMyClass@@@access@seria lization@boost@@SAXAAVxml_oarchive@archive@2@AAVMyClass@@I@Z) I have taken a look at boost-example.dll with Dependency Walker and there are quite a few symbols exported for MyClass and boost::archive::xml_oarchive. Any clue of what is going on? The strange thing is that I have another more complex example with an abstract class and derived classes in separate DLLs where I serialize to the base pointer and everything is compiling and working fine (both with 1.34.1 and HEAD). I have read the doc ('Exporting Class Serialization') and have seen in the logs that a lot have changed regarding export since 1.34.1. In what I have understood, the fix was concerning cases where the macro BOOST_CLASS_EXPORT() is used in the .hpp, leading to multiple registrations, and not in the .cpp as I am doing. Am I right? By the way, I have noticed a little typo in the doc concerning void_cast_register. In the doc the proposed syntax is 'boost::serialization::void_cast_register<derived, base>(static_cast<base *>(NULL), static_cast<derived *>(NULL))' although in my experience the correct syntax is: 'boost::serialization::void_cast_register<derived, base>(static_cast<derived*>(NULL),static_cast<base*>(NULL))' Thanks in advance, Cyril Picat

In order for this to work, you have to explicitly instantiate the code in the library/dll. It will not be instantiated automatically as it is not called from within the library. For an example of this take a look at demo_pimple Robert Ramey Cyril Picat wrote:
Hi everybody,
I have just tried a simple example of a class serialization in a DLL and did not manage to make it work. I have been using Boost serialization since a while but until now I was putting all the serialization code in the .hpp. By the way thanks Robert (and Boost) for this great library.
I am using MSVC 8.0 and Boost 1.34.1. I have tried the same with Boost Serialization 1.36 (head of Boost) without any success.
My test is the following. The class MyClass in declared in a DLL boost-example.dll built from Example.cpp. A test program Example.t.cpp linked with boost-example.dll try to serialize MyClass to an XML archive.
The files are the following:
//////// Example.hpp #if _MSC_VER > 1000 #pragma once #endif
// Do NOT include any headers after this point #if (defined(__COMPILING_Example_CPP__) && defined(WIN32)) #define _LIBSPEC __declspec(dllexport) #else #define _LIBSPEC #endif
class _LIBSPEC MyClass { public: MyClass();
private: friend class boost::serialization::access;
template <class Archive> void serialize(Archive & ar, const unsigned int version);
std::string m_str;
};
// add the folloing here #include <boost/archive/xml_oarchive.hpp> #include <boost/archive/xml_iarchive.hpp> .... // instanciate the code we want to generate and add tothe library template MyClass::serialize<boost::archive::xml_oarchive>; template MyClass::serialize<boost::archive::xml_iarchive>;

On Thu, May 29, 2008 at 10:20 AM, Robert Ramey <ramey@rrsd.com> wrote:
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp> ....
// instanciate the code we want to generate and add tothe library template MyClass::serialize<boost::archive::xml_oarchive>;
template MyClass::serialize<boost::archive::xml_iarchive>;
Shouldn't there also be something to register the types and archives that are statically linked into the DLL so that serialization of shared_ptr<polymorphic_base>, etc. can find them? Also, is it possible to safely unload a DLL that uses boost::serialization? -- Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

Emil Dotchevski wrote:
On Thu, May 29, 2008 at 10:20 AM, Robert Ramey <ramey@rrsd.com> wrote:
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp> ....
// instanciate the code we want to generate and add tothe library template MyClass::serialize<boost::archive::xml_oarchive>;
template MyClass::serialize<boost::archive::xml_iarchive>;
Shouldn't there also be something to register the types and archives that are statically linked into the DLL so that serialization of shared_ptr<polymorphic_base>, etc. can find them?
This would be necessary for those types - and only those types which use BOOST_CLASS_EXPORT. In this case the BOOST_CLASS_EXPORT would go into the *.cpp module which explicitly instantiates the code for each archive.
Also, is it possible to safely unload a DLL that uses boost::serialization?
Yes - when the DLL is unloaded, the main registration table is updated accordingly. The only restriction is that you can't be unloading dlls at the same time you're serializing some type which requires code in that dll. Robert Ramey

Ok, I thought that BOOST_CLASS_EXPORT will take care of instantiating it. Actually isn't it what is explained in the doc in paragraph 'Exporting Class Serialization'? I am a bit confused and sure I should dive into the Boost.Serialization code to understand more what the macro is doing. The problem with your solution is that I don't want to include any Boost header in Example.hpp. So I have tried the solution from demo_pimpl but it still fail to link with vc 8.0 (there is a comment saying that it fails with vc 7.0)? Is it expected? So I have ended with forward declaring the archives I need in Example.hpp and it works. Though, I would rather but the explicit template instantiation in Example.cpp to keep a clean implementation agnostic header. Is there any way to make this work with VC ? So if anybody is interested, the final Example.hpp is: /////// Example.hpp #if _MSC_VER > 1000 #pragma once #endif // Do NOT include any headers after this point #if (defined(__COMPILING_Example_CPP__) && defined(WIN32)) #define _LIBSPEC __declspec(dllexport) #define _EXPORT_TEMPLATE template _LIBSPEC #else #define _LIBSPEC #define _EXPORT_TEMPLATE extern template _LIBSPEC #endif namespace boost { namespace archive { class xml_oarchive; class xml_iarchive; }; }; class _LIBSPEC MyClass { public: MyClass(); private: friend class boost::serialization::access; template <class Archive> void serialize(Archive & ar, const unsigned int version); std::string m_str; }; _EXPORT_TEMPLATE void MyClass::serialize<boost::archive::xml_oarchive>(boost::archive::xml_oarchive& ar, const unsigned int version); _EXPORT_TEMPLATE void MyClass::serialize<boost::archive::xml_iarchive>(boost::archive::xml_iarchive& ar, const unsigned int version); anyway, thanks your your help and your prompt reply. Cyril Picat -----Original Message----- From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Robert Ramey Sent: Thursday, May 29, 2008 1:20 PM To: boost-users@lists.boost.org Subject: Re: [Boost-users] [Serialization] Serialization of a class in a DLL In order for this to work, you have to explicitly instantiate the code in the library/dll. It will not be instantiated automatically as it is not called from within the library. For an example of this take a look at demo_pimple Robert Ramey Cyril Picat wrote:
Hi everybody,
I have just tried a simple example of a class serialization in a DLL and did not manage to make it work. I have been using Boost serialization since a while but until now I was putting all the serialization code in the .hpp. By the way thanks Robert (and Boost) for this great library.
I am using MSVC 8.0 and Boost 1.34.1. I have tried the same with Boost Serialization 1.36 (head of Boost) without any success.
My test is the following. The class MyClass in declared in a DLL boost-example.dll built from Example.cpp. A test program Example.t.cpp linked with boost-example.dll try to serialize MyClass to an XML archive.
The files are the following:
//////// Example.hpp #if _MSC_VER > 1000 #pragma once #endif
// Do NOT include any headers after this point #if (defined(__COMPILING_Example_CPP__) && defined(WIN32)) #define _LIBSPEC __declspec(dllexport) #else #define _LIBSPEC #endif
class _LIBSPEC MyClass { public: MyClass();
private: friend class boost::serialization::access;
template <class Archive> void serialize(Archive & ar, const unsigned int version);
std::string m_str;
};
// add the folloing here #include <boost/archive/xml_oarchive.hpp> #include <boost/archive/xml_iarchive.hpp> .... // instanciate the code we want to generate and add tothe library template MyClass::serialize<boost::archive::xml_oarchive>; template MyClass::serialize<boost::archive::xml_iarchive>; _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

Cyril Picat wrote:
Ok, I thought that BOOST_CLASS_EXPORT will take care of instantiating it.
It is not know when the library is built for which archive classes it should be instantiated - unless you include the archive headers and use explicit instantiation. Actually isn't it what is explained in the doc in paragraph
'Exporting Class Serialization'? I am a bit confused and sure I should dive into the Boost.Serialization code to understand more what the macro is doing.
I'm not sure which version you're using. The version in the trunk has updated information on BOOST_CLASS_EXPORT which tries to be more clear about this confusing topic.
The problem with your solution is that I don't want to include any Boost header in Example.hpp.
Hmm - if you want to specify a member function serialize - Its not unreasonable to have to include the boost head which declares its arguments.
So I have tried the solution from demo_pimpl but it still fail to link with vc 8.0 (there is a comment saying that it fails with vc 7.0)? Is it expected?
I think it passes with vc 7.1 - as far as I know the example is correct. A deeper investigation of this might resolve the confusion.
So I have ended with forward declaring the archives I need in Example.hpp and it works.
Though, I would rather but the explicit template instantiation in Example.cpp to keep a clean implementation agnostic header. Is there any way to make this work with VC ?
Here's what I thnk you want to do: a) make your serialize functions - include boost serialization headers b) make a module in your library which includes the archive headers or ALL the archives you might want to use. c) explicitly instantiate all your serializaiton functions for these archives Then you main program should link with no problem. It will compile faster and include only that code actually used. The same solution works for serialization code in DLLS. Robert Ramey
participants (3)
-
Cyril Picat
-
Emil Dotchevski
-
Robert Ramey