5

モバイル デバイス用のネットワーク ビデオ ゲームの多目的シリアル化をセットアップしようとしています。ネットワークに接続されているため、最初の接続時にゲームの状態のすべてのデータをシリアル化する必要がありますが、ゲームの進行中に特定の変更をシリアル化するだけで済みます。boost シリアライゼーション ライブラリの一部である save メソッドと load メソッドには、パラメーターとしてバージョン番号しかありません。私ができるようにしたいのは、バージョン番号以上のものに基づいて保存およびロードされる条件を変更できるように、より多くのパラメーターを用意することです。

Boost シリアライゼーションのドキュメントは、参照用にここにあります。

現在、単純なブースト シリアライゼーションsaveメソッドは次のようになります。

template<class Archive>
void save(Archive& ar, const unsigned int version) const
{
    // Serialize stuff here
}

これが私が達成したいことです:

template<class Archive>
void save(Archive& ar, const unsigned int version, const unsigned int state_flags) const
{
    if (state_flags & INITIAL_SERIALIZATION)
    {
        // Serialize only data needed for an initial serialization
    }

    // Other serialization
}

上記の最初の例で、特定の署名を使用して演算子を呼び出すようにオーバーロードされた演算子があるため、boost ライブラリに必要なシリアライゼーション メソッドを呼び出させることができるとは思えません。save最初の例で示したへの呼び出し内で独自のバージョンの from を呼び出しsave、おそらくstate_flags別の場所から を取得することを想像しています。これをきれいに行う方法、または良い代替案について誰か考えがありますか?

編集:別の問題に遭遇しました。必ずしもクラスのメンバーではないオブジェクトをシリアル化する必要がありますが、ドキュメントにはこれに対するサポートについては言及されていません。

簡単な例を次に示します。

class Foo
{
private:
    SomeClass m_object;

    template<class Archive>
    void save(Archive& ar, const unsigned int version) const
    {
        Bar* pBar = m_object->getComponent<Bar>();
        ar & pBar;   // <--- But pBar isn't a member of Bar, it's part of SomeClass.
    }
};

シリアル化SomeClassしてBar、それを . Boost シリアライゼーションを使用すると、この方法でシリアライズおよびデシリアライズできますか?

4

3 に答える 3

3

編集:実際の問題に対処するために、以下に新しい回答が追加されました。

あなたの質問は、同じオブジェクトに繰り返し逆シリアル化することを意味します。それがきれいかどうかは状況次第です。たとえば、チェス盤を持っている場合、(最後に保存したゲームから続行するために) 駒の初期位置を同期する必要があります。ゲームのプレイ中に移動を通信するには、変更されたもののみを転送するボード オブジェクト全体を送信するのではなく、個々の移動を個別のオブジェクトとして送信することをお勧めします (受信すると、ボード オブジェクトに適用されます)。すでに「初期化」されている場合。そうすれば、最初に入力を検証し、無効な動きを無視できます。とにかく、私はそれについて言及したかっただけです。次に進みましょう。

複数回同期される可能性のあるオブジェクトがあり、メンバー データが 1 回だけ転送される必要がある場合は、オブジェクトが「初期化」されているかどうかを決定します (したがって、すべてを送信する必要があるか、サブセットのみを送信する必要があるかどうか)。フラグを使用する (シリアル化されていない)。

次に、投稿したコードと同じように、オブジェクトのシリアル化コードでフラグを確認できます(ただし、フラグはシリアル化メソッドのパラメーターではなく、デ/シリアル化するオブジェクトのメンバー変数です)。フラグが設定されている場合は、すべてを逆シリアル化し、フラグをリセットします。クライアントとサーバーの両方でフラグの状態が同じである必要があります。そうしないと、シリアライゼーションが中断されます。

または、最初にフラグをシリアライズして、デシリアライゼーションを実行する必要がある方法をレシーバーに伝えることもできます (たとえば、メンバー データ グループごとに 1 ビット)。

逆シリアル化はシリアル化と一致する必要があることに注意してください。シリアル化されたのと同じ順序で、同じオブジェクトを抽出する必要があります。

ただし、逆シリアル化されるのと同じレベルのクラス階層でシリアル化されている場合は、ポリモーフィック クラスをシリアル化できます (疑わしい場合は、送信時にベース ポインターにキャストし、ベース ポインターを介して逆シリアル化します)。

2 番目の質問に関して、探しているのは非侵入型のシリアライゼーションです。非侵入型のシリアライゼーションでは、独立した関数を呼び出し、シリアライズするオブジェクトをパラメーターとして渡します (これが std::vector と boost::shared_ptr のシリアライズ方法です)。を使用BOOST_SERIALIZATION_SPLIT_FREEして、自立serialize()関数をsave()とに分割できますload()。侵入シリアライゼーションの場合はBOOST_SERIALIZATION_SPLIT_MEMBER.

一般化された逆シリアル化関数 (たとえば、ネットワークを介してオブジェクトを送信する関数) を作成するには、テンプレートを使用できます。

template<typename T>
void transmit( const T& data ) {
    // ...
    archive << data
    socket << archive_stream;
}

このメソッドの制限は、受信者が送信されたオブジェクトの種類を認識している必要があることです。ランダム オブジェクトを送信する場合は、それらをポリモーフィックにします。

IData* data = 0;
archive >> data;
switch( data->type() ) {
case TYPE_INIT:
    return dispatch( static_cast<Board*>(data) );
case TYPE_MOVE:
    return dispatch( static_cast<Move*>(data) );
case TYPE_CHAT:
    return dispatch( static_cast<ChatMsg*>(data) );
}

UPDATE : (カスタム) シリアライゼーション メソッド/関数の動作を制御する必要がある場合は、シリアライズされる型に不明な状態に基づいて、状態を保持する独自のアーカイブ クラスを実装できます。その後、シリアライゼーション関数は状態を照会し、それに応じて動作できます。

データを逆シリアル化する方法を示すために、この状態 (または適切な置換) もシリアル化する必要があります。たとえば、シリアル化関数のこの「異なる動作」はある種の圧縮である可能性があり、状態は使用される圧縮の種類です。

カスタム出力アーカイブの最小限の例を次に示します。詳細については、既存のアーカイブからの派生を読んで、ブースト ソースを掘り下げることができます。

変更できないクラスがあるとします。

struct Foo {
    Foo() : i(42), s("foo") {}
    int i;
    std::string s;
};

シリアライズしたい、iおよび/またはsクラスに不明な条件に基づいています。ラッパーを作成してシリアル化して状態を追加することもできますが、オブジェクトがベクター (またはその他のクラス) 内にある場合、これは機能しません。

代わりに、アーカイブに状態を認識させる方が簡単な場合があります。

#include <boost/archive/text_oarchive.hpp>

// using struct to omit a bunch of friend declarations    
struct oarchive : boost::archive::text_oarchive_impl<oarchive>
{
    oarchive(std::ostream& os, unsigned flags=0)
      : boost::archive::text_oarchive_impl<oarchive>(os,flags),mask(0){}

    // forward to base class
    template<class T> void save( T& t ) {
        boost::archive::text_oarchive_impl<oarchive>::save(t);
    }

    // this is the 'state' that can be set on the archive
    // and queried by the serialization functions
    unsigned get_mask() const { return mask; }
    void set_mask(unsigned m) { mask = m; }
    void clear_mask() { mask = 0; }
private:
    unsigned mask;
};

// explicit instantiation of class templates involved
namespace boost { namespace archive {
   template class basic_text_oarchive<oarchive>;
   template class text_oarchive_impl<oarchive>;
   template class detail::archive_serializer_map<oarchive>;
} }

// template implementations (should go to the .cpp)
#include <boost/archive/impl/basic_text_oarchive.ipp>
#include <boost/archive/impl/text_oarchive_impl.ipp>
#include <boost/archive/impl/archive_serializer_map.ipp>

設定してクエリする状態は次のとおりです。

enum state { FULL=0x10, PARTIAL=0x20 };

状態を設定するメソッド (これは非常に基本的な例です):

oarchive& operator<<(oarchive& ar, state mask) {
    ar.set_mask(ar.get_mask()|mask);
    return ar;
}

最後に、(非侵入型) シリアライゼーション関数:

namespace boost { namespace serialization {

template<class Archive>
void save(Archive & ar, const Foo& foo, const unsigned int version)
{
    int mask = ar.get_mask(); // get state from the archive
    ar << mask; // serialize the state! when deserializing,
    // read the state first and extract the data accordingly

    if( mask & FULL )
        ar << foo.s; // only serialize s if FULL is set
    ar << foo.i;     // otherwise serialize i only
    ar.clear_mask(); // reset the state
}

} } // boost::serialization

BOOST_SERIALIZATION_SPLIT_FREE(Foo)

そして、これは次のように使用できます。

int main() {
    std::stringstream strm;
    oarchive ar(strm);

    Foo f;
    ar << PARTIAL << f << FULL << f;

    std::cout << strm.str();
}

この例の目的は、原理を説明することだけです。実動コードには基本的すぎます。

于 2012-12-28T04:15:53.467 に答える
0

私はこれに対する解決策を思いつきました、そして理想的ではありませんが、とにかく投稿する価値があるかもしれないと思いました。基本的に、すべてのシリアル化要求の送信を管理するシングルトンクラスを設定します。そのクラスは、その要求に使用されていた最新のビットフラグを追跡します。したがって、シリアル化または逆シリアル化中に、これらのメソッドはこれらのフラグを照会できます。これにより、Boostsaveloadメソッドで、これらのフラグを使用して特定のメンバーのみを選択的にシリアル化できる、より堅牢なメソッドのセットを呼び出すことができました。

// Boost's `save` method, which must have this exact signature
template<class Archive>
void save(Archive& ar, const unsigned int version) const
{
    const unsigned int flags = SerializationManager::getFlags(); // SerializationManager is a singleton.
    saveData(ar, version, flags);
}

// Your own custom method, which can have whichever parameters you need
template<class Archive>
void saveData(Archive& ar, const unsigned int version, const unsigned int state_flags) const
{
    if (state_flags & INITIAL_SERIALIZATION)
    {
        // Serialize only data needed for an initial serialization
    }

    // Other serialization
}
于 2012-12-28T17:59:02.590 に答える
0

より簡単な方法は次のとおりです。

// Boost's `save` method, which must have this exact signature
template<class Archive>
void save(Archive& ar, const unsigned int version) const
{
    const unsigned int flags = SerializationManager::getFlags(); //         SerializationManager is a singleton.
    ar << flags;
    if(flags && INITIAL_SERIALIZATION){
        // Serialize only data needed for an initial serialization
    }
    // Other serialization
}
template<class Archive>
void load(Archive& ar, const unsigned int version) const
{
    const unsigned int flags = SerializationManager::getFlags(); //         SerializationManager is a singleton.
    unsigned int flags;
    ar >> flags;
    if(flags && INITIAL_SERIALIZATION){
        // Serialize only data needed for an initial serialization
    }
    // Other serialization
}
于 2015-11-25T00:06:51.940 に答える