Использование универсального аргумента для вызова метода класса представлено в Листинг 50.

Листинг 50. Использование универсального аргумента для вызова метода класса

struct CallbackHandler

{

  void handler1(int eventID) {};

  bool handler2(int eventID, int contextID) { return false; };

};

int main()

{

  CallbackHandler callbackObject;

  UniArgument argument1;       // (1)

  UniArgument argument2;  // (2)

  argument1 = &CallbackHandler::handler1;  // (3)

  argument2 = &CallbackHandler::handler2;  // (4)

  argument1(&callbackObject, 100);         // (5)

  argument2(&callbackObject, 0, 1);        // (6)

}

В строках 1 и 2 объявлены универсальные аргументы для вызова соответствующих методов класса. Как видим, в сигнатуре функции первый параметр является типом класса, для которого будут вызываться соответствующие методы. В строках 3 и 4 производится настройка методов, в строках 5 и 6 – вызовы методов для экземпляра соответствующего класса.

Итак, универсальный аргумент практически готов. Нам осталось реализовать оператор копирования, оператор присваивания и некоторые другие операции. Но мы этим заниматься не будем: разработчики стандартной библиотеки уже обо всем позаботились, поэтому темой следующей главы будет обзор инструментов STL для организации обратных вызовов24.

<p>4.6. Использование стандартной библиотеки</p><p>4.6.1. Организация вызовов</p>

В стандартной библиотеке имеется полиморфный класс – оболочка std::function, предназначенная для организации вызовов различных типов. Этот класс идеально подходит на роль универсального аргумента. Кроме рассмотренных техник стирания типа и настройки сигнатуры, в нем реализовано множество других вещей: конструктор копирования, оператор присваивания, поддержка указателей на методы класса, проверка настройки аргумента, локальный буфер для хранения аргумента и многое другое. Мы не будем рассматривать реализацию std::function, потому что, во-первых, она достаточно сложная, а, во-вторых, может изменяться в зависимости от версии и платформы. При желании читатель сможет сделать это самостоятельно, проанализировав исходный код, мы же сосредоточимся на практическом использовании класса-оболочки.

Насколько сложна реализация 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);

  }

}

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

Для реализации инициатора с универсальным аргументом необходимо для хранения аргумента объявить соответствующую класс-оболочку 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)

};

Перейти на страницу:

Похожие книги