2009-08-14 2 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 -> Имеет файл protobuff с расширением GraphicsStateDef для определения необходимого состояния.

Я надеялся, что определение «продлить 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); 
     } 
    } 

Если кто-то может придумать лучшего способа, я был бы очень благодарен , =) Кроме того, я не уверен, насколько ясное описание проблемы вышло, поэтому, если есть какие-либо разъяснения или дополнительная информация, которую я могу предоставить, сообщите мне. =)

EDIT: Итак, много размышляя об этом и осознав, что я неправильно подхожу к проблеме. StateReference должен хранить список разных игр GameState. Кроме того, в нем хранится StateDefinition, в котором должно описываться состояние этой государственной справки. В настоящее время я пытаюсь десериализовать буферы состояний в разные классы (GraphicsStateDef), когда мне действительно нужно десериализовать сами объекты состояния.

Поэтому мне нужно переосмыслить дизайн таким образом, чтобы StateDefinition становился контейнером для потока и извлекал только достаточно информации для поля «repeat StateTypes requiredStates = 1». Затем в сборке ссылок остальная часть потока может быть десериализована в соответствующие состояния.

Есть ли у кого-нибудь рекомендации относительно того, как подойти к этому? Несколько идей формулируются, но ничего конкретного, и мне бы хотелось, чтобы вклад других.

+0

Вы используете protobuf-net? Существуют ли какие-либо известные проблемы с генерацией кода для расширенных определений? – Merritt

+0

Да, я использую protobuf-net. И нет, не то, что я знаю, я все же проверю. Это даже не проблема генерации кода, так это то, что я не могу придумать, какой языковой механизм использовать для «завершения» класса во внешней сборке. Частичные классы будут работать отлично, но откажутся пересекать границу сборки. – Quantumplation

+0

Любые мысли о моем редактировании? Я все еще не совсем уверен, как я к этому подхожу. – Quantumplation

ответ

0

Окончательный ответ:

Хорошо, так, несколько дней назад я остановился на решении, и я просто обновляя это в случае, если кто-то работает в том же номере.

Вся проблема возникла из-за того, что я не знал, что protobuf-net может поддерживать байт []. Итак, вот мое решение:

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

Жаль, что нет свойств расширения ... наследование было бы сложным, так как существует отдельный смысл наследования, который не будет хорошо сочетаться (особенно с одиночным наследованием). Мне также нужно проверить, можно ли расширять расширения, или же они «те же» для протока ... Я буду исследовать. –

Смежные вопросы