


While discussing [upcoming C++17 features](https://en.wikipedia.org/wiki/C%2B%2B17) with other attendees at [CppCon 2016](http://cppcon.org), I was surprised to hear complaints about the fact that [`std::variant`](http://en.cppreference.com/w/cpp/utility/variant) visitation requires an external callable object.

Even though [`std::visit`](http://en.cppreference.com/w/cpp/utility/variant/visit) requires an *overloaded callable object* as its first argument, it is possible to build such an object locally in the call site: this can easily be achieved by implementing something similar to `std::overload`, proposed in [P0051R2](https://github.com/viboes/tags/blob/master/doc/proposals/overload/P0051R2.md).

The aforementioned task however becomes **trivial** when using [`boost::hana`](http://www.boost.org/doc/libs/1_62_0/libs/hana/doc/html/index.html).

In this article, we're gonna take a look at:

* *"Traditional"* variant visitation.

* *"Lambda-based"* variant visitation using `boost::hana`.

* *"Fire-and-forget"* variant visitation.

* Future improvements/considerations.



### `boost::variant` or `std::variant`?

Before looking at visitation techniques, I just wanted to mention that everything written in this article applies both to [`boost::variant`](http://www.boost.org/doc/libs/1_62_0/doc/html/variant.html) and [`std::variant`](http://en.cppreference.com/w/cpp/utility/variant).

In fact, I've written a [very simple wrapper](https://github.com/SuperV1234/vittorioromeo.com/blob/master/extra/visiting_variants/variant_aliases.hpp) for the upcoming examples that conditionally aliases `vr::variant<Ts...>` to `std::variant<Ts...>` if available, otherwise to `boost::variant<Ts...>`.

The `vr::visit(xs...)` function is similarly an alias for `std::visit(xs...)` if available, otherwise for `boost::apply_visitor(xs...)`.

All the code snippets in this article are therefore **compliant to the C++14 standard**.



### *"Traditional"* visitation

Visiting a variant is usually done by writing a *visitor* `struct`/`class` outside of the scope where the variant is actually being visited. The visitor contains a *set of overloads* that will match the types a variant can hold.

As an example, let's say that we have a variant type alias `vnum` that can hold one of several numerical types:

```cpp
// Can hold either an `int`, a `float` or a `double`.
using vnum = vr::variant<int, float, double>;
```

We want to to visit this variant and print...

* `"$i"` for integers.

* `"$f"` for floats.

* `"$d"` for doubles.

...where `$` is the currently stored value.

To achieve that, it's sufficient to write a `vnum_printer` struct with an `operator()` overload for every type supported by our variant:

```cpp
struct vnum_printer
{
    void operator()(int x)    { cout << x << "i\n"; }
    void operator()(float x)  { cout << x << "f\n"; }
    void operator()(double x) { cout << x << "d\n"; }
};
```

Afterwards, we can simply invoke `vr::visit` using an instance of `vnum_printer` as the first argument, and an instance of the `vnum` variant as the second one:

```cpp
// Prints "0i".
vnum v0{0};
vr::visit(vnum_printer{}, v0);

// Prints "5f".
v0 = 5.f;
vr::visit(vnum_printer{}, v0);

// Prints "33.51d".
v0 = 33.51;
vr::visit(vnum_printer{}, v0);
```

This works, but requires us to define a new `vnum_printer` visitor type - this boilerplate can be avoided.

*(You can find a similar example [on GitHub](https://github.com/SuperV1234/vittorioromeo.com/blob/master/extra/visiting_variants/0_traditional_visitation.cpp).)*



### How does `vr::visit` work?

You might be asking...

> How does `vr::visit` work?

The idea is very simple: by providing an overloaded callable object to `vr::visit`, it can internally do something like this:

```cpp
// Warning: PSEUDOCODE!
// The code below is just an example for `vnum`.

using vnum = vr::variant<int, float, double>;

template <typename TVisitor>
auto visit(TVisitor visitor, vnum variant)
{
    // Everything in here would be generated by the compiler...

    if constexpr(variant.index() == 0)
    {
        return visitor(std::get<int>(variant));
    }
    else if constexpr(variant.index() == 1)
    {
        return visitor(std::get<float>(variant));
    }
    else if constexpr(variant.index() == 2)
    {
        return visitor(std::get<double>(variant));
    }
    else
    {
        static_assert(false, "Invalid variant state.");
    }
}
```

As you can see from the *pseudocode* above, all we need is a way of *generating a set of overloads* in order to build a valid visitor.



### *"Lambda-based"* visitation

So, our task is to create an *overload set* from a variadic number of lambdas.

> How can we do that?

We need a variadic `make_overload(...)` template function that takes **any number of callable objects as input** and returns **a single callable object as its output**.

The returned callable object will simply be an overload of all the input callable objects. Example:

```cpp
auto x = make_overload
(
    [](int y){ cout << "int!\n"; },
    [](float y){ cout << "float!\n"; }
);

// Prints "int!".
x(0);

// Prints "float!".
x(0.f);
```

Covering the implementation of `make_overload` is out of the scope of this article *(but an interesting idea for a future one)*. Therefore, you have a few options:

1. Implement your own `make_overload(...)` function using online resources *(e.g. the [`std::overload` proposal](https://github.com/viboes/tags/blob/master/doc/proposals/overload/P0051R2.md))*.

2. Copy-paste my [`vrm::core::make_overload(...)`](https://github.com/SuperV1234/vrm_core/blob/437a0afb35385250cd75c22babaeeecbfa4dcacc/include/vrm/core/overload/make_overload.hpp) implementation.

3. Use [`boost::hana::overload`](http://www.boost.org/doc/libs/1_61_0/libs/hana/doc/html/overload_8hpp.html), a **well-tested production-ready** solution. This is the approach we're going to use for the article.

Once you get hold of a C++14-compliant compiler and `#include <boost/hana.hpp>`... you're pretty much done!

**An overloaded callable object supporting a variant's types is a valid visitor.** Here's a minimal example:

```cpp
auto my_visitor = boost::hana::overload
(
    [](int){ std::cout << "int!\n"; },
    [](float){ std::cout << "float!\n"; }
);

vr::variant<int, float> my_variant{0};

// Prints "int!".
vr::visit(my_visitor, my_variant);

// Prints "float!".
my_variant = 5.f;
vr::visit(my_visitor, my_variant);
```

*(You can find a similar example [on GitHub](https://github.com/SuperV1234/vittorioromeo.com/blob/master/extra/visiting_variants/2_lambda_visitation.cpp).)*



### *"Fire-and-forget"* visitation

Sometimes you might want to visit a single variant with a *"fire-and-forget"* anonymous overload set of lambdas. That's very easy to implement, as well. We'll create a function that takes a variant as its first argument, then any number of callable objects:

```cpp
template <typename TVariant, typename... TVisitors>
auto visit_in_place(TVariant&& variant, TVisitors&&... visitors)
{
    return vr::visit
    (
        boost::hana::overload(std::forward<TVisitors>(visitors)...),
        std::forward<TVariant>(variant)
    );
}
```

*(Note: [`std::forward`](http://en.cppreference.com/w/cpp/utility/forward) needs to be used in order to [perfectly-forward `variant` and `visitors`, which are "forwarding references"](http://thbecker.net/articles/rvalue_references/section_07.html)).*

`visit_in_place` can be used as follows:

```cpp
struct response_success
{
    payload _p;
    message _m;
};

struct response_failure
{
    message _m;
};

// ...

using http_response = vr::variant<response_success, response_failure>;
http_response send_http_request(/* ... */);

// ...

visit_in_place
(
    send_http_request("get_users", some_endpoint),
    [](const response_success& x)
    {
        cout << "Successfully received response:\n"
             << "\tMessage: " << x._m << "\n";

        update_user_list(x._p.as<std::vector<User>>());
    },
    [](const response_failure& x)
    {
        cerr << "Request failure:\n"
             << "\tError: " << x._m << "\n";
    }
);
```

As you can see from the example, `visit_in_place` is useful for very specific variant visits, where the logic is not reused and all boilerplate can be easily avoided.



### Final thoughts, future improvements

*"Lambda-based"* visitation is a very elegant way of visiting variants with minimal boilerplate. *"Fire-and-forget"* visitation is even better when dealing with one-time specific variant visits.

Both of them, however, do not play nicely when a *stateful visitor* is required. In that case, writing a `struct`/`class` is probably the cleanest option. Making the visitor a *local type* or hiding it behind an implementation `namespace` is a good idea to keep it as local as possible.

Throughout the article we also did not cover **recursive variant visitation**: that will be the topic for my next article. It is possible to build a *"lambda-based"* and *"fire-and-forget"* visitation function for recursive variants as well, avoiding any extra overhead *(e.g. `std::function` is not going to be used)*.

Something else that could be interesting to explore is **linear overloading**, provided by [`boost::hana::overload_linearly`](http://www.boost.org/doc/libs/1_62_0/libs/hana/doc/html/group__group-functional.html#gaa46de6f618d9f14edb1589b36b6e75ec). This kind of overloading calls **the first matching function** in linear order - using this alongside *SFINAE* and *generic lambdas* could allow the creation of a visitor that uses a single function for a group of types sharing a common interface, or of a visitor that has a *"default"* case at the end *(useful when you want to ignore some types)*. That will also be covered in a future article.

Thanks for reading!



### series

* [*"visiting variants using lambdas - part 1"*](https://vittorioromeo.com/index/blog/variants_lambdas_part_1.html)

* [*"visiting variants using lambdas - part 2"*](https://vittorioromeo.com/index/blog/variants_lambdas_part_2.html)

* [*"visiting variants using lambdas - part 3"*](https://vittorioromeo.com/index/blog/variants_lambdas_part_3.html)
