can members of an object defined as a parameter be accessed in a generic function?

2

Good morning

I am creating a method member of a class, that allows me to receive an object and save the information of the object in a file.

MyClase::guardarDatos(Objeto a);

Specifically, I need to save three objects that are derived from a parent class. because I do not want to overload the method three times, and decided to create a function, so that it receives an object through the use of template.

template<class T> 
MyClase::guardarDatos(T &a);

inside this function I get the name of the incoming class:

MyClase::guardarDatos(T &a){
const type_info &tip = typeid(a);};

with this I realize a series of validations to obtain the respective data of each object:

if (tip.name() == "Objeto") { Datos1 d1= a.getDatos(); }else if(tip.name() == "Objeto1"){ Datos2 d2=a.getDatos(); }

The problem is when I try to access a member function of a EJ: a.getDatos() , the compiler throws me the following message:

  

C2440 error: 'initializing': can not convert from

I suppose that in the compilation the type T will be interpreted as my Objeto , for that reason I should avoid this type of errors,

I appreciate your help and guidance.

    
asked by alvaroc 28.09.2016 в 21:34
source

2 answers

1

Problem.

  

C2440 error: 'initializing': can not convert from

You have not put the complete error nor indicated on which line the error leaps, so we can only make conjectures. A quick error search reveals that you are using MSVC to compile and you could have one of the following errors :

  • Initialize a char* or% co_of non-constant% using a string literal.
  • Try converting a pointer to member to pointer to wchar_t* .
  • Try to fully use a class declared but not defined.
  • Incorrectly using a user-defined conversion.
  • Other specific incorrect uses of MSVC.
  • Without knowing more details, I bet your failure is 3 or 4, because in the code:

    if (tip.name() == "Objeto") {
        Datos1 d1= a.getDatos();
    }else if(tip.name() == "Objeto1"){
        Datos2 d2=a.getDatos();
    }
    

    You are using the types void and Datos1 , if you do not define both types or if you do not define how the return of Datos2 can be converted to both types then you will have this problem that you describe.

    But it is impossible to know for sure without seeing more code. With the code that you have published, you can see that you have an approximation to the serialization that is not considered a good practice in some areas. In addition to relying on type introspection using the implementation dependent operator a.getDatos() your implementation skips the principles of encapsulation .

    Type introspection or Run-time type information (RTTI in English) has significant runtime costs, and evaluating an expression with typeid can also incur runtime costs .

    Exposing externally to your class's data can be considered a design antipattron if it is done without necessity. I assume that after serializing the data you will want to deserialize them so if you expose methods to read internal data you will also need methods to write them.

    Your question.

      

    Is it possible to access the members of an object defined as a parameter in a generic function?

    It is possible, as long as the members are visible and public or the function is a friend of the object; the type of function does not change this fact:

    struct S { int miembro; };
    class  C { int miembro; };
    
    void guardarDatos(const S &s)
    {
        std::cout << "vamos a guardar S: " << s.miembro << '\n';
        ...
    }
    
    void guardarDatos(const C &c)
    {
        // Error, C::miembro es privado
        std::cout << "vamos a guardar C: " << c.miembro << '\n';
        ...
    }
    
    template <typename T>
    void guardarDatos_plantilla(const T &t)
    {
        std::cout << "vamos a guardar T: " << t.miembro << '\n';
        ...
    }
    
    S s; C c;
    guardarDatos_plantilla(s);
    guardarDatos_plantilla(c); // Error, C::miembro es privado
    
    class C2
    {
        int miembro;
        template <typename T> 
        friend void guardarDatos_plantilla(const T&);
        friend void guardarDatos(const C2 &);
    };
    

    In the previous example, the functions typeid or guardarDatos_plantilla (overload with guardarDatos ) will not have problems accessing C2 despite being private since they are friendly functions of C2::miembro .

    Proposal.

    Delegate the serialization and deserialization routines to the objects instead of creating an external class for these purposes.

    My approach would be that each object had the ability to serialize itself, either by writing directly to the file or by returning a binary version of its data and status. In parallel, each object should have the ability to deserialize itself, either reading directly from a file or reading binary data:

    class SerializableEnArchivo
    {
        int miembro; 
        void serializar(std::ofstream archivo)
        {
            archivo << miembro;
        }
    
        void deserializar(std::ifstream archivo)
        {
            archivo >> miembro;
        }
    };
    
    using byte_buffer = std::vector<std::uint8_t>;
    
    class SerializableEnBufer
    {
        int miembro; 
        byte_buffer serializar()
        {
            byte_buffer datos{sizeof(miembro)};
            // serializar
            return datos;
        }
    
        void deserializar(const byte_buffer &datos)
        {
            // deserializar
        }
    };
    

    You can create an interface based on these ideas and inherit from the interface, but you should think if that is convenient for you:

    struct Serializable
    {
        using byte_buffer = std::vector<std::uint8_t>;
    
        virtual byte_buffer serializar() = 0;
        virtual void deserializar(const byte_buffer &) = 0;
    };
    
    class Clase : public Serializable { ... };
    
        
    answered by 29.09.2016 / 09:38
    source
    1

    The design you are doing is a bit poor in that its implementation involves a great coupling within the template ... when it is assumed that the templates are used to create specific code. Solutions?

    If the set of operations to be performed with Datos1 , Datos2 , ... are exactly the same you can generalize the function. To get the cleanest code possible, it is advisable to centralize the configuration outside the template:

    // Template base.
    // Para tipos no especializados no compilará.
    template<class T>
    struct Configurador;
    
    template<>
    struct Configurador<Objeto1>
    {
      typedef Datos1 TipoRetorno;
    };
    
    template<>
    struct Configurador<Objeto2>
    {
      typedef Datos2 TipoRetorno;
    };
    

    This mechanism is very simple to scale and leaves the template cleaner:

    template<class Objeto>
    class MyClase
    {
      // Cargamos el tipo esperado en nuestro objeto
      // typename hay que usarlo porque el tipo viene dado por un template.
      typedef typename Configurador<Objeto>::TipoRetorno TipoRetorno;
    
      void GuardarDatos(Objeto& obj)
      {
        TipoRetorno d = obj.getDatos();
        // ...
      }
    };
    

    Of course, from C ++ 11 we can simplify this structure using auto . Since using auto delegates to the compiler the responsibility of assigning the correct type, the code will compile correctly.

    template<class Objeto>
    class MyClase
    {
      void GuardarDatos(Objeto& obj)
      {
        auto d = obj.getDatos();
        // ...
      }
    };
    

    On the other hand, if the logic to be executed depends on the type of object, the option may be to externalize said logic:

    template<class T>
    void GuardarObjeto(T obj);
    
    template<>
    void GuardarObjeto<Objeto1>(Objeto1& obj)
    {
      Datos1 d = obj.getDatos();
      // ...
    }
    
    template<>
    void GuardarObjeto<Objeto2>(Objeto2& obj)
    {
      Datos2 d = obj.getDatos();
      // ...
    }
    
    template<class Objeto>
    class MyClase
    {
      void GuardarDatos(Objeto& obj)
      {
        GuardarObjeto(obj);
      }
    };
    

    Greetings.

        
    answered by 29.09.2016 в 08:58