How to iterate over the types of std::variant?

You want compile time integers from 0 to variant's size minus 1, and possibly early exit from iterating over them.

There are lots of ways to get compile time integers. Two of my favorites are generating a tuple of integral constants, or calling a continuation with a parameter pack of integral constants.

Taking the tuple of integral constants version, you can use a "tuple for each" to visit each in turn.

template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;
template<std::size_t I>
constexpr index_t<I> index{};

template<std::size_t...Is>
constexpr std::tuple< index_t<Is>... > make_indexes(std::index_sequence<Is...>){
  return std::make_tuple(index<Is>...);
}
template<std::size_t N>
constexpr auto indexing_tuple = make_indexes(std::make_index_sequence<N>{});

from variant size you call that. From that you call a tuple_foreach.

The tuple_foreach emplaces the optional return value if parsing succeeds and it wasn't already parsed.

V parse(const json& j)
{
  auto indexes = indexing_tuple<tuple_size_v<V>>;
  std::optional<V> retval;
  tuple_foreach(indexes, [&](auto I){ // I is compile time integer
    if(retval) return;
    auto p = j.get<tuple_alternative_t<I>>();
    if(p) retval.emplace(std::move(*p));
  });
  if(!retval) throw ParseError("Can't parse");
  return std::move(*retval);
}

tuple_foreach can be found on the internet, but for completeness:

template<std::size_t...Is, class F, class T>
auto tuple_foreach( std::index_sequence<Is...>, F&& f, T&& tup ) {
  ( f( std::get<Is>( std::forward<T>(tup) ) ), ... );
}
template<class F, class T>
auto tuple_foreach( F&& f, T&& tup ) {
  auto indexes = std::make_index_sequence< std::tuple_size_v< std::decay_t<T> > >{};
  return tuple_foreach( indexes, std::forward<F>(f), std::forward<T>(tup) );
}

which should do it in c++17.


Types can be processed recursively from 0 to std::variant_size_v (exclusive), with an if-constexpr limiting template instantiations:

#include <variant>
#include <optional>
#include <cstddef>
#include <utility>

using V = std::variant<A, B, C>;

template <std::size_t I = 0>
V parse(const json& j)
{
    if constexpr (I < std::variant_size_v<V>)
    {
        auto result = j.get<std::optional<std::variant_alternative_t<I, V>>>();

        return result ? std::move(*result) : parse<I + 1>(j);
    }
    throw ParseError("Can't parse");
}

DEMO