Search code examples
c++templatesc++17metaprogrammingvariadic-templates

How to iterate over the size of a parameter pack with a compile-time index parameter


I'm trying to write a variadic template function that includes a loop that iterates over each type in the parameter pack. I'm using this to build up a tuple which I then apply on the callback function which determined the template types.

I thought I could do this using sizeof...(Args) as follows:

template <typename ...Args>
void addCallback(void* handle, std::string address, std::function<void(Args...)> callback)
{

    return addMessageCallback(std::move(address), [callback](const ci::osc::Message& message) {
        std::tuple<Args...> args;
        for (size_t i = 0; i < sizeof...(Args); i++)
        {
            std::get<i>(args) = message.getArg<std::tuple_element_t<i, decltype(args)>(i);
        }

        std::apply(callback, std::move(args));
        });
}

This doesn't compile because std::get<i>(args) requires i to be a compile time constant. However, all the information is available at compile time. Is there a way to make this work? I've thought about using a fold expression but

I'm using C++17.


Solution

  • You can define a helper function to use index_sequence to expand the elements of Tuple and assign values through fold expression.

    template<class Tuple, class Message, std::size_t... Is>
    void assign_tuple(Tuple& tuple, const Message& message, std::index_sequence<Is...>) {
      ((std::get<Is>(tuple) = message.getArg<std::tuple_element_t<Is, Tuple>(Is)), ...);
    };
    
    template <typename ...Args>
    auto addCallback(void* handle, std::string address, std::function<void(Args...)> callback) {
      return addMessageCallback(std::move(address), [callback](const ci::osc::Message& message) {
          std::tuple<Args...> args;
          assign_tuple(args, message, std::make_index_sequence<sizeof...(Args)>{});
          std::apply(callback, std::move(args));
      });
    }
    

    In C++20 you can just define template lambda inside the function to do this.