Search code examples
c++c++20template-meta-programmingvariantstd-variant

Get a compile-time index to a std::variant when visiting it?


I have a std::variant of possibly repeated types, and I would like to increment the active index of the variant.

Here is an example demonstrating what I would like to do:

template <typename... Ts>
bool update_variant(std::variant<Ts>& v, std::tuple<work_generator<Ts>>& gen) {
    return visit_with_index([&]<size_t Index /* the active index */>(auto& e) {
        if (e.done()) {
            // we are done with e
            constexpr size_t next_index = Index + 1;
            if constexpr (next_index == sizeof...(Ts)) {
                // no more other work to do
                return false;
            } else {
                // still have other work to do so we move on to the next variant alternative
                v.emplace(
                        std::in_place_index_t<next_index>{},
                        std::get<next_index>(gen).create_worker()
                    );
                return true;
            }
        } else {
            // do some work on e, so e gets closer to the done state
            e.do_some_work();
            return true;
        }
    }, v);
}

To implement this, I seem to need something akin to visit_with_index above, which gives me the current index as a compile-time value.

Other than writing my own version of visit_with_index above, which is effectively writing my own version of std::visit, are there any simpler ways to achieve what I want without performing more than one lookup by index?


Solution

  • I would use visit on the index "directly".

    First

    template <typename... Ts>
    auto get_index_variant(const std::variant<Ts...>& v)
    {
        return [&]<std::size_t...Is>(std::index_sequence<Is...>){
            using Ret = std::variant<std::integral_constant<std::size_t, Is>...>;
            std::array<Ret, sizeof...(Is)> a = {{Ret{std::integral_constant<std::size_t, Is>{}}...}};
            return a[v.index()];
        }(std::make_index_sequence<sizeof...(Ts)>());
    }
    

    Then

    std::visit([&](auto index){ // index() is constexpr
        // ...
        }, get_index_variant(v));
    

    Demo