1

Is it possible to store the variadic template arguments / parameter pack passed to a constructor of a non class template as a attribute of that class without turning thatclass into a class template?

I am currently developing a thin wrapper class (I've only created a minimal example here to minimize complexity) that has the following signature:

class Wrapper final {
 public:
  template <typename Function, typename... Args>
  auto start(Function&& function, Args&&... args) -> void;
};

The parameter pack is passed to the member function template start<Function, ... Args> and there is currently no need to "store" either function or args. Perfect forwarding is used for the further handling within that function.

Now, what I would like to achieve is a signature as follows (introducing an Interface Class):

class WrapperInterface {
 public:
  virtual ~WrapperInterface() = default;

  virtual auto start() -> void = 0;
};

// TODO(2019-03-17 by wolters) The following is non-working pseudo-code.
class Wrapper final : public WrapperInterface {
 public:
  template <typename Function, typename... Args>
  explicit Wrapper(Function&& function, Args&&... args)
      : function_{function}, args_{args} {
    // NOOP
  }

  auto start() -> void {
    // TODO(2019-03-17 by wolters) Invoke `function_` with `args_`.
    function_(args);
  }

 private:
  std::function<???> function_;
  std::tuple<???> args_;
};

Then Wrapper could be used as follows:

class WrapperClient final {
 public:
  WrapperClient() : wrapper_{[this](){
    // std::cout << "started\n";
  }} {
    // NOOP
  }

 private:
  Wrapper wrapper_;
};

Though the Interface Class is not required in the example above, it is required in general because the instances should be stored in a std::vector<std::unique_ptr<WrapperInterface>>.

I've read and tried How to store variadic template arguments?, but that approach requires turning Wrapper into a class template.

I think something similiar to the QThread *QThread::create(Function &&f, Args &&... args) implementation is required. Sadly that code is too advanced for me.

Can you guide me into the correct direction? Is it possible by using a private implementation class template?

Florian Wolters
  • 3,820
  • 5
  • 35
  • 55

1 Answers1

3

What you are trying to do is called type-erasure which is a very interesting technique (example and a shameless self-promotion here), but it's already done for you in std::function, so all you have to do is use std::function<void()> and use std::bind or lambda capture to store the arguments:

template <typename Function, typename... Args>
std::function<void()> wrap(Function&& function, Args&&... args)
{
    return [=] { function(args); };
    // or return std::bind(std::forward<Function>(function), std::forward<Args>(args)...);
}
r3mus n0x
  • 5,954
  • 1
  • 13
  • 34