2009-08-14 3 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 - > 必要な状態を定義するために "GraphicsStateDefを拡張する" protobuffファイルがあります。

「GraphicsStateDefを拡張する」と定義すると、プロパティを使用してこれらのフィールドにアクセスし、面倒な「Extendible.AppendValue()」およびGetValue()構文を避けるコードが生成されることを期待していました。ハックようだ私が考案し

一つの解決策は、これと同様に、拡張メソッドで参照する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も格納されています。StateDefinitionは、この状態参照の状態を記述する必要があります。現在、状態バッファーを別のクラス(GraphicsStateDef)にデシリアライズしようとしていますが、実際には状態オブジェクト自体にデシリアライズする必要があります。

したがって、StateDefinitionがストリームのコンテナになり、 "repeated StateTypes requiredStates = 1"フィールドの情報だけを抽出するように設計を再考する必要があります。次に、参照アセンブリでは、残りのストリームをそれぞれの状態にデシリアライズすることができます。

これにどのようにアプローチするのかとお考えの方はいらっしゃいますか?いくつかのアイデアが策定されていますが、具体的なものはありません。私は他のアイデアが大好きです。

+0

protobuf-netを使用していますか?拡張定義のコード生成に関する既知の問題はありますか? – Merritt

+0

はい、私はprotobuf-netを使用しています。そして、私が知っていることではないが、私はチェックするだろう。コード生成の問題もそれほどではありません。外部アセンブリのクラスを「終了」するために使用する言語メカニズムを考えることができません。部分クラスはうまくいくが、アセンブリの境界を越えることを拒否する。 – Quantumplation

+0

私の編集に関する考えは?どのように私はこれに近づきたいのかまだよく分かりません。 – Quantumplation

答えて

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

+0

"拡張プロパティ"がないことは残念です...継承が別々の意味(特に単一の継承)がうまく混ざり合わないため、継承が難しいです。私はまだ拡張機能が区別できるかどうか、あるいはそれらがprotocと同じかどうかをチェックする必要があります...私は調査します。 –