2009-08-14 322 views
3

我可能忽略了一些東西,但我試圖將協議緩衝區轉換爲稍後提供擴展的簡單方法。這似乎有點不清楚,所以我會直接跳到問題。協議緩衝區擴展

我正在編寫一個程序集以支持各種任務,其中一個任務包括描述結構化數據。完美的時間使用協議緩衝區。使用協議緩衝區的主類稱爲StateDefinition。這是我想出了它的.proto文件:

 
package Kannon.State; 
message StateDefinition { 
    enum StateTypes { 
     GRAPHICS = 0; 
     AUDIO = 1; 
     MIND = 2; 
     PHYSICS = 3; 
     NETWORK = 4; 
     GENERIC = 5; 
    } 
    repeated StateTypes requiredStates = 1; 
    optional GraphicsStateDef Graphics = 2; 
    optional AudioStateDef Audio = 3; 
     (etc) 
} 

message GraphicsStateDef { 
    extensions 100 to max; 
} 

message AudioStateDef { 
    extensions 100 to max; 
} 
    (etc) 

我的目標是讓那些_StateDef消息後與它所需要的領域擴展。但是,這種擴展會獨立於我目前正在編寫的庫。

Kagents.dll - > 處理StateDefinition解析等。

引用Kagents.dll的某些東西 - > 有一個帶有「extend GraphicsStateDef」的protobuff文件來定義所需的狀態。

我希望定義「擴展GraphicsStateDef」將生成代碼,使我可以使用屬性來訪問這些字段,並避免繁瑣的「Extendible.AppendValue()」和GetValue()語法。

一個解決方案我設計,這似乎hackish的,是在引用的DLL來定義一個類的擴展方法,像這樣:

 
    public static class GraphicsExt 
    { 
     enum Fields 
     { 
      someValue = 1, 
      someOtherValue = 2 
     } 

     public static Int32 someValue(this State.GraphicsStateDef def) 
     { 
      return Extensible.GetValue(def, Fields.someValue); 
     } 
     public static void someValue(this State.graphicsStateDef def, Int32 value) 
     { 
      Extensible.AppendValue(def, fields.someValue, value); 
     } 
    } 

如果有人能想到更好的辦法,我會非常感激。 =) 另外,我不清楚我對問題的描述是多麼清晰,所以如果有任何澄清或我可以提供的更多信息,請讓我知道。 =)

編輯: 所以,想了很多關於這一點,並意識到我接近錯誤的問題。 StateReference應該存儲不同GameState的列表。它還存儲一個StateDefinition,它應該描述這個狀態引用的狀態。目前,我試圖反序列化狀態緩衝區到不同的類(GraphicsStateDef),當我真的應該反序列化到狀態對象本身。

因此,我需要重新考慮設計,使StateDefinition成爲流的容器,併爲「重複的StateTypes requiredStates = 1」字段僅提取足夠的信息。然後,在引用程序集中,流的其餘部分可以反序列化到相應的狀態。

有沒有人有如何解決這個問題的建議?一些想法正在制定中,但沒有具體的,我很喜歡其他人的投入。

+0

你使用protobuf網?擴展定義的代碼生成是否存在已知的問題? – Merritt 2009-08-15 03:49:07

+0

是的,我正在使用protobuf網。不,我不知道,我會檢查。它甚至不是一個代碼生成問題,是我無法想象使用哪種語言機制來「完成」外部程序集中的類。部分班級工作很好,但拒絕跨越議會邊界。 – Quantumplation 2009-08-15 20:50:08

+0

對我的編輯有什麼想法?我仍然不太確定我想如何解決這個問題。 – Quantumplation 2009-08-16 20:09:33

回答

0

最終的答案:

好了,所以,前幾天我看中了一個解決方案,我只是更新此萬一別人運行到同樣的問題。

整個問題源於我沒有意識到protobuf-net可以支持byte []的事實。所以,這裏是我的解決方案:

namespace Kannon.State 
{ 
    /// <summary> 
    /// ReferenceDefinition describes the layout of the reference in general. 
    /// It tells what states it should have, and stores the stream buffers for later serialization. 
    /// </summary> 
    [ProtoBuf.ProtoContract] 
    public class ReferenceDefinition 
    { 
     /// <summary> 
     /// There are several built in state types, as well as rudimentary support for a "Generic" state. 
     /// </summary> 
     public enum StateType 
     { 
      Graphics=0, 
      Audio, 
      Mind, 
      Physics, 
      Network, 
      Generic 
     } 

     /// <summary> 
     /// Represents what states should be present in the ReferenceDefinition 
     /// </summary> 
     [ProtoBuf.ProtoMember(1)] 
     List<StateType> m_StatesPresent = new List<StateType>(); 

     /// <summary> 
     /// Represent a list of StateDefinitions, which hold the buffers for each different type of state. 
     /// </summary> 
     [ProtoBuf.ProtoMember(2)] 
     List<StateDefinition> m_StateDefinition = new List<StateDefinition>(); 

     /// <summary> 
     /// Add a state, mapped to a type, to this reference definition. 
     /// </summary> 
     /// <param name="type">Type of state to add</param> 
     /// <param name="def">State definition to add.</param> 
     public void AddState(StateType type, StateDefinition def) 
     { 
      // Enforce only 1 of each type, except for Generic, which can have as many as it wants. 
      if (m_StatesPresent.Contains(type) && type != StateType.Generic) 
       return; 
      m_StatesPresent.Add(type); 
      m_StateDefinition.Add(def); 
     } 
    } 

    /// <summary> 
    /// Represents a definition of some gamestate, storing protobuffered data to be remapped to the state. 
    /// </summary> 
    [ProtoBuf.ProtoContract] 
    public class StateDefinition 
    { 
     /// <summary> 
     /// Name of the state 
     /// </summary> 
     [ProtoBuf.ProtoMember(1)] 
     string m_StateName; 
     /// <summary> 
     /// Byte array to store the "data" for later serialization. 
     /// </summary> 
     [ProtoBuf.ProtoMember(2)] 
     byte[] m_Buffer; 

     /// <summary> 
     /// Constructor for the state definition, protected to enforce the Pack and Unpack functionality to keep things safe. 
     /// </summary> 
     /// <param name="name">Name of the state type.</param> 
     /// <param name="buff">byte buffer to build state off of</param> 
     protected StateDefinition(String name, byte[] buff) 
     { 
      m_StateName = name; 
      m_Buffer = buff; 
     } 

     /// <summary> 
     /// Unpack a StateDefinition into a GameState 
     /// </summary> 
     /// <typeparam name="T">Gamestate type to unpack into. Must define Protobuf Contracts.</typeparam> 
     /// <param name="def">State Definition to unpack.</param> 
     /// <returns>The unpacked state data.</returns> 
     public static T Unpack<T>(StateDefinition def) where T:GameState 
     { 
      // Make sure we're unpacking into the right state type. 
      if (typeof(T).Name == def.m_StateName) 
       return ProtoBuf.Serializer.Deserialize<T>(new MemoryStream(def.m_Buffer)); 
      else 
       // Otherwise, return the equivalent of Null. 
       return default(T); 
     } 

     /// <summary> 
     /// Pack a state type into a State Definition 
     /// </summary> 
     /// <typeparam name="T">Gamestate to package up. Upst define protobuf contracts.</typeparam> 
     /// <param name="state">State to pack up.</param> 
     /// <returns>A state definition serialized from the passed in state.</returns> 
     public static StateDefinition Pack<T>(T state) where T:GameState 
     { 
      // Using a memory stream, to make sure Garbage Collection knows what's going on. 
      using (MemoryStream s = new MemoryStream()) 
      { 
       ProtoBuf.Serializer.Serialize<T>(s, state); 
       // Uses typeof(T).Name to do semi-enforcement of type safety. Not the best, but it works. 
       return new StateDefinition(typeof(T).Name, s.ToArray()); 
      } 
     } 
    } 
} 
1

我是protobuf-net的作者。我沒有添加任何東西來解決這個情況(除了Extensible代碼),但我願意提供您認爲應該做什麼的建議。

我還需要檢查「protoc」(在代碼生成之前用於解析.proto的.proto編譯器)是否允許區分常規成員和擴展成員。

+0

嗯,我不確定。在我看來,在代碼生成工作之後,允許將擴展消息的消息轉換爲派生類。或者可能採取我的方法並將其自動化,爲自動訪問IExtensible接口生成「擴展方法」。 我對你的系統並不太熟悉(現在已經使用了它3到4天),因此我不覺得足以向所有保證人建議一個解決方案。 – Quantumplation 2009-08-15 22:42:45

+0

這是一個遺憾,沒有「擴展屬性」...繼承將是棘手的,因爲有一個單獨的遺傳意義,不會混合好(特別是單一繼承)。我還需要檢查擴展是否可以區分,或者它們是否與協議相同......我將進行調查。 – 2009-08-15 23:25:28