Search code examples
c++crtpboost-variantvisitor-pattern

Adding debugging facilities to boost variant visitor


I'm using boost-variant throughout my projects and I'm considering it to be one the most useful and versatile tools of boost.

But if it comes to complicated use of the visitor pattern with recursively nested variants the debugging is sometimes cumbersome.

Hence I decided to implement a reusable DebugVisitor, that might be added to my existing visitors. It should be easily added/removed to my existing visitors in case a defect occurs.

Easily removable means, that it should be addable to any existing Visitor class, instead of modifying the places, where instances of the Visitor are being used.

I tried to find a solution, which fits my requirements. The following code compiles, but unfortunately it does not print the message.

Does anyone knows why?

#include <iostream>
#include <boost/variant.hpp>
#include <functional>

template<typename V> // V must have the boost::static_visitor Interface
struct DebugVisitor : public V {
    template<typename U>
    typename V::result_type operator()(const U& u) const {
        std::cout << "Visiting Type:" << typeid(U).name() << " with Visitor: " << typeid(V).name() << std::endl;
        return V::operator()(u);
    }
};


struct AddVisitor : public DebugVisitor<boost::static_visitor<boost::variant<int, double>>> {
    template<typename U>
    result_type operator()(const U& u) const {
        return u + 1.;
    }
};

int main(int, char**) {
    boost::variant<double, int> number{ 3.2 };

    AddVisitor d;
    auto incr=number.apply_visitor(d);
    if (auto dValue = boost::get<double>(incr)) {
        std::cout << "Increment: " << dValue << std::endl;
    }
    return 0;
}

Solution

  • The reason that you don't see debug output is because AddVisitor::operator() does not call DebugVisitor::operator(). If it did, you would have an error trying to resolve boost::static_visitor<>::operator(), which doesn't exist.

    Option 1.

    Just conditionally compile the debugging in the definition of AddVisitor

    struct AddVisitor : public boost::static_visitor<boost::variant<int, double>> {
        template<typename U>
        result_type operator()(const U& u) const {
    #ifdef DEBUG
            std::cout << "Visiting Type:" << typeid(U).name() << " with Visitor: " << typeid(AddVisitor).name() << std::endl;
    #endif
            return u + 1.;
        }
    };
    

    Option 2.

    Conditionally define the symbol AddVisitor to be wrapped by DebugVisitor

    template<typename V> // V must have the boost::static_visitor Interface
    struct DebugVisitor : public V {
        template<typename U>
        typename V::result_type operator()(const U& u) const {
            std::cout << "Visiting Type:" << typeid(U).name() << " with Visitor: " << typeid(V).name() << std::endl;
            return V::operator()(u);
        }
    };
    
    struct AddVisitorBase : public boost::static_visitor<boost::variant<int, double>> {
        template<typename U>
        result_type operator()(const U& u) const {
            return u + 1.;
        }
    };
    
    #ifdef DEBUG
    using AddVisitor = DebugVisitor<AddVisitorBase>;
    #else
    using AddVisitor = AddVisitorBase;
    #endif
    

    Option 3.

    A pair of CRTP bases

    template<typename V> // V must have the boost::static_visitor Interface
    struct DebugVisitor : public V {
        template<typename U>
        typename V::result_type operator()(const U& u) const {
            std::cout << "Visiting Type:" << typeid(U).name() << " with Visitor: " << typeid(V).name() << std::endl;
            return V::call(u);
        }
    };
    
    template<typename V> // V must have the boost::static_visitor Interface
    struct NotDebugVisitor : public V {
        template<typename U>
        typename V::result_type operator()(const U& u) const {
            return V::call(u);
        }
    };
    
    struct AddVisitor : public boost::static_visitor<boost::variant<int, double>>, public 
    #ifdef DEBUG
    DebugVisitor<AddVisitor>
    #else
    NotDebugVisitor<AddVisitor>
    #endif
    {
        template<typename U>
        result_type call(const U& u) const {
            return u + 1.;
        }
    };