Передать функцию с переменным шаблоном и ее аргументы в функцию - PullRequest
0 голосов
/ 24 февраля 2019

Я хотел бы иметь возможность передать функцию, принимающую любое количество аргументов плюс совпадающие аргументы, в качестве аргументов функции.Это то, что я пробовал до сих пор, я заблудился, так как не совсем понимаю, как работают пакеты параметров.

template <class T, typename... Args>
static void BindAction(const std::string& name, T* owner, const std::function<void(T*, Args...)>& func, Args... args)
{
    for (InputAction& action : m_actions)
        if (action.m_name == name)
            action.BindFunction(std::bind(func, owner, args...));
}

И я проверяю это с помощью:

void Test(float f, std::string s, int i);

BindAction<Simulation>("Test", this, &Simulation::Test, 5.f, "a string", 2);

Ошибка просто в том, что ни один экземпляр шаблона функции "BindAction()" не соответствует списку аргументов.

Пожалуйста, не останавливайтесь на достигнутом, сказав мне вызвать std::bind(), а затем передатьрезультирующая функция в качестве аргумента.Я действительно хочу, чтобы звонок был таким.И если это невозможно, слишком плохо, но только тогда я проверю другие решения.

Ответы [ 2 ]

0 голосов
/ 24 февраля 2019

В вашем коде есть некоторые проблемы.

В произвольном порядке

(1) BindAction() получает std::function<void(T*, Args...)> const &, для некоторых типов T e Args...быть выведен, но вы передаете ему &Simulation::Test, поэтому указатель на (static?) метод класса / структуры, который не является std::function.

Вы передаете указателькоторый может быть преобразован в std::function, но не является std::function.

Этакая проблема с яйцом и курицей: компилятор не может преобразовать указатель в std::function, потому что не знаеттипы Args... и не могут выводить типы Args..., потому что не получает std::function

(2), вам нужны два различных вариационных списка типов аргументов: список Args1... дляАргумент std::function и второй список Args2... для полученных аргументов (args...).

См. пример вашего вызова

void Test(float f, std::string s, int i); 

BindAction<Simulation>("Test", this, &Simulation::Test, 5.f, "a string", 2);

Метод Test() получает a float, std::string и int;но BindAction() получает float, char const [9] и int.

Если вы используете один список Args..., компилятор не может вывести типы, поскольку имеет два разных спискатипы.

(3) не уверен, но ... ваш BindAction() является методом static, но использует (если я правильно понимаю) член объекта (m_action)

(4) ваш метод BindAction() является методом void, но возвращает функцию [исправлено]

Предложения:

(a) получает вызываемый какСам шаблонный тип, без определения типов аргументов

(b) получить список аргументов в качестве универсальных ссылок и использовать пересылку шаблонов

Чтобы показать упрощенный пример, что-то следующее

#include <functional>
#include <iostream>

template <typename F, typename ... As>
static auto BindAction (F const & func, As && ... args)
 { return std::bind(func, std::forward<As>(args)...); }

void Test(float f, std::string s, int i)
 { std::cout << "f[" << f << "], s[" << s << "], i[" << i << "]" << std::endl; } 

int main ()
 { 
   auto ba = BindAction(Test, 5.f, "a string", 2);

   std::cout << "post BindAction(), pre ba()\n";

   ba();
 }
0 голосов
/ 24 февраля 2019

Вы связываете все аргументы, поэтому ваша карта будет заполнена std::function<void()>, она не будет соответствовать входящей функции.Вы можете соединить лямбды с std::invoke для более предсказуемого результата и просто использовать шаблон для определения типа входящего вызова:

#include <functional>
#include <iostream>

class Actions {
 public:
  template <typename F, typename... Args>
  void BindAction(const std::string& name, F f, Args... args) {
    actions_.emplace(
      name,
      [f, args...] () mutable { return std::invoke(f, args...); }
    );
  }

  void call(const std::string& name) {
    actions_[name]();
  }

 private:
  std::unordered_map<std::string, std::function<void()>> actions_;
};

И простой пример использования:

class Cls {
  public:
    void do_func(int i) {
      std::cout << "Cls::do_func(" << i << ")\n";
    }
};

int main() {
  Cls c;
  Actions actions;
  actions.BindAction("test", &Cls::do_func, c, 1);
  actions.call("test");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...