Создание аргументов и пересылка из шаблона типа variadic - PullRequest
0 голосов
/ 12 апреля 2019

Я пытаюсь создать аргументы из шаблона переменной и переслать его в хранимую функцию.Если аргументы (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, где вы можете создать Worker с простой структурой, содержащейвыполнить функцию.Затем типы параметров будут использоваться для получения ссылки на экземпляр каждого типа, который хранится в контейнере.А затем отправьте его в функцию execute.Идея была взята из этого Game Engine talk

1 Ответ

0 голосов
/ 13 апреля 2019

Мы можем очень легко связать воедино функцию и аргументы для последующего вызова:

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