2016-09-23 2 views
0

Je fais face le problème suivant: J'ai quelques visiteurs sur pour boost :: variante, tous font la même chose pour un type spécifique, ici foo, la méthodeévitant le code redondant boost :: visiteurs variantes

void operator()(const foo& ast) 
{ 
    //allways the same 
} 

est toujours la même dans chaque visiteur. Puisque je ne veux pas écrire cette méthode redondante dans tous les visiteurs, j'ai essayé d'éviter cela en ajoutant une classe de base commune, qui implémente cette méthode, à tous les visiteurs. problème la méthode appelle le visiteur lui-même récursivement, comme celui-ci:

void operator(const foo& ast) 
{ 
    for(auto&& item : ast.members) 
    { 
     boost::apply_visitor(*this, item); 
    } 
} 

et que toutes les autres méthodes, qui sont correspondant pour les membres ne coûtent pas mis en œuvre dans la classe de base, je reçois une erreur du compilateur, sur ce point. Maintenant, ma question est, comment puis-je me débarrasser de mon code redondant?

Voici un exemple sur la façon dont le problème pourrait ressembler:

struct variant_one; 
struct variant_two; 
struct nil{}; 
typedef boost::variant< 
    boost::spirit::x3::forward_ast<variant_one>, 
    boost::spirit::x3::forward_ast<variant_two>, 
    nil 
> example_variant; 

struct variant_one {}; 
struct variant_two 
{ 
    std::vector<example_variant> members; 
}; 


struct visitor_one : boost::static_visitor<void> 
{ 
    void operator()(const variant_one& ast) 
    { 
     std::cout << "visitor_one detected var_one" << std::endl; 
    } 

    //this is the redundant method 
    void operator()(const variant_two& ast) 
    { 
     std::cout << "visitor detected var_two, output members:" <<std::endl; 
     for(auto&& member : ast.members) 
     { 
      boost::apply_visitor(*this, member); 
     } 
    } 
} 

struct visitor_two : boost::static_visitor<void> 
{ 

    void operator()(const variant_one& ast) 
    { 
     std::cout << "visitor_one detected var_two" << std::endl; 
    } 

    //this is the redundant method 
    void operator()(const variant_two& ast) 
    { 
     std::cout << "visitor detected var_two, output members:" <<std::endl; 
     for(auto&& member : ast.members) 
     { 
      boost::apply_visitor(*this, member); 
     } 
    } 
} 
+2

s'il vous plaît fournir un [mcve] –

Répondre

2

Quelque chose comme ça?

template<typename Derived> 
struct VisitorBase { 
    void operator()(const foo& ast) { 
     for(auto&& item : ast.members) { 
      boost::apply_visitor(*static_cast<Derived*>(this), item); 
     } 
    } 
}; 

struct VisitorA : VisitorBase<VisitorA> { 
    void operator()(const ItemType& item) { 
     // do stuff 
    } 
}; 

ou si les types utilisés dans les visiteurs sont les mêmes/connues à l'avance et des fonctions virtuelles sont très bien:

struct VisitorBase { 
    void operator()(const foo& ast) { 
     for(auto&& item : ast.members) { 
      boost::apply_visitor(*this, item); 
     } 
    } 
    virtual void operator()(const ItemTypeA&) = 0; 
    virtual void opetator()(const ItemTypeB&) = 0; 
}; 

struct VisitorA : VisitorBase { 
    void operator()(const ItemTypeA& item) { 
     // do stuff 
    } 
    void operator()(const ItemTypeB& item) { 
     // do stuff 
    } 
}; 

Dans le premier exemple, vous voudrez peut-être vous assurer que vous n'instancier pas accidentellement le modèle avec un type non dérivé, par exemple avec ceci:

static_assert(std::is_base_of<VisitorBase,Derived>::value, "Derived should be derived from VisitorBase"); 

ce sera toujours laisser ouverte la possibilité d'instancier un VisitorBase de type dérivée de avec un type dérivé VisitorBase différent dans le paramètre modèle, ce qui conduit à un comportement indéfini. Donc sois prudent.

+0

merci beaucoup, je pense que le premier devrait être bien, je voudrais éviter les méthodes virtuelles – Exagon

+0

hmm pourquoi pas seulement http://paste.ubuntu.com/23219932/ – sehe

+1

@ sehe Comme je ne l'ai pas compris, il existe plusieurs classes de visiteurs partageant le comportement affiché pour le type 'foo' mais variant behvior pour les autres types à' operator() '. – user4407569