c++overloadingtemplate-meta-programmingparameter-packnon-type-template-parameter

How to interleave C++ template parameter packs


I have this c++ template function

template <int N, int B, auto... Args>
void somefunc() {
    // do work...
}

, which I can call like this:

somefunc<1, true, 2, false>();

I want to expand the API so that clients can call somefunc like this as well:

somefunc<Pack<1, true>, Pack<2, false>>(); // equivalent to somefunc<1, true, 2, false>()

, where Pack is some struct:

template <int N, bool B>
struct Pack
{
    static constexpr int value = N;
    static constexpr int flag = B;
};

A straightforward approach would be to try something like this:

template <typename... Packs>
void somefunc() {
    somefunc<Packs::value..., Packs::flag...>();
}

But this expands to:

somefunc<1, 2, true, false>();

instead of to:

somefunc<1, true, 2, false>();

Is there a simple way to expand the API in the way described?


Solution

  • To flatten Packs, you might use std::tuple:

    template <typename... Packs>
    void somefunc() {
        constexpr std::tuple t = std::tuple_cat(std::tuple{Packs::value, Packs::flag}...);
        return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
            return somefunc<std::get<Is>(t)...>();
        }(std::make_index_sequence<std::tuple_size_v<decltype(t)>>());
    }
    

    Demo

    or with std::apply which requires to wrap constants into std::integral_constant as regular function parameters are not constexpr.

    template <typename... Packs>
    void somefunc() {
        constexpr std::tuple t =
            std::tuple_cat(std::tuple{std::integral_constant<int, Packs::value>{},
                                      std::integral_constant<int, Packs::flag>{}}...);
        return std::apply([](auto... args){ somefunc<args()...>(); }, t);
    }
    

    Demo