10
votes

Modèle de visiteur pour boost :: variante

Je voudrais utiliser un boost.variant code> en tant que paramètre à un modèle de classe "visiteur" qui fournirait des opérateurs de visiteurs comme requis par le mécanisme de visiteur boost.variant , dans ce cas, tous renvoient le vide IE,

typedef VariantWapper<bool,int,double> VariantWrapperType;
typedef VariantWrapperType::VariantType VariantType;
typedef VariantWrapperType::VisitorType VisitorType;

struct Visitor : public VisitorType
{
    void Process(bool val){/*do something*/}
    void Process(int val){/*do something*/}
    /* this class is not interested in the double value */
};

VariantType data(true);
apply_visitor(Visitor(),data);


0 commentaires

3 Réponses :


16
votes

Le code avec une variante de boost et une expédition virtuelle est un peu de poisson. En tenant compte surtout que vous savez que vous êtes intéressé par le traitement pendant la compilation et il n'est absolument pas nécessaire de créer une table virtuelle au moment de l'exécution afin d'atteindre vos objectifs.

Je vous recommanderais d'utiliser Spécialisation partielle de modèle . Ainsi, ont une méthode de modèle par défaut qui peut accepter tout type dans la variante et ne fera rien. Pour les types qui vous intéressent, il suffit de spécialiser un modèle. P>

Voici un exemple. Nous avons trois types - foo, bar et guerre. Nous ne sommes intéressés que dans les deux derniers types et nous avons une spécialisation pour eux. Donc, FOO est ignoré. P>

#include <cstddef>
#include <iostream>
#include <boost/variant.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/contains.hpp>
#include <boost/utility/enable_if.hpp>

// Generic visitor that does magical dispatching of
// types and delegates passes down to your visitor only
// those types specified in a type list.
template <typename Visitor, typename TypeList>
struct picky_visitor :
    public boost::static_visitor<void>,
    public Visitor
{
    template <typename T>
    inline void
    operator () (T v, typename boost::enable_if< typename boost::mpl::contains< TypeList, T >::type >::type *dummy = NULL) const
    {
        Visitor::operator () (v);
    }

    template <typename T>
    inline void
    operator () (T v, typename boost::disable_if<typename boost::mpl::contains< TypeList, T >::type >::type *dummy = NULL) const
    {
    }
};

// Usage example:

struct nil {};
typedef boost::variant<nil, char, int, double> sql_field;

struct example_visitor
{
    typedef picky_visitor< example_visitor, boost::mpl::vector<char, int> > value_type;

    inline void operator () (char v) const
    {
        std::cout << "character detected" << std::endl;
    }

    inline void operator () (int v) const
    {
        std::cout << "integer detected" << std::endl;
    }
};

int
main ()
{
    example_visitor::value_type visitor;

    sql_field nilField;
    sql_field charField ('X');
    sql_field intField (1986);
    sql_field doubleField (19.86);

    boost::apply_visitor (visitor, nilField);
    boost::apply_visitor (visitor, charField);
    boost::apply_visitor (visitor, intField);
    boost::apply_visitor (visitor, doubleField);
}


8 commentaires

Merci, VLAD, c'est une très bonne suggestion, obtenant ce que je voulais sans l'expédition de temps d'exécution, que je suis d'accord est un peu de poisson. Je serais toujours intéressé de savoir si ce que j'essayais de faire des modèles, c'est-à-dire que, instanciant d'une classe directement avec un autre type de modèle et à l'aide des paramètres de type de ce dernier type dans le type précédent, est possible?


Vous êtes les bienvenus, Tom. C'est un peu difficile de faire votre idée, mais si je comprends bien, c'est d'au moins 90% correctement, cela est réalisable mais, à mon avis, n'est pas si pratique dans les cas généraux. J'ai édité ma réponse et j'ai ajouté une autre solution qui est un peu plus proche de votre idée initiale. Mais au lieu d'utiliser une table virtuelle, le visiteur générique hérite de votre implémentation et non de Vise Versa. Si cela n'est pas acceptable, vous pouvez toujours utiliser des fonctions virtuelles. J'essaie juste de les éviter dans la mesure du possible.


Votre deuxième solution semble intéressante et je vais l'étudier (je me connais toujours avec certaines des techniques de moderne C ++). Je suppose que ma légère préoccupation avec votre première solution suggérée, maintenant j'y ai pensé davantage, c'est qu'il perd la vérification du type de temps de compilée du mécanisme de variante-visiteur, en raison de la fonction de modèle par défaut. En particulier, vous pouvez appliquer un visiteur à n'importe quelle variante et le compilateur ne se plaint jamais. Cela peut être un prix acceptable pour payer la commodité, mais je cherchais une solution qui a toujours fourni une certaine sécurité de type.


@TOM, pour une garantie que les types qui ne sont pas pris en charge ne seront pas ignorés silencieusement avec l'opérateur par défaut, j'ajouterais "Boost :: activer_if" pour désactiver cet opérateur pour des types non pris en charge ou l'activer uniquement pour ceux pris en charge. Les deux solutions sont bonnes, mais la seconde a quelques problèmes de délégation d'un constructeur à la classe de base si elle n'est pas triviale. Dans les deux cas, Boost :: Variante <> n'est pas ce modèle largement utilisé et s'il est rarement étendu avec de nouveaux types. Andrei Alexandrescu couvre très bien ce sujet dans son livre de conception moderne C ++, fortement recommandé.


Merci beaucoup, Vlad, je comprends votre utilisation d'activation_if / dactyliste maintenant. J'ai un opérateur () (t) en utilisant activé_if et il fait ce que je veux, c'est-à-dire la sécurité et une solution simple. Un problème cependant, est que le code ne compile pas (avec VS10) si j'initialise la variante AS- SQL_FIELD Charfield (Charstract ()). Je reçois une erreur 'lhs de .apply_visitor doit être la classe / structure / union.' Il compile OK si vous initiez la variante à l'aide de SQL_Field Charfield = Charfring (). Je suppose que cela pourrait être le problème de la délégation du constructeur à la classe de base? Pourquoi dites-vous que la variante n'est pas si largement utilisée?


@TOM, "Type variable (type ())" - Il s'agit d'une définition de la fonction selon les règles C ++. C'est difficile à expliquer en bref, alors ne l'utilisez pas :-) "Tapez la variable = type ()" est la voie à suivre. Cela fait exactement ce que vous vouliez et n'appelle pas d'opérateur d'affectation, en fait, cela appelle simplement un constructeur.


Bien sûr. Merci, ainsi que de résoudre mon problème d'origine spécifique, j'ai appris plus que prévu et je vais maintenant en apprendre plus moi-même. Cela a été une bonne aide et j'apprécie vraiment votre temps. Meilleures salutations!


Bizarre que cela n'a jamais été avancé. J'aime le choix du nom pour le visiteur "pigmentaire" .



0
votes

Tom, je crois que votre question ait beaucoup de sens dans un contexte particulier. Dites que vous souhaitez stocker les visiteurs de multiples types d'un vecteur, mais vous ne pouvez pas parce qu'ils sont tous des types différents. Vous avez quelques choix: utilisez à nouveau une variante pour stocker les visiteurs, utilisez Boost.any ou utilisez des fonctions virtuelles. Je pense que les fonctions virtuelles sont une solution élégante ici, mais certainement pas la seule.

Voici comment ça se passe. P>

Tout d'abord, utilisons une variante; bool code>, int code> et float code> fera. p> xxx pré>

est ensuite sur la classe de base , plus ou moins comme vous l'aviez. p>

p> xxx pré>

Suivant, nous avons deux variantes spécifiques. P>

p> xxx pré>

Enfin, nous pouvons faire un seul vecteur de variantes différentes: p>

p> xxx pré>

et voici la sortie : P>

I am Visitor1 at T2
I am Visitor2 at T2
I am Visitor1 at T0
I am Visitor at T0


0 commentaires

1
votes

Au fil du temps, les bibliothèques nouvelles et intéressantes se développent. Cette question est ancienne, mais depuis lors, il y a une solution que pour moi personnellement est beaucoup plus supérieure à celle qui a été donnée jusqu'à présent.

L'excellent Mach7 Bibliothèque qui permet des fonctionnalités de correspondance sans précédent (et donc de visite). Il est écrit par Yuriy Solodkyy, Gabriel Dos Reis et Bjarne Stroustrup lui-même. Pour ceux qui sortent sur cette question, voici un exemple de README: P>

void print(const boost::variant<double,float,int>& v)
{
    var<double> d; var<float> f; var<int> n;

    Match(v)
    {
      Case(C<double>(d)) cout << "double " << d << endl; break;
      Case(C<float> (f)) cout << "float  " << f << endl; break;
      Case(C<int>   (n)) cout << "int    " << n << endl; break;
    }
    EndMatch
}


0 commentaires