2010-07-16 13 views
9

¿Cómo 'de-serializa' una clase derivada de datos serializados? O tal vez debería decir, ¿hay una mejor manera de "deserializar" los datos en clases derivadas?¿Cómo 'de-serializar' una clase derivada de datos serializados?

Por ejemplo, supongamos que tiene una clase base virtual pura (B) heredada por otras tres clases, X, Y y Z. Además, tenemos un método, serialize(), que traducirá X: B, Y: B y Z: B en datos serializados.

De esta manera se puede pasar a través de un socket, un tubo con nombre, etc. a un proceso remoto.

El problema que tengo es, ¿cómo creamos un objeto apropiado a partir de los datos serializados?

La única solución que se me ocurre es incluir un identificador en los datos serializados que indica el tipo de objeto derivado final. Donde el receptor, primero analiza el campo de tipo derivado de los datos serializados, y luego usa una declaración de conmutación (o algún tipo de lógica como esa) para invocar el constructor apropiado.

Por ejemplo:

B deserialize(serial_data) 
{ 
    parse the derived type from the serial_data 

    switch (derived type) 
     case X 
      return X(serial_data) 
     case Y 
      return Y(serial_data) 
     case Z 
      return Z(serial_data) 
} 

Así que después de conocer el tipo de objeto derivado que invocar el constructor de tipo derivada adecuada.

Sin embargo, esto se siente incómodo y engorroso. Espero que haya una manera más elocuente de hacer esto. ¿Esta ahí?

+5

serialización compacto (bit a bit) puede morder a la derecha en el culo, sobre todo si va a guardar el material en archivos. A medida que pasan los años y las clases cambian, estás bastante jodido cuando tratas de cargarlos de nuevo. Por esa razón, hacer que los archivos sean auto-documentados y versionados es una buena idea. Solo si el cliente y el servidor acuerdan en todo momento el protocolo utilizado, ¿está bien enviar bytes directos? Si no, entonces recurriría a 'XML/JSON'. Sin embargo, también buscaría cosas que pueden hacer esto más fácil, como 'SOAP', etc. –

+0

@Hamish +1, ¡haz que una respuesta! –

Respuesta

2

De hecho, es un problema más general que la serialización llamada Virtual Constructor.

El enfoque tradicional es un Factory, que en función de un ID devuelve el tipo derivado correcto. Hay dos soluciones:

  • el método switch Como habrá notado, sin embargo es necesario asignar en el montón
  • el método prototype

El método prototipo va así:

// Cloneability 
class Base 
{ 
public: 
    virtual Base* clone() const = 0; 
}; 

class Derived: public Base 
{ 
public: 
    virtual Derived* clone() const { return new Derived(*this); } 
}; 

// Factory 
class Factory 
{ 
public: 
    Base* get(std::string const& id) const; 
    void set(std::string const& id, Base* exemplar); 

private: 
    typedef std::map < std::string, Base* > exemplars_type; 
    exemplars_type mExemplars; 
}; 

Es algo tradicional hacer que el Factory sea singleton, pero es completamente diferente.

Para la deserialización adecuada, es más fácil si tiene un método virtual deserialize para llamar al objeto.

EDITAR: ¿Cómo funciona la fábrica?

En C++ no puede crear un tipo que no conozca.La idea anterior es, por lo tanto, que la tarea de construir un objeto Derived se da a la clase Derived, mediante el método clone.

Luego viene el Factory. Vamos a usar un map que asociará una "etiqueta" (por ejemplo, "Derived") a una instancia de un objeto (digamos Derived aquí).

Factory factory; 
Derived derived; 
factory.set("Derived", &derived); 

Ahora, cuando queremos crear un objeto que el tipo no sabemos en tiempo de compilación (porque el tipo se decide sobre la marcha), se pasa una etiqueta a la fábrica y pedir un objeto en regreso.

std::unique_ptr<Base> base = factory.get("Derived"); 

Bajo la cubierta, la Factory se encuentra el Base* asociado a la etiqueta "Derived" e invocar el método del objeto clone. Esto realmente (aquí) creará un objeto de tipo de tiempo de ejecución Derived.

Podemos comprobar esto mediante el operador typeid:

assert(typeid(base) == typeid(Derived)); 
+0

Disculpe mi ignorancia. Pero no entiendo cómo una fábrica resuelve el problema. De hecho, me caí del camión en la clase de fábrica de arriba. –

+0

No hay problema, todos estamos aquí para aprender, he agregado una explicación sobre cómo la fábrica resuelve el problema de la construcción virtual. No te confundas con la estructura 'map', es solo un elegante interruptor que se puede completar en tiempo de ejecución. –

+0

Gracias por la información adicional. Entonces si entiendo esto correctamente Todavía necesitamos algún tipo de etiqueta para decirnos qué tipo de objeto es. La fábrica usa esta etiqueta para encontrar la función "registrada" que devuelve un nuevo clon. Corrígeme si me equivoco, pero esto parece una declaración de cambio "dinámico". En lugar de tener un interruptor codificado duro como el que acabo de mencionar, ¿la fábrica le ofrece una forma "flexible" de proporcionar la misma funcionalidad? O me estoy perdiendo algo. ¡POR CIERTO, GRACIAS! ¡Ya adapté esta solución para otro problema que estaba teniendo! –

0
 
inmemory: 
-------- 
type1 { 
    chartype a; 
    inttype b; 
}; 
serialize(new type1()); 

serialized(ignore { and ,): 
--------------------------- 
type1id,len{chartypeid,adata,inttypeid,bdata} 

supongo, en un protocolo de serialización ideales, cada tipo no primitivo necesita ser prefijado con typeid, len. Incluso si serializas un tipo único que no se deriva de nada, agregarías un ID de tipo, porque el otro extremo debe saber qué tipo obtiene (independientemente de la estructura de herencia). Por lo tanto, debe mencionar identificadores de clase derivados en la serialización, ya que lógicamente son tipos diferentes. Corrígeme si estoy equivocado.

+0

Esta es una especie de dirección hacia donde me dirigía, pero no creo que se necesite longitud. Por supuesto, dependerá de cómo se defina el tipeo. Estaba visualizándolo como identificando el tipo de clase, que luego inferiría la duración también. Pero sospecho que mi pensamiento también se basa en el hecho de que estoy usando POSIX Message Queues para la transmisión, por lo que la capa de transporte conoce la longitud. –

Cuestiones relacionadas