[serialization] BOOST_CLASS_EXPORT regression?

Hello, I've have a header file like this: namespace lvk { namespace nm_model { namespace runtime { class IntTraceItem {}; }}} BOOST_CLASS_EXPORT(lvk::nm_model::runtime::IntTraceItem); The header is included in two files. It used to work before, but I've just updated to CVS state of the serialization lib, and get: src/runtime/bin/gcc/debug/threading-multi/TraceArbiter.o(.bss+0x0):src/runtime/TraceArbiter.cpp:16: multiple definition of `boost::archive::detail::guid_initializer<lvk::nm_model::runtime::IntTraceItem>::instance' src/runtime/bin/gcc/debug/threading-multi/Trace.o(.bss+0x0):src/runtime/Trace.cpp:21: first defined here I attach the testcase. When I run g++ -I ~/Work/boost-rc s1.cpp s2.cpp the first error is about duplicate symbols. Am I doing something wrong or is this a regression? - Volodya

Are you sure this worked before? That would surprise me. The problem is that template that implement the "export" functionality are instantiated for each archive type previously "seen" in the header. So including the following #include <boost/archive/text_oarchive.hpp> #include <boost/serialization/export.hpp> BOOST_CLASS_EXPORT(C) in multiple modules is going to produce multiple symbols at link time. My suggestion would be to include the archive headers only in the modules that actually invoke serialization. And that be only in one module. My intention was that each class header contain BOOST_CLASS_EXPORT for its classes and the the "main" or other module (see demo_pimpl) that actually invoked the serialiation would include the headers for the archive classes that are desired. This makes it easy to switch between archive types for things like debugging, etc. Robert Ramey Vladimir Prus wrote:
Hello, I've have a header file like this:
namespace lvk { namespace nm_model { namespace runtime { class IntTraceItem {}; }}}
BOOST_CLASS_EXPORT(lvk::nm_model::runtime::IntTraceItem);
The header is included in two files. It used to work before, but I've just updated to CVS state of the serialization lib, and get:
src/runtime/bin/gcc/debug/threading-multi/TraceArbiter.o(.bss+0x0):src/runti me/TraceArbiter.cpp:16:
multiple definition of
`boost::archive::detail::guid_initializer<lvk::nm_model::runtime::IntTraceIt em>::instance'
src/runtime/bin/gcc/debug/threading-multi/Trace.o(.bss+0x0):src/runtime/Trac e.cpp:21:
first defined here
I attach the testcase. When I run
g++ -I ~/Work/boost-rc s1.cpp s2.cpp
the first error is about duplicate symbols.
Am I doing something wrong or is this a regression?
- Volodya
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

#if defined(BOOST_ARCHIVE_BINARY_WOARCHIVE_HPP) #if ! defined(BOOST_ARCHIVE_EXPORT) #define BOOST_ARCHIVE_EXPORT #endif , boost::archive::binary_woarchive #endif ---> #if ! defined(BOOST_ARCHIVE_EXPORT) ---> #define BOOST_ARCHIVE_EXPORT ---> #endif #if defined(BOOST_ARCHIVE_BINARY_WIARCHIVE_HPP) #if ! defined(BOOST_ARCHIVE_EXPORT) #define BOOST_ARCHIVE_EXPORT #endif , boost::archive::binary_wiarchive #endif You can't BOOST_EXPORT_CLASS() without including an archive type first... the snippet above, from known_archive_types.hpp, breaks things if you include serialization/export.hpp and BOOST_EXPORT_CLASS() in a header file without including an archive header: BOOST_ARCHIVE_EXPORT gets #defined even if you haven't previously included an archive header, and then some of the metaprogramming stuff involving managing lists of known archives gets pulled in, but with empty mpl lists, compile error ensues. So what naturally happens is you include the archive header above the export header, to stop the compile problem, and then you get bitten later by the multiple-symbol thing at link time. Here's a test that shows the bug: #include <boost/serialization/export.hpp> struct S { }; BOOST_CLASS_EXPORT(S) // EOF Robert, I sent you this off-list, apologies for the noise, if I'm missing some subtlety and am off-base... troy d. straszheim Robert Ramey wrote:
Are you sure this worked before? That would surprise me.
The problem is that template that implement the "export" functionality are instantiated for each archive type previously "seen" in the header. So including the following
#include <boost/archive/text_oarchive.hpp> #include <boost/serialization/export.hpp>
BOOST_CLASS_EXPORT(C)
in multiple modules is going to produce multiple symbols at link time.
My suggestion would be to include the archive headers only in the modules that actually invoke serialization. And that be only in one module. My intention was that each class header contain BOOST_CLASS_EXPORT for its classes and the the "main" or other module (see demo_pimpl) that actually invoked the serialization would include the headers for the archive classes that are desired. This makes it easy to switch between archive types for things like debugging, etc.
Robert Ramey

Robert Ramey wrote:
Are you sure this worked before? That would surprise me.
Absolutely. This worked for about half an year ;-)
The problem is that template that implement the "export" functionality are instantiated for each archive type previously "seen" in the header. So including the following
#include <boost/archive/text_oarchive.hpp> #include <boost/serialization/export.hpp>
BOOST_CLASS_EXPORT(C)
in multiple modules is going to produce multiple symbols at link time.
Yes, I see -- with the current implementation. Maybe, you changed the implementation recently?
My suggestion would be to include the archive headers only in the modules that actually invoke serialization. And that be only in one module. My intention was that each class header contain BOOST_CLASS_EXPORT for its classes and the the "main" or other module (see demo_pimpl) that actually invoked the serialiation would include the headers for the archive classes that are desired. This makes it easy to switch between archive types for things like debugging, etc.
This is fragile. If I have two modules, linked in one application, which both use serialization, then if they both include a single header with BOOST_CLASS_EXPORT, I'll get link error, right? I don't think it's realistic to expect that either: 1. Classes are serialized only in one module. 2. No header file is used by more that one module. In my case, I've moved BOOST_CLASS_EXPORT to an implementation file and it worked. I am not sure this solution is generally applicable -- one might want header-only class hierarchy... - Volodya

Vladimir Prus wrote:
Robert Ramey wrote:
Are you sure this worked before? That would surprise me.
Absolutely. This worked for about half an year ;-)
Well, I can't explain it - oh well
Yes, I see -- with the current implementation. Maybe, you changed the implementation recently?
I'm sure I have - but I can' t imagine how something like this would happen. I'll look in to it.
My suggestion would be to include the archive headers only in the modules that actually invoke serialization. And that be only in one module. My intention was that each class header contain BOOST_CLASS_EXPORT for its classes and the the "main" or other module (see demo_pimpl) that actually invoked the serialiation would include the headers for the archive classes that are desired. This makes it easy to switch between archive types for things like debugging, etc.
This is fragile. If I have two modules, linked in one application, which both use serialization, then if they both include a single header with BOOST_CLASS_EXPORT, I'll get link error, right? I don't think it's realistic to expect that either: 1. Classes are serialized only in one module. 2. No header file is used by more that one module.
Not quite. Header containing the serialization can be specified all over the place as many times as one wants. Its the combination if archve and serializaition which would appear in only one module. The pimpl example sort of illustrates how I would imagine would be a practical way of doing this: a) include BOOST_CLASS_EXPORT(C) in c.hpp b) make a module c.cpp which implements serialization for all the archives you plan to use c) build the c.cpp with granularity to the function level (the requires /Gy on microsoft platforms) d) compile c.cpp to the library. e) your "main" application can import the header c.hpp f) you link step will include the code corresponding to the combinations of archive/serialable classes that you actually have used. Once things are organized in this way you can add/delete code and headers from your "main" app as you wish and everything is taken care of automatically It gets better. If you follow the above scenario you can compile the serialization implemention modules to DLLS so that the code only gets loaded when used at runtime. It gets even better. If you follow the above and compile your serializaition modules as DLLS and use the polymorphic archive. You'll be able to update your main application and even use archive classes yet to be implemented. Even better yet. If the "main" code serializes a pointer to a base class, and a new derived class is built as a DLL, this new class can be dynamically loaded even by an older program - plugin capability. All these thiings are contemplated in the code for the current library. The last (plugins) hasn't yet been tested. And doing so will likely generate some changes / bug fixes in the library. But it has been done by Martin Ecker in his own customized version of this library. I've basically incorporated his suggestions in accordance with boost requirements.
In my case, I've moved BOOST_CLASS_EXPORT to an implementation file and it worked. I am not sure this solution is generally applicable -- one might want header-only class hierarchy...
I know people have had some problems with this in the past. I believe I've got this under control - but I don't know for sure. Note that the instantiation model for C++ has variations across implementations. In building the static and DLL versions of the library I found the simplest and most reliable way to deal with the variety of platforms was to explicitly instantiate the code I wanted. This turned out not to be a problem as I had expected. I just instantiate all combinations and include them in the library and let the linker select what it want's to use. Crude perhaps, but very effective. It also has the very convenient aspect that it greatly lowers build times and forces a more robust code factoring. Also I forgot to mention, there is always the posibiity of setting the linker switches so taht duplicate symbols are not flaged as errors. I believe that this can work, but its too big a hammer for my taste. Robert Ramey
participants (3)
-
Robert Ramey
-
troy d. straszheim
-
Vladimir Prus