Search code examples
c++asynchronouslambdavariadic-templates

Expand variadic arguments in initializer list fails


I try to create a simple program in which I want to create vector of future arguments. I created a wrapper function which is used to submit lambda functions and stores internally in a vector the future objects

I use an intermediate step in which I create an initiliazer_list using variadic arguments . But fails to compile. I try to use to call a function in order to push the elements in the vector and fails to compile as well

Below is the code

#include <iostream>
#include <thread>
#include <future>
#include <functional>
#include <cstdlib>
#include <chrono>
#include <initializer_list>
using namespace std;
using FunctPtr  = function<int(int, int) >;
using FutureInt = future<int>;
using AsyncVector = vector<FutureInt>;
AsyncVector asyncVec;

template<typename... TemplatePtr>
void submit(TemplatePtr... pFunc)
{
  auto initList {pFunc... };
  for (auto & element : initList)
  {
      asyncVec.emplace_back(async(launch::async, element,4,5));
  }
}


int main()
{
    int a;
    int b;
    auto addPtr = [](int x, int y)->int 
    {
        std::cout << "add :" << x + y << std::endl;
        return x + y; 
    };
    auto multPtr = [](int x, int y)->int
    {
        std::cout << "mult :" << x * y << std::endl;
        return x * y;
    };
   // submit(add,4,5);
    submit(addPtr, multPtr);
    for (auto & v : asyncVec)
    {
        std::cout << "Wait for " << v.get() << std::endl;
    }
}

Solution

  • Yes, they are of different types so cannot be in the same init-list easily.

    Your best options should probably be:

    1. Either push them all into asyncVec in the same fold-expression.
    template<typename... TemplatePtr>
    void submit(TemplatePtr... pFunc)
    {
      (asyncVec.emplace_back(async(launch::async, std::move(pFunc), 4, 5)), ...);
    }
    
    1. Or, if they all are of the same signature, type-erase them, like keeping them in an array of std::function.
    template<typename... TemplatePtr>
    void submit(TemplatePtr... pFunc)
    {
      for (auto &&element: {std::function<int(int, int)>(std::move(pFunc))...})
      {
          asyncVec.emplace_back(async(launch::async, std::move(element), 4, 5));
      }
    }
    

    (I have specified function signature explicitly though compiler should be able to deduce it.)

    1. Or, if all closures are captureless and of the same signature, simply cast them to the same type when calling submit:
    using SameType = int (*)(int, int);
    submit(static_cast<SameType>(addPtr), static_cast<SameType>(mulPtr));
    

    This way your original submit should work as is.