2009-08-14 223 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