2009-08-14 5 views
3

Je suis peut-être en train de négliger quelque chose, mais j'essaie de lutter contre les tampons de protocole dans une méthode facile pour fournir des extensions plus tard. Cela semble un peu flou, donc je vais sauter directement dans le problème.Tampons de protocole avec des extensions

Je suis en train d'écrire un ensemble pour supporter diverses tâches, dont l'une comprend la description de données structurées. Temps parfait pour utiliser les tampons de protocole. La classe primaire à utiliser les tampons de protocole s'appelle StateDefinition. Voici le fichier .proto je suis venu avec pour elle:

 
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) 

Mon objectif était de permettre à ces messages _StateDef soient étendus plus tard avec ce que les champs dont il aurait besoin. Cependant, cette extension serait indépendante de la bibliothèque que j'écris actuellement. Kagents.dll -> Gère l'analyse syntaxique de StateDefinition et ainsi de suite. Quelque chose référençant Kagents.dll -> A un fichier de protobuff avec "extend GraphicsStateDef" pour définir l'état nécessaire. J'espérais que la définition de "extend GraphicsStateDef" génèrerait du code qui me permettrait d'utiliser les propriétés pour accéder à ces champs, et d'éviter la lourde syntaxe "Extendible.AppendValue()" et GetValue().

Une solution j'ai conçu, qui semble hackish, est de définir une classe dans la DLL faisant référence à des méthodes d'extension, comme ceci:

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

Si quelqu'un peut penser à une meilleure façon, je serais bien obligé . =) En outre, je ne suis pas sûr de la clarté de ma description du problème, donc s'il y a des éclaircissements ou des informations supplémentaires que je peux fournir, s'il vous plaît faites le moi savoir. =)

EDIT: Donc, après avoir réfléchi beaucoup à ce sujet et réalisé que j'aborde le problème mal. StateReference est supposé stocker une liste de différents GameState. De même, il stocke une StateDefinition, qui devrait décrire l'état de cette référence d'état. Actuellement, j'essaie de désérialiser les tampons d'état en différentes classes (GraphicsStateDef), alors que je devrais vraiment désérialiser dans les objets d'état eux-mêmes.

Par conséquent, j'ai besoin de repenser la conception de telle sorte que StateDefinition devienne un conteneur pour le flux et extrait seulement assez d'informations pour le champ "StateTypes requiredStates = 1 répété". Ensuite, dans l'assemblage de référencement, le reste du flux peut être désérialisé dans les états respectifs.

Quelqu'un a-t-il des recommandations sur la façon d'aborder cette question? Quelques idées sont en train de se formuler, mais rien de concret, et j'adorerais l'apport des autres.

+0

Utilisez-vous protobuf-net? Y a-t-il des problèmes connus avec la génération de code pour étendre les définitions? – Merritt

+0

Oui, j'utilise protobuf-net. Et non, pas que je sache, je vérifierai bien. Ce n'est même pas un problème de génération de code, c'est que je ne peux pas penser au mécanisme de langage à utiliser pour "terminer" la classe dans un assemblage externe. Les classes partielles fonctionneraient bien, mais refuseraient de franchir la limite de l'assemblée. – Quantumplation

+0

Des pensées sur mon édition? Je ne sais toujours pas comment je veux aborder cela. – Quantumplation

Répondre

0

réponse finale:

D'accord, donc, il y a quelques jours je me suis installé sur une solution et je suis juste mettre à jour ce au cas où quelqu'un d'autre va dans le même problème.

L'ensemble du problème provenait du fait que je n'avais pas réalisé que protobuf-net pouvait supporter octet []. Donc, voici ma solution:

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

Je suis l'auteur de protobuf-net. Je n'ai rien ajouté pour traiter le scénario comme directement présenté (autre que le code Extensible), mais je suis ouvert à la suggestion sur ce que vous pensez que devrait faire faire.

Je voudrais également vérifier si "protoc" (le compilateur .proto que j'utilise pour analyser .proto avant la génération de code) me permet de distinguer entre les membres réguliers et étendus.

+0

Hmm, je ne suis pas sûr. Il me semble permettre aux messages qui étendent un message d'être traduits en classes dérivées après que la génération de code puisse fonctionner. Ou peut-être prendre mon approche et l'automatiser, générant des "méthodes d'extension" pour un accès automatisé à l'interface IExtensible. Je ne connais pas très bien votre système (je l'utilise depuis 3 ou 4 jours maintenant), et je ne me sens donc pas assez à l'aise pour suggérer avec certitude une solution. – Quantumplation

+0

Il est dommage qu'il n'y ait pas de "propriétés d'extension" ... l'héritage serait difficile, car il y a une signification séparée de l'héritage qui ne se mélange pas bien (surtout avec un héritage simple). J'ai encore besoin de vérifier si les extensions peuvent être distinguées, ou si elles sont "identiques" à protoc ... Je vais enquêter. –

Questions connexes