[serialization] serializable objects implemented in dlls

Robert, I've made a small project which replicates the problems I was having yesterday. I think that part of the problem was that my solution for exporting the guids for shared_ptrs was like this... typedef boost::detail::sp_counted_base_impl<T*, ....> T_shared_ptr; BOOST_CLASS_EXPORT(T_shared_ptr); ...and if the typedef wasn't in scope where the streaming occurred then the streaming system didn't recognize that the sp_counted_base_impl<T*,...> was a T_shared_ptr. Anyway, I've resolved that by making a more complicated macro for exporting the guids, which doesn't use any typedefs. That's included in the attached archive as hack_shared_ptr.h. It isn't possible simply to do... BOOST_CLASS_EXPORT(boost::detail::sp_counted_base_impl<T*, ....>) ...because that contains a comma, and the preprocessor thinks you're passing two parameters to a macro which expects only one. Anyway, I think the new macro (HACK_SP_BOOST_CLASS_EXPORT(T)) avoids that problem. Now to my other problem. I wanted to investigate the serialization tools in the context of a larger project. My habit is to define my class interfaces in header files, like base_class.h, and provide their implementations in source files like base_class.cpp. I like my headers to be lightweight and include the minimum number of other header files in order that compilation times don't get out of hand. I had been including the BOOST_CLASS_EXPORT(T) commands in the headers but that means I have to include all of the archive headers, and export.hpp in every header, and that makes for slow compilations. My hope is that I can include all of those only in the implementation .cpp and put the BOOST_CLASS_EXPORT(T) there. I was also worried that if I included the BOOST_CLASS_EXPORT(T) in the header then every other .cpp which included that header would also try to register the guid for T - surely an unnecessary replication of effort. Having done all of this I found that my program would work fine if everything existed in a single EXE. However, if I implement them as a DLL containing my base_class and derived_class implementations and an EXE, which uses these classes, then things fail. In the attached .ZIP there is a VC7.1 solution which builds three different projects: single_project Contains all of my example source code, and builds a single .EXE which streams two shared_ptr<base_class> to file and then loads them back. One of the pointers contains a derived_class. dll_implemetation Contains implementations of base_class and derived_class. It also contains the BOOST_CLASS_EXPORT commands for those classes. This project builds a DLL which exports these classes. exe_using_dll Contains just the main() function. It should do the same as single_project does as the main() is the same. Single_project works fine. exe_using_dll fails when it tries to save the pointer to the derived_class. The failure is an unregistered_class exception. I've stepped through the code for exe_using_dll and I can see that the failure occurs when it tries to get extended type information for the derived_class (at line 297 of oserializer.hpp). However, I know that the class has been registered by the guid_initializer (at line 130 of export.hpp) because I put a breakpoint there, and saw initialization happen for base_class, derived_class and the shared_ptr versions of both. My suspicion is that the problem is caused by the serialization library using a static library, rather than a DLL. If the BOOST_CLASS_EXPORT commands updated a list contained in the static library then there would be one instance of this list statically linked into my DLL, and another in my EXE. Thus, the registration occurring in the DLL wouldn't affect the list used in the EXE. If this is the case then it's going to be quite inconvenient. It would mean one would have to BOOST_CLASS_EXPORT every class in every DLL where an instance of it (or a shared_pointer which might contain it) might be saved. If that were the case then I'd advocate making boost_serialization into a DLL, rather than a static library. I'd be grateful if you could take a look at my example, and let me know if there is something I can do to resolve my problems. Regards, David Tonge

David Tonge wrote:
I had been including the BOOST_CLASS_EXPORT(T) commands in the headers but that means I have to include all of the archive headers, and export.hpp in every header, and that makes for slow compilations. My hope is that I can include all of those only in the implementation .cpp and put the BOOST_CLASS_EXPORT(T) there. I was also worried that if I included the BOOST_CLASS_EXPORT(T) in the header then every other .cpp which included that header would also try to register the guid for T - surely an unnecessary replication of effort.
I actually though that we should have "polymorphic archive" -- one whose operations are virtual. It would have a number of derived class which wrap existing archives in a polymorphic interface. In code: class polymorphic_archive { } : template<class Wrapped> class polymorphic_archive_wrapper { /// Forward everything to m_wrapper Wrapper& m_wrapper; }; The serialization of exported class would work like: 1. BOOST_CLASS_EXPORT registers the class with all #included archives *and* with the polymorphic archive. 2. When saving, if class was registered with the used archive types, everything works as now. Otherwise, a wrapper over archive is created and used for saving polymorphic_archive_wrapper<ActualArchiveType> w(actual_archive); w << object; This way, you don't need to #include archive headers before calling BOOST_CLASS_EXPORT at all -- everything will work. It would only be needed if you want extra efficiency. Howver, I suspect BOOST_CLASS_EXPORT has some overhead already, so this might not be an issue. The problem, now, is to have somebody implement polymorphic archive ;-)
If this is the case then it's going to be quite inconvenient. It would mean one would have to BOOST_CLASS_EXPORT every class in every DLL where an instance of it (or a shared_pointer which might contain it) might be saved. If that were the case then I'd advocate making boost_serialization into a DLL, rather than a static library.
Maybe, you could try tweaking the Jamfile so that library is build as dynamic and try your test? - Volodya

Vladimir,
I actually though that we should have "polymorphic archive" -- one whose operations are virtual. It would have a number of derived class which wrap existing archives in a polymorphic interface.
That sounds cool. It would save me a lot of heartache if I wanted to use a new type of archive. Right now I have a header which #includes all of the archives I might ever use, and I have to put that header in every file before I #include export.hpp.
Maybe, you could try tweaking the Jamfile so that library is build as dynamic and try your test?
Good point. I tried adding the section below (which is based on the same section in date_time's jamfile) to the jamfile (btw, jamfiles are very cool). Anyway, that did lead to a .dll, but I guess there aren't __declspec(dllexports) in the right places because the .dll had no exported functions (and because of that, the linker didn't bother to produce a .lib for the stubs). I guess we might need source code changes. DT ---- dll boost_serialization : ## sources ## ../src/$(SOURCES).cpp : ## requirements ## <include>$(SPIRIT_ROOT) <include>$(BOOST_ROOT) <sysinclude>$(BOOST_ROOT) <borland><*><cxxflags>-w-8080 <msvc><*><cxxflags>-Gy <vc7><*><cxxflags>-Gy <vc7.1><*><cxxflags>-Gy <define>BOOST_ALL_DYN_LINK=1 # the common names rule ensures that the library will # be named according to the rules used by the install # and auto-link features: common-variant-tag : ## default-build debug release #<runtime-link>static <threading>single #<runtime-link>static/dynamic #<runtime-link>dynamic <runtime-link>dynamic <threading>multi ;
participants (2)
-
David Tonge
-
Vladimir Prus