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

问题描述 投票:0回答:1

我正在尝试从可变参数模板创建参数并将其转发到存储函数。如果参数是(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没有实现(并且很可能无法做到),但这是我正在尝试创建的系统。

编辑:更多解释

这就是我的系统看起来的样子:

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

c++ metaprogramming c++17 variadic-templates template-meta-programming
1个回答
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); 
    }
}; 
© www.soinside.com 2019 - 2024. All rights reserved.