Where to put BOOST_CLASS_EXPORT for boost::serialization?
Asked Answered
V

6

12

I'm trying to serialize a pointer to a polymorphic class Shape. So I need to use the BOOST_CLASS_EXPORT macro to define a GUID for each subclass. The problem: where to put it?

Let me show a minimal test case first:

shapes.hpp

#include <boost/serialization/access.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>

class Shape {
    friend class boost::serialization::access;

    template<typename Archive>
    void serialize(Archive &ar, unsigned int const version) {
        // nothing to do
    }

    public:
        virtual ~Shape() { }
};

class Rect : public Shape {
    friend class boost::serialization::access;

    template<typename Archive>
    void serialize(Archive &ar, unsigned int const version) {
        ar & boost::serialization::base_object<Shape>(*this);
    }

    public:
        virtual ~Rect() { }
};

#ifdef EXPORT_IN_HEADER
    BOOST_CLASS_EXPORT(Rect)
#endif

export.cpp

#include <boost/serialization/export.hpp>
#include "shapes.hpp"

#ifdef EXPORT_IN_OBJECT
    BOOST_CLASS_EXPORT(Rect)
#endif

main.cpp

#include <iostream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/export.hpp>
#include "shapes.hpp"

#ifdef EXPORT_IN_MAIN
    BOOST_CLASS_EXPORT(Rect)
#endif

int main() {
    Shape *shape = new Rect();
    boost::archive::text_oarchive ar(std::cout);
    ar << shape;
}

On gcc, I compile these with

g++ -omain main.cpp export.cpp -Wl,-Bstatic -lboost_serialization-mt -Wl,-Bdynamic -DEXPORT_IN_XXX

Here, export.cpp may look a bit silly. In my actual situation, it contains an enclosing class that uses the PIMPL idiom, and tries to serialize its (polymorphic) Shape implementation. The important point is: the BOOST_CLASS_EXPORT could be in a different object file than the code that invokes the serialization.

So here's the problem: where to use BOOST_CLASS_EXPORT? I have three options, which can be enabled using the EXPORT_IN_XXX macros.

  1. EXPORT_IN_MAIN works, but is not what I want. The code invoking the serialization should not need to know about the implementation details of the PIMPL class.

  2. EXPORT_IN_OBJECT compiles, but does not work: it results in a boost::archive::archive_exception with the message unregistered void cast. According to the documentation, this should be solved by serializing base classes using boost::serialization::base_object, like I did, but it doesn't help.

  3. EXPORT_IN_HEADER does not even compile. The macro BOOST_CLASS_EXPORT expands to a template specialization (which we'd like to be in the header file), but also to the definitiof of a static member therein. So I get a linker error about a multiple definition of 'boost::archive::detail::init_guid<Rect>::guid_initializer'.

If it matters, I'm using g++ 4.4.3 and Boost 1.40.

Venenose answered 3/8, 2010 at 12:16 Comment(2)
Have you solved this problem? I have encountered this problem myself, either getting an unregistered class exception at run time or boost::archive::detail::init_guid<SomeClass>::guid_initializer errors at compile time. I am pretty stumped, so if you've figured it out since asking this question, I'd really appreciate if you shared!! Thanks!Pareto
@bguiz: Not really solved it, no. See my answer below.Venenose
V
4

I ended up putting all the serialization code in a header s11n.h that is included from the CPP file that invokes the serialization. Essentially, the EXPORT_IN_MAIN scenario I sketched above, but with the BOOST_CLASS_EXPORT macro invocations in a different file.

This only works as long as only one compilation unit includes s11n.h, of course, so although it works for now, it's no real solution...

Venenose answered 20/10, 2010 at 19:40 Comment(2)
Alas, this didn't work for me - what I wound up doing was to use Archive::register_type(static_cast<ClassName*>(NULL)) within the serialize method of the central class being serilaized. This worked for me, h/w I don't think it is the same scenario you encountered~Pareto
@bguiz: Well, that would be an argument against use of the abbreviation, I guess... ;) But I'm the only one maintaining this code, so it's not a problem for me. Saves loads of typing.Venenose
B
10

Exporting Class Serialization of the Boost.Serialization docs (1.44.0) states the following:


BOOST_CLASS_EXPORT in the same source module that includes any of the archive class headers will instantiate code [...]

Note that the implemenation of this functionality requires that the BOOST_CLASS_EXPORT macro appear after and the inclusion of any archive class headers for which code is to be instantiated. So, code that uses BOOST_CLASS_EXPORT will look like the following:

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
... // other archives

#include "a.hpp" // header declaration for class a
BOOST_CLASS_EXPORT(a)
... // other class headers and exports

[...] Including BOOST_CLASS_EXPORT in the "a.hpp" header itself as one would do with other serialization traits will make it difficult or impossible to follow the rule above regarding inclusion of archive headers before BOOST_CLASS_EXPORT is invoked. This can best be addressed by using BOOST_CLASS_EXPORT_KEY in the header declarations and BOOST_CLASS_EXPORT_IMPLEMENT in the class definition file.


Bombastic answered 4/11, 2010 at 7:37 Comment(2)
I have put the BOOST_CLASS_EXPORT_KEY in my header and the BOOST_CLASS_EXPORT_IMPLEMENT in my implementation file. I build a library out of those, and then when I try to serialize the object in an executable that links to the library the serializer doesn't know about the type. See this example: chat.stackoverflow.com/transcript/message/28926778#28926778Chifley
@DavidDoria - sorry, can't help you there. Long time since doing anything with Boost.SerBombastic
V
4

I ended up putting all the serialization code in a header s11n.h that is included from the CPP file that invokes the serialization. Essentially, the EXPORT_IN_MAIN scenario I sketched above, but with the BOOST_CLASS_EXPORT macro invocations in a different file.

This only works as long as only one compilation unit includes s11n.h, of course, so although it works for now, it's no real solution...

Venenose answered 20/10, 2010 at 19:40 Comment(2)
Alas, this didn't work for me - what I wound up doing was to use Archive::register_type(static_cast<ClassName*>(NULL)) within the serialize method of the central class being serilaized. This worked for me, h/w I don't think it is the same scenario you encountered~Pareto
@bguiz: Well, that would be an argument against use of the abbreviation, I guess... ;) But I'm the only one maintaining this code, so it's not a problem for me. Saves loads of typing.Venenose
P
2

You can use EXPORT_IN_OBJECT but the file that contains BOOST_CLASS_EXPORT must also include all the archive hpp files that plan to use.

This is because the BOOST_CLASS_EXPORT macro registers the derived type information which each archive it thinks you will use (implicitly determined based upon which archives you have included.)

In your example, use EXPORT_IN_OBJECT but also add #include to export.cpp.

In our code, we created archives.hpp that contains the archives we use and include it where ever we need to use BOOST_CLASS_EXPORT. (That way we have a single official list of archives.)

The downside is that we need to rebuild everything when we decide to use a new archive type, but we found that much easier to use than the polymorphic archive support.

Patent answered 3/11, 2010 at 20:42 Comment(0)
N
1

Check out this older thread.

http://lists.boost.org/boost-users/2005/01/9390.php

Nautilus answered 10/8, 2010 at 18:52 Comment(0)
G
1

This problem drove me insane until I realized that my base class was not polymorphic. In other words, it never used the keyword "virtual" anywhere. Because I didn't need polymorphic behavior.

Here is how I fixed it:

  1. I just slapped the keyword "virtual" on some random method in my base class.
  2. In my derived class's .cpp file, I added the following:

    #include <boost/serialization/export.hpp>
    BOOST_CLASS_EXPORT(testnamespace::derivedclass)
    

This is all that I had to do.

Gillespie answered 7/8, 2014 at 23:17 Comment(0)
S
0

you can use and unique BOOST_CLASS_EXPORT_GUID() for each .cpp and add it only in the .cpp. not the .h

Spriggs answered 27/3, 2014 at 14:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.