2

派生クラスをベースクラスポインタに逆シリアル化するのを手伝ってください。完全なソースコードの例を添付します。

request.hpp(ペアcppファイルなし)

#ifndef REQUEST_HPP
#define REQUEST_HPP

#include <memory>
#include <string>

#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>

namespace demo {
namespace common {

        class request {
        public:
            static const int INVALID_ID = -42;

            request() 
                : id_(INVALID_ID), timestamp_(0), source_ip_("unknown") {};

            request(int id, long timestamp, const std::string& source_ip) 
                : id_(id), timestamp_(timestamp), source_ip_(source_ip) {};

            virtual ~request() {};

            int id() const { return id_; }
            long timestamp() const { return timestamp_; }
            std::string source_ip() const { return source_ip_; }



        protected:
            int id_;
            long timestamp_;
            std::string source_ip_;



        private:
            friend class boost::serialization::access;

            template<class Archive>
            void serialize(Archive& ar, const unsigned version) {
                ar & BOOST_SERIALIZATION_NVP(id_);
                ar & BOOST_SERIALIZATION_NVP(timestamp_);
                ar & BOOST_SERIALIZATION_NVP(source_ip_);
            }

        };

        typedef std::shared_ptr<request> request_ptr;

    }
};

#endif

command.hpp(派生クラス)

#ifndef COMMAND_HPP
#define COMMAND_HPP

#include <memory>
#include <string>

#include <boost/serialization/export.hpp>

#include <demo/common/request.hpp>

namespace demo {
    namespace common {

            class command : public request {
            public:
                command(): name_("untitled") {};
                explicit command(const std::string& name) : name_(name) {};
                virtual ~command() {};

                virtual void execute();

                std::string name() const { return name_; }

            protected:
                std::string name_;

            private:
                friend class boost::serialization::access;

                template<class Archive>
                void serialize(Archive& ar, const unsigned version) {
                    ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(request);
                    ar & BOOST_SERIALIZATION_NVP(name_);
                }

            };

            typedef std::shared_ptr<command> command_ptr;

        }
};

BOOST_CLASS_EXPORT_KEY(demo::common::command)


#endif

command.cpp

#include "command.hpp"
#include <iostream>

BOOST_CLASS_EXPORT_IMPLEMENT(demo::common::command)

namespace demo {
    namespace common {

            void command::execute() {
                std::cout << "  I am '" + name_ +"' and I am executing..." << std::endl;
            }

    }
};

serializer.hpp

#ifndef SERIALIZER_HPP
#define SERIALIZER_HPP

#include <sstream>
#include <string>

/* classes to serialize */
#include <demo/common/request.hpp>
#include <demo/common/command.hpp>

namespace demo {
    namespace common {

        class serializer {
        public:
            serializer() : {};

            template<typename T>
            std::string serialize(const T& t){  
                std::stringstream stream;
                boost::archive::xml_oarchive archive(stream);
                archive << BOOST_SERIALIZATION_NVP(t);
                std::string serialized = stream.str();

                return serialized;
            }


            template<typename T>
            void deserialize(const std::string& serialized, T& t) {
                std::stringstream stream(serialized);
                boost::archive::xml_iarchive archive(stream);
                archive >> BOOST_SERIALIZATION_NVP(t);
            }
        };

    }
}

#endif

使用例

#include <iostream>

#include <demo/common/serializer.hpp>
#include <demo/common/command.hpp>


using namespace std;
using namespace demo::common;

int main(){
    serializer serializer_;

    command r("123"); // <-- (1) my desired way of declaring
    //request* r = new command("123"); <-- (2) replacing with this makes all work!
    //command* r = new command("123"); <-- (3) replacing with this crashes the app, like (1)
    std::string s = serializer_.serialize(r);
    std::cout << s << std::endl;
    request* rr = nullptr;
    serializer_.deserialize(s, rr); //this throws an exception

    command* rrr = dynamic_cast<command*>(rr);

    rrr->execute();
}

クラスをエクスポートする前にアーカイブを含め、すべてのデフォルトのコンストラクターがメンバーを初期化するなど、必要なことはすべて行ったと思いました。

シリアライズ可能なクラスとシリアライザーはlibファイルにコンパイルされることに注意してください。次に、そのlibは、ヘッダーにアクセスでき、そのlibがリンクされている2つのサブプロジェクトで使用されます。それらはそれらのクラスを使用して相互に通信し、ネットワークを介してシリアル化されたオブジェクトを送信します。

派生クラスを基本クラスポインタに逆シリアル化できないのはなぜですか? Boost1.51とVC11を使用しています。

4

2 に答える 2

4

問題:

Boost::serialization について私が問題を引き起こした 2 つの主要な点は、次のとおりです。

  1. スタック上のオブジェクトとヒープ上のオブジェクトが混在するシリアライズ/デシリアライズ。たとえば、スタック上のオブジェクトからシリアル化してからポインターに逆シリアル化しようとすると (例: load_construct_data<> を呼び出す)、例外が発生する可能性があります。逆のシナリオと同じです。
  2. エクスポートが適切にリンクされていない。たとえば、シリアル化テンプレート/クラスを作成して.libに配置すると、エクスポートが適切にリンク/公開されないようです。これは、リンクして共有オブジェクト/DLL から使用する場合に当てはまります。

ソリューション:

#1 については、ポインターとの間で常にシリアル化/逆シリアル化するルールを作成するのが最も簡単であることがわかりました。スタック上のオブジェクトでも、シリアライズ時に一時ポインターを使用して、この規則を許可できます。例えば:

// serialize
MyObject myobj;
std::ostringstream oss;
boost::archive::text_oarchive oa(oss);
MyObject* myObjPtr = &myObj;
oa << myObjPtr; // this is different than oa << myObj!!
std::string serialized = oss.str();

// deserialize
MyObject* myNewObjPtr;
std::stringstream iss(serialized);
boost::archive::text_iarchive ia(iss);
ia >> myNewObjPtr; // invokes new, don't forget to delete (or use smart ptrs!!!)

#2 については、すべてのエクスポートを含む .cpp ファイルを作成するだけです。この CPP をモジュールに直接リンクします。つまり、一連の BOOST_CLASS_EXPORT_IMPLEMENT() を含む .cpp が作成されます。

BOOST_CLASS_EXPORT_IMPLEMENT(MyObject);
// ...

より完全な例:
以下は、非侵入型テンプレートを使用したシリアライゼーションのトリックのいくつかを示すより完全な例です。侵入型メンバー メソッドは非常に似ています。

MyObject.h

// Can be broken into MyObject.h, MyObject.cpp, MyObjectSerialization.h for example as well. 
// This stuff can live in your .lib
#include <boost/serialization/export.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
// assume this class contains GetSomeMember() returning SomeMemberType
class MyObject { /* ... */ };
BOOST_CLASS_EXPORT_KEY(MyObject);
namespace boost { namespace serialization {
  template<class Archive>
      void serialize(Archive& ar, MyObject& myObj, const unsigned int version)
  {
      ar & myObj.m_someMember;
  }

  template<class Archive>
      inline void save_construct_data(Archive& ar, const MyObject* myObj, const unsigned int version)
  {
    ar & boost::serialization::make_nvp("SomeMemberType", static_cast<const SomeMemberType&>(myObj->GetSomeMember()));
  }

  template<class Archive>
  inline void load_construct_data(Archive& ar, MyObject* myObj, const unsigned int version)
  {
      SomeMemberType t;
      ar & boost::serialization::make_nvp("SomeMemberType", t);
      ::new(myObj)MyObject(t);
  }
} } // end boost::serialization ns

MyObjectExports.cpp

// This file must be explicitly linked into your module(s) that use serialization.
// This means your executable or shared module/DLLs
#include <boost/serialization/export.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include "MyObject.h"

BOOST_CLASS_EXPORT_IMPLEMENT(MyObject);
于 2012-10-29T16:10:17.407 に答える
1

ライブラリを使用すると、デモでinput_stream_errorとunregistered_class例外が発生する可能性があります。これは、ブーストがクラスを自動的に登録する方法が原因です。

BOOST_CLASS_EXPORT* マクロを使用しているにもかかわらず、派生オブジェクトをシリアライズしてそのベースにデシリアライズすると、自動登録プロセスが混乱するようです。

ただし、アーカイブで i/o 操作を実行する前に、クラスを明示的に登録できます。

// ...
boost::archive::xml_iarchive archive(stream);
// register the class(es) with the archive
archive.template register_type<command>();
archive >> BOOST_SERIALIZATION_NVP(t);
// ...

シリアル化するときは、同じ登録順序を使用します。これにより、エクスポート マクロが不要になります。

于 2012-10-31T22:14:18.333 に答える