visitor template for boost::variant

13,225

As time passes new and interesting libraries develop. This question is old, but since then there is a solution that to me personally is far more superior to the ones that have been given so far.

The excellent Mach7 library which allows unprecedented matching (and therefore visiting) capabilities. It is written by Yuriy Solodkyy, Gabriel Dos Reis and Bjarne Stroustrup himself. For the ones stumbling on this question, here is an example taken from the README:

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
}

I am working with it now and so far it is a real pleasure to use.

Share:
13,225
Admin
Author by

Admin

Updated on July 18, 2022

Comments

  • Admin
    Admin almost 2 years

    I would like to use a boost.variant<T0,T1,T2> as a parameter to a template 'Visitor' class which would provide visitor operators as required by the boost.variant visitor mechanism, in this case all returning void i.e.,

    void operator()(T0 value);
    void operator()(T1 value);
    void operator()(T2 value);
    

    The template would also have for each of the types T0... in the variant a corresponding virtual function which by default does nothing. The user is able inherit from the template class and redefine only those virtual functions which he is interested in. This is something akin to the well-known 'Template Method' pattern. The only solution I have been able to come up with is by wrapping both the boost::variant and the associated visitor in a single template, and accessing them via typedefs. This works okay, however it feels a little clunky. Here's the code:

    #include "boost/variant.hpp"
    
    //create specializations of VariantWrapper for different numbers of variants - 
    //just show a template for a variant with three types here. 
    //variadic template parameter list would be even better! 
    
    template<typename T0, typename T1, typename T2>
    struct VariantWrapper
    {
        //the type for the variant
        typedef boost::variant<T0,T1,T2> VariantType;
    
        //The visitor class for this variant
        struct Visitor : public boost::static_visitor<>
        {
            void operator()(T0 value)
            {
                Process(value);
            }
            void operator()(T1 value)
            {
                Process(value);
            }
            void operator()(T2 value)
            {
                Process(value);
            }
            virtual void Process(T0 val){/*do nothing */}
            virtual void Process(T1 val){/*do nothing */}
            virtual void Process(T2 val){/*do nothing */}
        protected:
            Visitor(){}
        };
    
        typedef Visitor VisitorType;
    private:
        VariantWrapper(){}
        };
    

    The class is then used as follows:

    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);
    

    As I say, this seems to work okay but I would prefer it if I didn't have to create a special wrapper class to tie the variant and the visitor together. I would prefer to be able just to use a boost.variant directly to instantiate the template visitor class. I've had a look at using type parameters, non-type parameters and template template parameters but nothing seems to suggest itself. Is what I am trying to do not possible? I may be missing something, and would appreciate it if anyone has any input on this.