创建参数并从可变参数类型模板转发

时间:2019-04-12 20:51:51

标签: c++ metaprogramming c++17 variadic-templates template-meta-programming

我正在尝试从可变参数模板中创建参数并将其转发到存储的函数。如果参数为(typename... Args),则我想迭代每种类型,并从存储容器中获取该类型的参数,然后将参数转发给函数。

我尝试了不同的方法,但总会导致无法存储无类型的参数向量,也无法将向量作为单独的参数转发。

这是我要完成的伪代码。

template <typename S, typename... Args>
void store_lambda() {
    vec.push_back([this]() -> void {
        ArgumentList arguments;
        (get_arguments<Args>(arguments), ...);
        my_function(arguments...);
    });
}

template <typename T>
void get_arguments(ArgumentList& arguments) {
    arguments.append(inner_storage.get<T>();)
}

void my_function(SomeStruct& s, const AnotherStruct& as) {
    // do something with arguments
}

类型ArgumentList未实现(可能无法执行),但这是我要创建的系统。

编辑:更多说明

这是我的系统atm的外观:

struct WorkerWrapperBase {
   public:
    virtual ~WorkerWrapperBase() {}
}

template <typename... Args>
using exec_fn = std::function<void()>;
template <typename Job>
using job_ptr = std::unique_ptr<Job>;

template <typename J, typename R = void, typename... Args>
struct WorkerWrapper {
    exec_fn<Args...> fn;
    job_ptr<J> job_ptr;
    WorkerWrapper(J* job, R (S::*f)(Args...) const)
        : job_ptr{job_ptr<J>(job)} {
        fn = [this, f]() -> R {
            (job_ptr.get()->*f)(/* send arguments fetched from Args as explained above */);
        };
    }
};

struct CollectionOfWorkers {
    std::vector<WorkerWrapperBase> workers;
    template <typename Worker>
    void add() {
        workers.push_back(WorkerWrapper(new Worker(), &Worker::execute));
    }
}

用法如下:

struct TestWorker {
    void execute(SomeStruct& s, const AnotherStruct& as) const {
        // do something with arguments
    }
}

CollectionOfWorkers.add<TestWorker>();

// and then somewhere we can loop each Worker and call their execute function

我想创建一个干净的API,您可以在其中使用包含执行功能的简单结构创建一个Worker。然后,将使用参数的类型来尝试获取存储在容器中的每种类型的实例的引用。然后将其发送到execute函数。这个想法来自Game Engine talk

1 个答案:

答案 0 :(得分:0)

我们可以将函数和参数绑定在一起,以便以后轻松调用:

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); 
    }
};