2010-12-02 15 views
6

Me gustaría tener una estructura (o algo similar) en C++, que permita el acceso a sus miembros de forma dinámica. Debería tener un getter y setters genéricos que reciban el nombre del miembro como una cadena, y devolver algún tipo de tipo de variante (por ejemplo, boost::variant).Acceder a los miembros en una estructura de C++ de forma dinámica y estática

Estaba pensando que podría implementarse usando boost::fusion::map, agregando una cadena que representa el nombre de cada miembro y construyendo un mapa STL entre cadenas y funciones getter o setter. No quiero reinventar la rueda, así que esperaba que algo similar ya existiera.

¿Qué opinas? ¿Funcionaría mi idea? ¿Conoces otras formas de lograr mi objetivo?

Gracias, Hageo

+2

Me pregunto por qué quieres esto? Incluso en los lenguajes que lo soportan directamente, la reflexión es un truco utilizado para evitar el código incorrecto, o un código barato para programadores perezosos. –

+0

Estás derrotando el tipo de seguridad que te brinda C++. ¿Qué diablos puede justificar el intercambio de una herramienta tan simple y fuerte para la corrección con un truco desordenado para lograr la incertidumbre? – wilhelmtell

+0

@wilhelmtell: Estoy tratando de encontrar el equilibrio correcto. Estoy leyendo valores de una fuente muy incierta, y necesitan ser analizados y manejados de una manera genérica. Solo para una pequeña parte de ellos sé (y quiero saber) el tipo correcto. –

Respuesta

6

fusión es una aproximación, pero por qué no almacenar sus "campos" en un std::map introducido por un std::string, donde la carga útil es la boost::variant ...

es decir

struct generic 
{ 
std::map<std::string, boost::variant<foo, bar, bob, int, double> > _impl; 
}; 

y luego se puede solo busque la clave en su getter/setter ...

heck, envuelva el variant en un optional y podría tener campos opcionales!

un ejemplo más complejo:

class foo 
{ 
public: 
    typedef boost::variant<int, double, float, string> f_t; 
    typedef boost::optional<f_t&> return_value; 
    typedef map<string, return_value> ref_map_t; 

    foo() : f1(int()), f2(double()), f3(float()), f4(string()), f5(int()) 
    { 
    // save the references.. 
    _refs["f1"] = return_value(f1); 
    _refs["f2"] = return_value(f2); 
    _refs["f3"] = return_value(f3); 
    _refs["f4"] = return_value(f4); 
    _refs["f5"] = return_value(f5); 
    } 

    int getf1() const { return boost::get<int>(f1); } 
    double getf2() const { return boost::get<double>(f2); } 
    float getf3() const { return boost::get<float>(f3); } 
    string const& getf4() const { return boost::get<string>(f4); } 
    int getf5() const { return boost::get<int>(f5); } 

    // and setters.. 
    void setf1(int v) { f1 = v; } 
    void setf2(double v) { f2 = v; } 
    void setf3(float v) { f3 = v; } 
    void setf4(std::string const& v) { f4 = v; } 
    void setf5(int v) { f5 = v; } 

    // key based 
    return_value get(string const& key) 
    { 
    ref_map_t::iterator it = _refs.find(key); 
    if (it != _refs.end()) 
     return it->second; 
    return return_value(); 
    } 

    template <typename VT> 
    void set(string const& key, VT const& v) 
    { 
    ref_map_t::iterator it = _refs.find(key); 
    if (it != _refs.end()) 
     *(it->second) = v; 
    } 

private: 
    f_t f1; 
    f_t f2; 
    f_t f3; 
    f_t f4; 
    f_t f5; 

    ref_map_t _refs; 
}; 

int main(void) 
{ 
    foo fancy; 
    fancy.setf1(1); 
    cout << "f1: " << fancy.getf1() << endl; 

    fancy.set("f1", 10); 
    cout << "f1: " << fancy.getf1() << endl; 

    return 0; 
} 
+0

Esto funcionaría, pero prefiero tener un estricto control de tipos y un acceso más rápido en tiempo de ejecución en los casos en que conozco el nombre del campo en el momento de la compilación. –

+0

bien, en ese caso almacene en el mapa referencias a los campos reales (que son de tipo variante - ejemplo anterior) ... – Nim

+0

El problema aquí es que hay 5 lugares donde necesito agregar un nuevo campo. Esto parece una gran repetición para agregar lo que consiste solo de un nombre en tiempo de compilación, una cadena y el tipo. Tal vez podría modificar su solución haciendo que los campos sean públicos (y con su tipo no variante), y almacenar en el mapa algún tipo de envoltorio que convierta cada uno a una variante y desde ella. –

1

Usted está solicitando Reflection en C++ que creo que no está disponible. Tendrás que inventar algo propio.

+0

RTTI está disponible en C++, estás pensando en * reflection * ... :) – Nim

+0

Sí, la reflexión en C++ lo haría mucho más simple. Me conformaré con una solución que requiere un mínimo de nuevas líneas de código para agregar un nuevo campo. –

+0

Sí, tienes razón. Respuesta actualizada – Aamir

1

Lo que hice para esto era un tipo lista impulso :: contras similar que contiene mis miembros y algún tipo de descripción. Luego construyo este mapeo al agregar sucesivamente mis miembros a una estructura de datos de "metainformación" mediante llamadas a funciones "encadenadas". Todo parece muy similar a la definición de una clase en boost.python. Si realmente usa boost :: cons, también debería funcionar como una secuencia en boost.fusion, por lo que puede iterar muy bien sobre sus datos. Tal vez pueda usar un mapa boost.fusion para obtener los tiempos de acceso de registro (n) en tiempo de ejecución, pero parece que su tamaño es limitado hasta que estén disponibles las plantillas variadic.

+0

Creo que tanto boost :: fusion :: map como boost :: contra tienen O (1) acceso en tiempo de ejecución. –

Cuestiones relacionadas