I have the following implementation of an AST, built using C++17's std::variant type, on which I would like to apply a visitor recursively. I have done this with the help of some utilities from Boost's Hana library.
#include <iostream>
#include <memory>
#include <variant>
#include <boost/hana.hpp>
namespace hana = boost::hana;
struct AddExpr;
struct SubExpr;
struct MulExpr;
struct DivExpr;
using Expr = std::variant<int, AddExpr, SubExpr, MulExpr, DivExpr>;
struct BinaryExpr
{
BinaryExpr(std::unique_ptr<Expr> lhs, std::unique_ptr<Expr> rhs) noexcept
: lhs{ std::move(lhs) }, rhs{ std::move(rhs) } { }
std::unique_ptr<Expr> lhs;
std::unique_ptr<Expr> rhs;
};
struct AddExpr : BinaryExpr { using BinaryExpr::BinaryExpr; };
struct SubExpr : BinaryExpr { using BinaryExpr::BinaryExpr; };
struct MulExpr : BinaryExpr { using BinaryExpr::BinaryExpr; };
struct DivExpr : BinaryExpr { using BinaryExpr::BinaryExpr; };
int main()
{
Expr root {
MulExpr{
std::make_unique<Expr>(AddExpr{
std::make_unique<Expr>(1),
std::make_unique<Expr>(2),
}),
std::make_unique<Expr>(SubExpr{
std::make_unique<Expr>(3),
std::make_unique<Expr>(4),
}),
}
};
constexpr auto printer = hana::fix([](auto visitor, auto const& arg) -> void {
hana::overload(
[&](int val) {
std::cout << val;
},
[&](AddExpr const& exp) {
std::cout << "(";
std::visit(visitor, *exp.lhs);
std::cout << "+";
std::visit(visitor, *exp.rhs);
std::cout << ")";
},
[&](SubExpr const& exp) {
std::cout << "(";
std::visit(visitor, *exp.lhs);
std::cout << "-";
std::visit(visitor, *exp.rhs);
std::cout << ")";
},
[&](MulExpr const& exp) {
std::cout << "(";
std::visit(visitor, *exp.lhs);
std::cout << "*";
std::visit(visitor, *exp.rhs);
std::cout << ")";
},
[&](DivExpr const& exp) {
std::cout << "(";
std::visit(visitor, *exp.lhs);
std::cout << "/";
std::visit(visitor, *exp.rhs);
std::cout << ")";
}
)(arg);
});
std::visit(printer, root);
std::cout << "\n";
return 0;
}
The code compiles without any warnings when using GCC 7.1+, but Clang does not seem to be able to compile it, issuing the following error instead.
<source>:54:22: error: function 'visit<boost::hana::fix_t<(lambda at <source>:47:40)> &, std::variant<int, AddExpr, SubExpr, MulExpr, DivExpr> &>' with deduced return type cannot be used before it is defined
std::visit(visitor, *exp.lhs);
...
Is this a bug in Clang? Or am I doing something wrong, and, if so, why does this work for GCC?
Also, is there an elegant way to do what I want in Clang? Particularly, I would prefer to be able to create the printer visitor from some overloaded lambdas, rather than having to create a PrinterVisitor class with operator() overloaded for each type. However, if that's not possible, I am open to other suggestions.