Объект C ++ сохраняет шаблонную функцию и аргументы как члены для последующего вызова - PullRequest
0 голосов
/ 24 мая 2018

У меня есть класс Door, который реализует метод LockCheck(), и класс Stove с методом BurnerCheck().Я хочу класс House, который принимает в качестве аргумента конструктора либо Door::LockCheck, либо Stove::BurnerCheck вместе с неизвестным набором аргументов для данной функции.House будет хранить функцию и ее аргументы так, чтобы она могла вызывать их позднее.Например,

auto stove = Stove();
auto stove_check = stove.BurnerCheck;
auto burner_args = std::make_tuple<bool, bool>(true, false);
auto house = House(burner_args, stove_check);
// do some other stuff...
house.check_safety();  // internally calls stove.BurnerCheck(burner_args)

Как должен выглядеть класс House

Пока у меня есть

template <typename ReturnType, typename... Args>
class House {

public:

    House(Args... args, std::function<ReturnType(Args...)> func)
        : input_args_(std::forward_as_tuple(args...)),
          safety_func_(func) {}
    };

private:

    Args... input_args_;  // Is this the correct declaration?
    std::function<ReturnType(Args...)> safety_func_;
};

Примечания:

  • C ++ 11

  • Я уже видел похожие вопросы, такие как this и this .

Ответы [ 2 ]

0 голосов
/ 24 мая 2018

Некоторые предварительные соображения.

1) Если вы пишете шаблон класса House

template <typename ReturnType, typename... Args>
class House {
  // ...

    House(Args... args, std::function<ReturnType(Args...)> func)
        : input_args_(std::forward_as_tuple(args...)),
          safety_func_(func) {}

, где аргумент методов проверки "неизвестен" (и, я полагаю,отличается от типа к типу) вы должны знать, что аргументы при определении объектов House и у вас есть разные типы House (один тип House для проверок Door, один тип House для Stove проверки и т. д.) и (до C ++ 17) вы не можете объявить House объект просто как

auto house = House(burner_args, stove_check);

, но вам необходимо явно указать типы шаблонов;что-то вроде

House<void, bool, bool> house{burner_args, stove_check};

Предложение: вас не интересует ReturnType методов проверки (и если вы можете его игнорировать), сделайте House класс не-шаблона и сделайте конструктор шаблона с переменным числом элементовдля этого;что-то вроде

class House
 {
   public:
      template <typename ... Args, typename F>
      House (Args ... as, F f) 

2) Если у вас есть шаблонная функция / метод / конструктор с некоторыми фиксированными аргументами и вариационным списком аргументов, поместите вариабельный список аргументов в last позициюТаким образом, компилятор может выводить список переменных типов из аргументов, и нет необходимости явно его использовать.

Таким образом, предыдущий конструктор становится чем-то вроде

  template <typename F, typename ... Args>
  House (F f, Args ... as) 

3) Насколько я знаю, нет способа передать указатель на фактический метод функции o в переменную;поэтому нет

auto stove_check = stove.BurnerCheck;

и stove_check в качестве аргумента конструктора House.

Обычный способ, который я знаю для такого рода проблем, - передать объект (stove)и указатель на метод BurnerCheck, относящийся к классу, а не к объекту;что-то вроде

auto house { House(stove, &Stove::BurnerCheck, /* variadic args */) };

Теперь конструктор становится

  template <typename T, typename M, typename ... Args>
  House (T t, M m, Args ... as) 

, и вы можете назвать BurnerCheck() метод stove как

  (t.*m)(as...)

Теперь мой предложенныйHouse class: класс с std::function<void(void)> элементом, который инициализируется в конструкторе House с лямбда-выражением, которое захватывает объект, метод указателя и аргументы.

И check_safety() метод, который просто вызываетэтот член.

Что-то следующее

class House
 {
   private:
      std::function<void(void)> fn;

   public:
      template <typename T, typename M, typename ... Args>
      House (T t, M m, Args ... as) : fn{[=]{ (t.*m)(as...); }}
       { }

      void check_safety ()
       { fn(); }
 };

Ниже приведен полный рабочий пример

#include <iostream>
#include <functional>

struct Door
 { void LockCheck (int, long) const { std::cout << "Door" << std::endl; } };

struct Stove
 { void BurnerCheck (char) const { std::cout << "Stove" << std::endl; } };

class House
 {
   private:
      std::function<void(void)> fn;

   public:
      template <typename T, typename M, typename ... Args>
      House (T t, M m, Args ... as) : fn{[=]{ (t.*m)(as...); }}
       { }

      void check_safety ()
       { fn(); }
 };

int main ()
 {
   auto stove { Stove{} };
   auto door { Door{} };
   auto house1 { House{stove, &Stove::BurnerCheck, 'a'} };
   auto house2 { House{door, &Door::LockCheck, 1, 2L} };

   std::cout << "Some other stuff" << std::endl;

   house1.check_safety();
   house2.check_safety();
 }

Если вас интересует значение, возвращаемое из проверенного метода... Полагаю, вы можете сделать House классом шаблона только с параметром ReturnType и соответственно настроить класс.

0 голосов
/ 24 мая 2018

Вы можете хранить все аргументы в виде кортежа.Затем вы можете вызвать safety_func_, распаковав кортеж в аргументы функции.Распаковка может быть осуществлена ​​непосредственно в C ++ 17 с использованием std :: apply .

template <typename ReturnType, typename... Args>
class House {
public:

    House(Args... args, std::function<ReturnType(Args...)> func)
        : input_args_(std::forward<Args>(args)...),
          safety_func_(func) {}

    // Require C++17
    ReturnType call_safety() {
        return std::apply(safety_func_, input_args_);
    }
private:

    std::tuple<Args...> input_args_;
    std::function<ReturnType(Args...)> safety_func_;
};

Для получения чистого решения по распаковке кортежей C ++ 11 см. этот пост

...