Обратные вызовы в C++ | страница 51



, потому что, во-первых, она достаточно сложная, а, во-вторых, может изменяться в зависимости от версии и платформы. При желании читатель сможет сделать это самостоятельно, проанализировав исходный код, мы же сосредоточимся на практическом использовании класса-оболочки.

Насколько сложна реализация std::function, настолько же просто ее использование. По аналогии с универсальным аргументом, рассмотренном в предыдущей главе, достаточно объявить экземпляр класса с нужной сигнатурой, после чего ему можно назначать различные объекты вызовов (Листинг 51).

Листинг 51. Использование std::function

>void External(int eventID) {};


>int main()

>{

>  struct Call

>  {

>    void operator() (int eventID) {};

>  } objectCall;


>  std::function fnt;


>  fnt = External;

>  fnt = objectCall;

>  fnt = [](int evetID) {};


>  fnt(0);

>}


Полезной особенностью std::function является проверка настройки объекта вызова. Если объект не настроен, т. е. не было ни одного присваивания, то при попытке вызова будет выброшено исключение. Проверить, настроен ли объект, можно с помощью перегруженного оператора bool, пример приведен в Листинг 52.

Листинг 52. Проверка настройки аргумента

>int main()

>{

>  std::function fnt;


>  fnt(0); //Error: argument is not set. Exception will be thrown


>  fnt = [](int) {};

>  fnt(0); //Ok, argument is set


>  //Check if the argument is set

>  if (fnt)

>  {

>    fnt(0);

>  }

>}

4.6.2. Инициатор с универсальным аргументом

Для реализации инициатора с универсальным аргументом необходимо для хранения аргумента объявить соответствующую класс-оболочку std::function (Листинг 53).

Листинг 53. Инициатор с оболочкой std::function

>class Initiator  // (1)

>{

>public:

>  template

>  void setup(const CallbackArgument& argument)  // (2)

>  {

>    callbackHandler = argument;

>  }


>  void run()

>  {

>  int eventID = 0;

>  //Some actions

>  callbackHandler(eventID);

>  }


>private:

>  std::function callbackHandler;  // (3)

>};


Если сравнить реализацию инициатора с фиксированным типом аргумента (Листинг 37 п. 4.4.1) с приведенной, то можно заметить следующие отличия. В первом случае инициатор является шаблоном, здесь он объявляется обычным способом. Далее, хранимый аргумент 3 не является переменной типа, задаваемого параметром шаблона, он объявлен как универсальный аргумент std::function. Метод настройки 2 объявлен как шаблон, параметром которого является тип назначаемого аргумента.

Описанный инициатор не работает с указателями на функцию и на метод класса: в первом случае необходимо передавать контекст, во втором случае необходимо передавать указатель на экземпляр класса и использовать другой синтаксис для вызова. Как уже рассматривалось в п. 4.2.2, в этих случаях необходимо преобразование вызовов. Однако, поскольку в универсальном аргументе сигнатура может настраиваться, в объекты преобразования также нужно ввести поддержку настройки сигнатуры.