2012-12-27 117 views
5

我正在嘗試爲移動設備的聯網視頻遊戲設置多用途序列化。因爲它是聯網的,所以在初始連接期間,我需要序列化遊戲狀態的所有數據,但是,一旦遊戲正在進行中,我只需要序列化某些更改。作爲boost序列化庫一部分的保存和加載方法只有版本號作爲參數。我希望能夠做的是擁有更多的參數,以便我可以根據不僅僅是版本號而更改保存和加載的條件。擴展boost序列化

Boost serialization docs are here, for reference.

這裏是平原升壓系列化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庫調用我想要的序列化方法,因爲它已經重載了運算符來調用具體的speci上面第一個例子中的fic簽名。我想象在第一個示例中顯示的呼叫save內呼叫我自己的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,但在這種情況下,它是一類,它是一個第三方庫/引擎的一部分,不是我可以修改。將提升序列化允許我序列化和反序列化這種方式?

回答

3

編輯:新的答案下面添加以解決實際問題。

你的問題意味着你反序列化爲同一個對象。如果那是乾淨的或不乾淨的情況。例如,如果你有一個國際象棋棋盤,你會想要 同步棋子的初始位置(從上次保存的遊戲繼續)。爲了在遊戲進行時傳達這些動作,將個別移動 作爲單獨的對象發送(然後將其應用於一旦接收到板對象)可能是更好的主意,而不是傳送整個板對象,其將僅傳送具有如果它已經「初始化」就更改。 通過這種方式,您可以先驗證輸入並忽略無效移動。無論如何,我只想提一提,讓我們繼續前進。

如果您有一個對象可能被多次同步,並且只需要傳輸一次成員數據,讓對象決定它是否被「初始化」(並且因此,如果它需要傳輸所有內容或只是一個子集)通過使用一個標誌(這是不序列化)。

然後,您可以檢查對象序列化代碼中的標誌,就像在您發佈的代碼中一樣(除了該標誌不是序列化方法的參數 ,而是對象的成員變量德/序列化)。如果該標誌已設置,則將所有內容解除/序列化並重置該標誌。客戶端和服務器都必須具有相同的標誌狀態,否則序列化會中斷。

或者,您可以先序列化標記,告訴接收器如何執行反序列化(例如,每個成員數據組一個位)。

請記住,反序列化必須與序列化相匹配;您必須按照序列化的順序提取相同的對象。

但是,您可以序列化多態類,因爲它們在類層次結構中被序列化,因爲它們是反序列化的 (如果有疑問,當通過基指針發送和反序列化時將其轉換爲基指針)。

關於你的第二個問題,你要找的是non-intrusive serialization。 非侵入式序列化調用獨立函數並將要被序列化的對象作爲參數傳遞(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:如果你需要控制你的(自定義)的序列化方法/功能的行爲,基於未知類型的狀態被序列化,你可以實現你自己的檔案類,它保存狀態。序列化函數然後可以查詢狀態並相應地採取行動。

此狀態(或適當的替代)也必須序列化以指示數據如何反序列化。例如,序列化函數的這種「不同行爲」可能是某種壓縮,而狀態是使用的壓縮類型。

下面是一個自定義輸出存檔的簡單示例。欲瞭解更多信息,您可以閱讀Derivation from an Existing Archive並挖掘助推源。

給出一個類,你不能修改:

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(); 
} 

本實施例的目的僅僅是說明原理。生產代碼太基本了。

+0

我有加速的問題是,我必須序列化類的成員,我想在某些情況下,做的是序列化類的唯一部分,可能甚至不是成員變量的部分,而是從成員變量的一部分構建的數據。我不會每次完全序列化所有數據,我只會序列化所需的數據以確定發生了什麼變化。只有初始序列化將包含所有數據。 –

+0

根據我的整體需求,我決定不使用Boost序列化。大多數情況下,我只是想要一個能夠將常見數據類型序列化爲二進制/從二進制序列化的庫,但提升的方式並不完全符合我的要求。我可能會寫我自己的方法來將類型轉換爲/從二進制文件,並使用一個單獨的庫來壓縮/解壓縮比特流。儘管你的回答基於我的問題接近我所需要的,所以我會將其標記爲我的問題的答案。謝謝。 –

+1

@NicFoster自從我誤解了你的問題之後,我即將刪除答案。在我(或其他人)提出正確的解決方案之前,請隨時不接受並取消其評論。你想序列化一個你不能修改的類(例如)一個向量,並且你想控制如何序列化它而不會回覆到一個全局變量。 –

0

我確實想出了一個解決方案,雖然不太理想,但我認爲無論如何都值得發帖。基本上我設置了一個單例類來管理髮送所有序列化請求,並且該類將跟蹤用於該請求的最新位標誌。所以在序列化或反序列化過程中,這些方法可以查詢這些標誌。這使我可以讓Boost的saveload方法調用更強大的一組方法,這些方法可以使用這些標誌來選擇性地僅序列化某些成員。

// 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 
} 
+0

我目前的想法是創建一個可以保存標誌的自定義存檔。然後你可以用iomanip這樣的操縱器來設置這些標誌。例如'archive << arflag(COMPLETE)<< foo << arflag(MINIMAL)<< bar;'在你的保存中你可以簡單地用'ar.get_flags()'查詢標誌。 –

+0

我相信像你提到的東西可以工作,對於自定義類型,其中'Foo'和'Bar'可以實現基於標誌的自定義序列化,類似於上面的。我遇到的限制是它期望完整的數據成員被序列化,偶爾我只需要序列化原始數據。例如,如果我有一個'std :: vector '作爲成員變量,但我只需要在網絡上序列化它的大小,是否有任何方法可以做到這一點,而無需另一個代表其大小的成員變量極難維護)? –

+1

更糟糕的情況是,您總是可以爲矢量本身編寫一個非侵入式序列化程序(在這種情況下不要包含'/ serialization/vector.hpp!),並且只在適當時序列化其大小。然而,你必須告訴接收者它只需要提取一個標記的大小(因爲你沒有發送實際的元素)。 –

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 
}