I would like to visit a "recursive" std::variant
using lambdas and overload-creating functions (e.g. boost::hana::overload
).
Let's assume I have a variant type called my_variant
which can store one an int
, a float
or a vector<my_variant>
:
struct my_variant_wrapper;
using my_variant =
std::variant<int, float, std::vector<my_variant_wrapper>>;
struct my_variant_wrapper
{
my_variant _v;
};
(I'm using a wrapper my_variant_wrapper
class in order to define the variant type recursively.)
I want to recursively visit the variant printing different things depending on the stored types. Here's a working example using a struct
-based visitor:
struct struct_visitor
{
void operator()(int x) const { std::cout << x << "i\n"; }
void operator()(float x) const { std::cout << x << "f\n"; }
void operator()(const std::vector<my_variant_wrapper>& x) const
{
for(const auto& y : x) std::visit(*this, y._v);
}
};
Calling std::visit
with the above visitor prints the desired output:
my_variant v{
std::vector<my_variant_wrapper>{
my_variant_wrapper{45},
std::vector<my_variant_wrapper>{
my_variant_wrapper{1}, my_variant_wrapper{2}
},
my_variant_wrapper{33.f}
}
};
std::visit(struct_visitor{}, v);
// Prints:
/*
45i
1i
2i
33f
*/
I would like to locally create the visitor as a series of overloaded lambdas using boost::hana::overload
and boost::hana::fix
.
fix
is an implementation of the Y-combinator, which can be used to implement recursion in type-deduced lambdas. (See this question for more information.)
This is what I tried, and expected to work:
namespace bh = boost::hana;
auto lambda_visitor = bh::fix([](auto self, const auto& x)
{
bh::overload(
[](int y){ std::cout << y << "i\n"; },
[](float y){ std::cout << y << "f\n"; },
[&self](const std::vector<my_variant_wrapper>& y)
{
for(const auto& z : y) std::visit(self, z._v);
})(x);
});
My reasoning is as follows:
boost::hana::fix
returns an unary generic lambda that can be used as the visitor for anstd::variant
.boost::hana::fix
takes a binary generic lambda where the first parameter is an unary function that allows recursion of the lambda, and the second parameter is the initial argument for the body of the lambda.Calling
boost::hana::overload
with handlers for all the possible types insidemy_variant
creates some sort of visitor which is equivalent tostruct_visitor
.Using
self
instead oflambda_visitor
inside theconst std::vector<my_variant_wrapper>&
overload should allow recursion to work properly.Immediately calling the created overload with
bh::overload(...)(x)
should trigger the recursive visitation.
Unfortunately, as you can see in this wandbox example, the lambda_visitor
example fails to compile, spewing out a lot of almost-undecipherable template-heavy errors:
...
/usr/local/boost-1.61.0/include/boost/hana/functional/fix.hpp:74:50: error: use of 'main():: [with auto:2 = boost::hana::fix_t >; auto:3 = int]' before deduction of 'auto' { return f(fix(f), static_cast(x)...); }
...
The error seems similar to what I would get without using boost::hana::fix
:
auto lambda_visitor = bh::overload(
[](int y){ std::cout << y << "i\n"; },
[](float y){ std::cout << y << "f\n"; },
[](const std::vector<my_variant_wrapper>& y)
{
for(const auto& z : y) std::visit(lambda_visitor, z._v);
});
std::visit(lambda_visitor, v);
error: use of 'lambda_visitor' before deduction of 'auto' for(const auto& z : y) std::visit(lambda_visitor, z._v);
What am I doing wrong? Is it possible to achieve local recursive variant visitation using fix
, overload
and a set of lambdas?
My intuition was that lambda_visitor
would have been "equivalent" to struct_visitor
, thanks to the indirection offered by fix
.
boost::hana::fix
(GCC 6.1 and Clang HEAD work fine), so I think the problem here is utterly unrelated tostd::variant
. It would be interesting to try Clang HEAD with libstdc++ HEAD, but I don't know how to do that on wandbox even though all the bits are obviously present... – Levan