Мы можем очень легко связать воедино функцию и аргументы для последующего вызова:
auto bind_args = [](auto&& func, auto&&... args) {
return [=]() mutable {
return func(args...);
};
};
И мы можем стереть этот тип, чтобы потом его можно было легко выполнить позже:
template<class Ret = void>
struct FuncInterface {
virtual ~VirtualFunc() = default;
virtual Ret operator()() = 0;
// Provided to allow concrete classes to copy themselves
virtual FuncInterface* clone() const = 0;
};
template<class Ret, class Func>
struct ConcreteFunc : public FuncInterface<Ret> {
Func func;
Ret operator()() override {
func();
}
FuncInterface* clone() const override {
return new ConcreteFunc<Ret, Func>{func};
}
};
Давайте добавим вспомогательную функцию для создания конкретных функций, используя bind_args
:
auto makeConcrete = [](auto&& func, auto&&... args) {
using Func = decltype(bind(func, args...));
using Ret = decltype(const_cast<Func&>(bind(func, args...))());
return ConcreteFunc<Ret, Func>{bind(func, args...)};
}
Мы можем написать класс-оболочку, который автоматически обрабатывает владение:
template<class Ret>
struct AnyFunc {
std::unique_ptr<FuncInterface> func;
AnyFunc() = default;
AnyFunc(AnyFunc const& f) : func(f.func->clone()) {}
AnyFunc(AnyFunc&& f) = default;
explicit AnyFunc(FuncInterface* func) : func(func) {}
Ret operator()() {
return (*func)();
};
};
И затем мы можем написатьWorkerContainer:
struct WorkerContainer {
std::vector<AnyFunc> funcs;
template<class Worker, class... Args>
void addWorker(Worker&& worker, Args&&... args) {
auto func = [=](auto&&... args) {
worker.execute(args...);
};
FuncInterface* ptr = new auto(makeConcrete(func, args...));
func.emplace_back(ptr);
}
};
Если у вас есть значения по умолчанию для аргументов, вы можете переписать это так, чтобы они выглядели так:
template<class... DefaultArgs>
struct WorkerContainer {
std::vector<AnyFunc> funcs;
std::tuple<DefaultArgs...> storage;
template<class Worker, class... Args>
void addWorker(Worker&& worker) {
auto func = [=, This=this]() {
worker.execute(std::get<Args>(This->storage)...);
};
FuncInterface* ptr = new auto(makeConcrete(func));
func.emplace_back(ptr);
}
};