boost serialize polymorphic class

浪子不回头ぞ 提交于 2019-12-06 05:49:23

Your program has Undefined Behaviour because all of the factory functions have missing returns.

Next up, using a type code in a class hierarchy is a Design Smell.

Concrete hints:

  • serialize the same type as you deserialize
  • let Boost Serialization handle the polymorphism (otherwise, why do you use polymorphism, or why do you use Boost Serialization?). Boost handles it when you serialize (smart) pointers to base.
  • register your classes (BOOST_CLASS_EXPORT). You had included the header but didn't use it.
  • There doesn't seem to be a reason for the factory. Consider dropping it

In general, remove cruft. it's hard to think when your code is too noisy. Here's my cleaned up version:

Live On Coliru

This also uses Boost for streaming to string without unnecessary copying.

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/unique_ptr.hpp>

#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>

namespace Todo
{
    struct BaseTodo {
        using Ptr = std::unique_ptr<BaseTodo>;

        virtual ~BaseTodo() = default;
        virtual void Do() = 0;
        virtual unsigned getInitType() { return 0x00; };

      private:
        friend class boost::serialization::access;
        template <class Ar> void serialize(Ar &, unsigned) {}
    };

    class Exec : public BaseTodo {
      public:
        Exec(std::string const &command = "") : _command(command){};

        virtual unsigned getInitType() { return 0x01; };
        virtual void Do() { std::cout << "foo: " << getCommand() << std::endl; };

        std::string getCommand() const { return _command; };

      private:
        friend class boost::serialization::access;
        template <class Archive> void serialize(Archive &ar, unsigned) {
            boost::serialization::base_object<BaseTodo>(*this);
            ar &_command;
        }

        std::string _command;
    };
}

//BOOST_CLASS_EXPORT(BaseTodo)
BOOST_SERIALIZATION_ASSUME_ABSTRACT(Todo::BaseTodo)
BOOST_CLASS_EXPORT(Todo::Exec)

namespace Todo 
{
    class Factory {
        Factory() = default;
      public:
        using Ptr = BaseTodo::Ptr;
        using FactoryPtr = std::shared_ptr<Factory>;

        static FactoryPtr create() { return FactoryPtr(new Factory); }

        static std::string save(Ptr todo) {
            std::string out;
            {
                namespace io = boost::iostreams;
                io::stream<io::back_insert_device<std::string> > os(out);

                boost::archive::text_oarchive archive(os);
                archive << todo;
            }

            return out;
        }

        static Ptr load(std::string const &s) {
            Ptr p;
            {
                namespace io = boost::iostreams;
                io::stream<io::array_source> is(io::array_source{ s.data(), s.size() });
                boost::archive::text_iarchive archive(is);
                archive >> p;
            }
            return std::move(p);
        }

        Ptr createExec(std::string command) { return BaseTodo::Ptr(new Exec(command)); }
    };
}

int main() {
    auto factory = Todo::Factory::create();

    // ROUNDTRIP save,load
    auto todo = factory->load(
            factory->save(
                factory->createExec("ls -al /home/ajonen")
            )
        );

    std::cout << "Type: " << std::hex << std::showbase << todo->getInitType() << std::endl;
    todo->Do();
}

Here's another take without virtuals, inheritance and dynamic allocations:

Live On Coliru

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/variant.hpp>

#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>

namespace Todo
{
    struct None {
        void Do() const {};
        template <class Ar> void serialize(Ar&, unsigned) {}
    };

    class Exec {
      public:
        Exec(std::string const &command = "") : _command(command){};
        void Do() const { std::cout << "foo: " << getCommand() << std::endl; };

        std::string getCommand() const { return _command; };

      private:
        friend class boost::serialization::access;
        template <class Ar> void serialize(Ar &ar, unsigned) {
            ar &_command;
        }

        std::string _command;
    };

    using Todo = boost::variant<None, Exec>;

    struct Factory {
        static std::string save(Todo const& todo) {
            std::string out;
            {
                namespace io = boost::iostreams;
                io::stream<io::back_insert_device<std::string> > os(out);

                boost::archive::text_oarchive archive(os);
                archive << todo;
            }

            return out;
        }

        static Todo load(std::string const &s) {
            Todo todo;
            {
                namespace io = boost::iostreams;
                io::stream<io::array_source> is(io::array_source{ s.data(), s.size() });
                boost::archive::text_iarchive archive(is);
                archive >> todo;
            }
            return std::move(todo);
        }
    };
}

namespace visitors {
    namespace detail {
        template <typename F> struct internal_vis : boost::static_visitor<void> {
            internal_vis(F& f) : _f(f) {}
            template <typename... T>
                void operator()(T&&... a) const { return _f(std::forward<T>(a)...); }
            private:
                F& _f;
        };
    }

    template <typename F, typename V>
    void apply(F const& f, V const& v) { return boost::apply_visitor(detail::internal_vis<F const>(f), v); }

    template <typename F, typename V>
    void apply(F const& f, V& v) { return boost::apply_visitor(detail::internal_vis<F const>(f), v); }
}

namespace Todo { namespace Actions { template <typename T>
        void Do(T const& todo) {
            visitors::apply([](auto const& cmd) { cmd.Do(); }, todo);
        }
} }

int main() {
    using namespace Todo;
    Factory factory;

    // ROUNDTRIP save,load
    auto todo = factory.load(
            factory.save(
                Exec("ls -al /home/ajonen")
            )
        );

    std::cout << "Type: " << std::hex << std::showbase << todo.which() << std::endl;

    Actions::Do(todo);

}

Prints

Type: 0x1
foo: ls -al /home/ajonen
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!