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;
}
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.;
}
};