John
John

Reputation: 3105

Boost serializing all derived types dynamically

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";
        }
    }

Upvotes: 1

Views: 189

Answers (1)

sehe
sehe

Reputation: 392833

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

Upvotes: 1

Related Questions