boost 序列化多态类
Posted
技术标签:
【中文标题】boost 序列化多态类【英文标题】:boost serialize polymorphic class 【发布时间】:2015-05-13 01:38:56 【问题描述】:通过以下示例,我尝试学习一些新概念。
-
抽象
多态类
工厂编程。
增强序列化
指针行为方式的细微差别仍然是我正在努力解决的问题。
这是我编写的一个小程序,旨在向您展示我正在努力理解的问题。 当我反序列化下面的多态对象时,我只得到一个从默认构造函数创建的对象。 TodoFactory::retrieveATodo 没有从序列化数据中重新创建对象。这由该函数中“未序列化命令”的输出显示。
这是完整的程序:
#include <string>
#include <bitset>
#include <boost/serialization/string.hpp>
#include <sstream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>
//abstract class
class aTodo
private:
friend class boost::serialization::access;
protected:
const char _initType;
public:
aTodo():_initType(0x00);
aTodo(const char type):_initType(type);
std::string oarchive()
std::ostringstream archive_stream;
boost::archive::text_oarchive archive(archive_stream);
archive << *this;
archive_stream.flush();
std::string outbound_data=archive_stream.str();
std::string foutbound_data;
foutbound_data=_initType;
foutbound_data+=outbound_data;
std::cout << "length: " << foutbound_data.length() << std::endl;
return foutbound_data;
virtual void Do()=0;
virtual ~aTodo();
template<class Archive>
void serialize(Archive & ar, unsigned int version)
ar & _initType;
;
char getInitType()return _initType;;
;
// include headers that implement a archive in simple text format
class todoExec:public aTodo
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(
Archive& ar,
unsigned int version
)
std::cout << "serialize todoexec" << std::endl;
//base
boost::serialization::base_object<aTodo>(*this);
//derived
ar & _command;
std::string _command;
protected:
public:
static const char _TYPE=0x01;
todoExec():aTodo(_TYPE);
todoExec(std::string command):aTodo(_TYPE)_command=command;;
void Do()std::cout << "foo" << std::endl;;
virtual ~todoExec();
std::string getCommand()return _command;;
;
class todoFactory
private:
protected:
public:
std::unique_ptr<aTodo> retrieveAtodo(const std::string & total)
std::cout << "here" << std::endl;
char type=total.at(0);
std::cout << "bitset: " << std::bitset<8>(type) << std::endl;
std::string remainder=total.substr(1);
if(type==0x01)
std::cout << "remainder in retrieve: " << remainder << std::endl;
std::unique_ptr<todoExec> tmp(new todoExec());
std::stringstream archive_stream(remainder);
std::cout << "stream remainder: " << archive_stream.str() << std::endl;
boost::archive::text_iarchive archive(archive_stream);
archive >> *tmp;
std::cout << "unserialized type: " << std::bitset<8>(tmp->getInitType()) << std::endl;
std::cout << "unserialized command: " << tmp->getCommand() << std::endl;
return std::move(tmp);
;
std::unique_ptr<aTodo> createAtodo(char type,std::string command)
if(type==0x01)
std::unique_ptr<todoExec> tmp(new todoExec(command));
return std::move(tmp);
;
;
int main()
char mtype=0x01;
std::string dataToSend = "ls -al /home/ajonen";
std::unique_ptr<todoFactory> tmpTodoFactory; //create factory
std::unique_ptr<aTodo> anExecTodo=tmpTodoFactory->createAtodo(mtype,dataToSend); //create ExecTodo from factory
if(auto* m = dynamic_cast<todoExec*>(anExecTodo.get()))
std::cout << "command to serialize: " << m->getCommand() << std::endl;
//archive
std::string remainder = anExecTodo->oarchive();
//now read in results that are sent back
std::unique_ptr<aTodo> theResult;
theResult=tmpTodoFactory->retrieveAtodo(remainder);
std::cout << "resultant type: " << std::bitset<8>(theResult->getInitType()) <<std::endl;
if(auto* d = dynamic_cast<todoExec*>(theResult.get()))
std::cout << "resultant Command: " << d->getCommand() <<std::endl;
return 0;
这是程序输出:
command to serialize: ls -al /home/ajonen
length: 36
here
bitset: 00000001
remainder in retrieve: 22 serialization::archive 12 0 0 1
stream remainder: 22 serialization::archive 12 0 0 1
serialize todoexec
unserialized type: 00000001
unserialized command:
resultant type: 00000001
resultant Command:
我还发现仅对基类 aTodo 调用 serialize 方法。我需要找到一种方法来使那个虚拟化,但它是一个模板函数。这是第一个问题。
【问题讨论】:
【参考方案1】:您的程序有Undefined Behaviour,因为所有工厂函数都缺少返回。
接下来,在类层次结构中使用类型代码是Design Smell。
具体提示:
序列化与反序列化相同的类型 让 Boost Serialization 处理多态(否则,为什么要使用多态,或者为什么要使用 Boost Serialization?)。当您序列化(智能)指向基址的指针时,Boost 会处理它。 注册您的课程 (BOOST_CLASS_EXPORT)。您已包含标题但没有使用它。 似乎没有工厂的原因。考虑放弃它一般来说,去除杂物。当您的代码太嘈杂时,很难想象。这是我的清理版本:
Live On Coliru
这也使用 Boost 流式传输到字符串,而无需进行不必要的复制。
#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();
【讨论】:
发布another answer 没有虚拟、继承和动态分配 谢谢你。我将通过您的示例进行调试,以查看您提出的最佳实践。 咳咳。您对学习上述访问者编程风格有什么建议。另外,您一般建议学习什么提升。我只编程了很短的时间,提升对我来说有点跳跃。 这又是什么意思:“在类层次结构中使用类型代码是一种设计气味。” 这意味着它经常是糟糕设计的标志。你可以点击那个链接。typeid
应该“成为”这种类型的代码,而虚拟调度应该消除对分支的需要。如您所见,我在这里有一个示例,它非常好 没有分支,而在另一个答案中,类型码是variant
的鉴别器。所以无论你喜欢运行时多态性(这里)还是静态多态性(另一个答案)并不重要,但无论你做什么,都不要做两次工作。它会造成混乱和维护噩梦。【参考方案2】:
这是另一个没有虚拟、继承和动态分配的例子:
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);
打印
Type: 0x1
foo: ls -al /home/ajonen
【讨论】:
以上是关于boost 序列化多态类的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Boost.serialize 序列化派生模板类?