1

Is it absolutely necessary to call BOOST_CLASS_EXPORT() or register_type() for all derived types of a virtual base class? Is there any way to just specify the base class?

Here's some sample code (I'm using boost 1.59):

    #include <iostream>
    #include <string>
    #include <memory>
    #include <boost/serialization/serialization.hpp>
    #include <boost/serialization/shared_ptr.hpp>
    #include <boost/archive/text_oarchive.hpp>
    #include <boost/archive/text_iarchive.hpp>
    //#include <boost/make_shared.hpp>
    #include <boost/serialization/export.hpp>
    #include <boost/serialization/base_object.hpp>

    class Parent {
      public:
        int test_val = 1234234;

        int p() { return 13294; }
            virtual void testing() = 0;
        int get_test_val() {
            std::cout << __PRETTY_FUNCTION__ << ":" << test_val << "\n";
            return test_val;
        }

        template <class Archive> void serialize(Archive &ar, unsigned) {
            ar & test_val;
        }
    };

    //BOOST_SERIALIZATION_ASSUME_ABSTRACT(Parent)

    class RefMem : public Parent {
      public:
        RefMem() {
            test_val = 12342;
            std::cout << __PRETTY_FUNCTION__ << ":" << test_val << "\n";
        }   
            void testing() {
                    std::cout << "TEST" << std::endl;
            }
            template<class Archive> void serialize(Archive &ar, unsigned) {
                    ar & boost::serialization::base_object<Parent>(*this);
            }
    };


class RefMem2 : public Parent {
        public:
                RefMem2() {
                        test_val  = 9823;
                        std::cout << __PRETTY_FUNCTION__ << ":" << test_val << "\n";
                }

                void testing() {
                        std::cout << "TEST2" << std::endl;
                }
                 template<class Archive> void serialize(Archive &ar, unsigned) {
                        ar & boost::serialization::base_object<Parent>(*this);
                }
};
    using ParentRef = std::shared_ptr<Parent>;

    class Test {
      public:  
        int t_ = 0;
        ParentRef parent_;

        Test(int t = 0, ParentRef parent = std::make_shared<RefMem>()) : t_(t), parent_(parent) { }

        template <class Archive> void serialize(Archive &ar, const unsigned int file_version) {
            ar & t_ & parent_;
        }
    };

    //BOOST_CLASS_EXPORT(RefMem)

    #include <sstream>

    int main() {
        ParentRef the_instance = std::make_shared<RefMem>();

        Test test = Test(50, the_instance);

        std::cout << "t_: " << test.t_ << "\n";
        std::cout << "Test val: " << test.parent_->get_test_val() << "\n";
        std::ostringstream oss;
        {
            boost::archive::text_oarchive oa(oss);
            oa.register_type<RefMem>();
            oa.register_type<RefMem2>();
            oa << the_instance << test; // NOTE SERIALIZE test AS-IF A POINTER
        }

        {
            ParentRef the_cloned_instance;
            Test cloned;

            std::istringstream iss(oss.str());
            {
                boost::archive::text_iarchive ia(iss);
                    ia.register_type<RefMem>();
                    ia.register_type<RefMem2>();
                ia >> the_cloned_instance >> cloned;
            }

            std::cout << "t_: " << cloned.t_ << "\n";
            std::cout << "Test val: " << cloned.parent_->get_test_val() << "\n";
            std::cout << "Are Parent objects aliasing: " << std::boolalpha <<
                (cloned.parent_ == the_cloned_instance) << "\n";
        }
    }
John
  • 3,037
  • 8
  • 36
  • 68

1 Answers1

1

Yes this is required.

You already see the mechanics. The macro is nothing but a way to automate the register_type dance for the archive types known at the time of the macro expansion.

You could, in this instance, get away with listing the types in the Test serialize method, which would be "just in time" - provided that you do not deserialize Parent polymorphic instances before that.

This also works:

class Test {
public:
    int t_ = 0;
    ParentRef parent_;

    Test(int t = 0, ParentRef parent = std::make_shared<RefMem1>()) : t_(t), parent_(parent) {}

    template <class Archive> void serialize(Archive &ar, unsigned) {
        ar.template register_type<RefMem1>();
        ar.template register_type<RefMem2>();
        ar & t_ & parent_; 
    }
};

Iff you change the archive order:

    oa << test << the_instance;
    // ...
    ia >> cloned >> the_cloned_instance;

See it Live On Coliru

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Two questions: 1) Must I register/export everywhere in my library wherever I serialize a derived class through it's base? There's no one and done? 2) Which one do you think should be used? The macro, i.e. BOOST_CLASS_EXPORT() or register_type<>()? I read that register_type<>() allows for a sort of dynamic export where you can choose specific derived classes, but I can't see the point of that if you're using a base class in the first place. – John Sep 09 '15 at 10:24
  • I found this (http://stackoverflow.com/questions/3396330/where-to-put-boost-class-export-for-boostserialization) as a potential answer to #1, but I couldn't follow the reasoning for not including export in the header. The sentence "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." makes it sound like it's possible, but not recommended. – John Sep 09 '15 at 10:29
  • #1 There is no shortcut (there is no reflection in C++ so there would be literally no possible way to know what types might be deserialized with you explicitly registering it. There are two mechanisms: the `register_type` is more tedious but gives more control. `EXPORT` macros are more convenient. – sehe Sep 09 '15 at 11:43
  • #2 see http://stackoverflow.com/a/32447142/85371 ; you can split the macros as [per the documentation](http://www.boost.org/doc/libs/1_59_0/libs/serialization/doc/special.html#dlls) – sehe Sep 09 '15 at 11:46
  • Thank you. Also in your code on coliru, you have register_type on lines 86 and 87 which I tested aren't necessary and is different from what you mention in your answer, right? That might confuse someone. – John Sep 09 '15 at 17:09