2010-12-02 5 views
6

Je voudrais avoir une structure (ou quelque chose de similaire) en C++, qui permettra l'accès à ses membres de façon dynamique. Il doit avoir un getter générique et des setters qui reçoivent le nom du membre sous forme de chaîne, et renvoyer un type de type variant (par exemple, boost::variant). Je pensais qu'il pourrait être implémenté en utilisant boost::fusion::map, en ajoutant une chaîne représentant le nom de chaque membre, et en construisant une carte STL entre les chaînes et les fonctions getter ou setter. Je ne veux pas réinventer la roue, alors j'espérais que quelque chose de similaire existait déjà.Accéder aux membres dans une structure C++ à la fois dynamiquement et statiquement

Qu'en pensez-vous? Mon idée fonctionnerait-elle? Connaissez-vous d'autres façons d'atteindre mon objectif?

Merci, Aggée

+2

Je me demande pourquoi vous voulez ça? Même dans les langages qui le supportent directement, la réflexion est un hack utilisé pour contourner le mauvais code, ou du code bon marché pour les programmeurs paresseux. –

+0

Vous vaincre le type de sécurité que vous offre C++. Qu'est-ce qui peut justifier l'échange d'un outil si simple et si solide avec un hack désordonné pour atteindre l'incertitude? – wilhelmtell

+0

@wilhelmtell: J'essaie de trouver le bon équilibre. Je lis des valeurs provenant d'une source très incertaine, et elles doivent être analysées et traitées de manière générique. Seulement pour une petite partie d'entre eux je sais (et je veux savoir) le bon type. –

Répondre

6
fusion

est une approche, mais pourquoi ne pas stocker vos « champs » dans un std::map calée par un std::string, où la charge utile est le boost::variant ...

-à-dire

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

et vous pouvez il suffit de rechercher la clé dans votre getter/setter ...

heck, envelopper le variant dans un optional et vous pourriez avoir des champs optionnels!

un exemple plus complexe:

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

Cela fonctionnerait, mais je préfère avoir une vérification de type stricte, et un accès à l'exécution plus rapide dans les cas où je connais le nom du champ au moment de la compilation. –

+0

ok, dans ce cas, stocker dans la carte des références aux champs réels (qui sont de type variante - exemple ci-dessus) ... – Nim

+0

Le problème ici est qu'il ya 5 endroits où j'ai besoin d'ajouter un nouveau champ. Cela ressemble beaucoup à ce qui consiste à ajouter uniquement un nom à la compilation, une chaîne et le type. Peut-être que je pourrais modifier votre solution en ayant les champs publics (et avec leur type non-variant), et en stockant dans la carte une sorte de wrapper qui converti chacun vers et à partir d'une variante. –

1

Vous demandez Reflection en C++ que je pense n'est pas disponible. Vous devrez trouver quelque chose de votre choix.

+0

RTTI est disponible en C++, vous pensez à * reflexion * ... :) – Nim

+0

Oui, la réflexion en C++ le rendrait beaucoup plus simple. Je vais me contenter d'une solution qui nécessite un minimum de nouvelles lignes de code pour ajouter un nouveau champ. –

+0

Oui, vous avez raison. Réponse mise à jour – Aamir

1

Ce que je l'ai fait pour cela était un type liste boost :: comme le contre qui contient mes membres et une sorte de description. Je construis ensuite cette cartographie en ajoutant successivement mes membres à une structure de données «méta-info» par des appels de fonction «chaînés». Le tout ressemble beaucoup à la définition d'une classe dans boost.python. Si vous utilisez en fait boost :: cons, cela devrait aussi fonctionner comme une séquence dans boost.fusion, donc vous pouvez bien parcourir vos données. Peut-être que vous pouvez utiliser une carte boost.fusion à la place pour obtenir des temps d'accès au journal (n) au moment de l'exécution, mais il semble que leur taille soit limitée jusqu'à ce que des modèles variés soient disponibles.

+0

Je pense que boost :: fusion :: map et boost :: contre ont un accès à l'exécution O (1). –

Questions connexes