Обратные вызовы в C++ | страница 51
Насколько сложна реализация std::function, настолько же просто ее использование. По аналогии с универсальным аргументом, рассмотренном в предыдущей главе, достаточно объявить экземпляр класса с нужной сигнатурой, после чего ему можно назначать различные объекты вызовов (Листинг 51).
>void External(int eventID) {};
>int main()
>{
> struct Call
> {
> void operator() (int eventID) {};
> } objectCall;
> std::function
> fnt = External;
> fnt = objectCall;
> fnt = [](int evetID) {};
> fnt(0);
>}
Полезной особенностью std::function является проверка настройки объекта вызова. Если объект не настроен, т. е. не было ни одного присваивания, то при попытке вызова будет выброшено исключение. Проверить, настроен ли объект, можно с помощью перегруженного оператора bool, пример приведен в Листинг 52.
>int main()
>{
> std::function
> 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).
>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
>};
Если сравнить реализацию инициатора с фиксированным типом аргумента (Листинг 37 п. 4.4.1) с приведенной, то можно заметить следующие отличия. В первом случае инициатор является шаблоном, здесь он объявляется обычным способом. Далее, хранимый аргумент 3 не является переменной типа, задаваемого параметром шаблона, он объявлен как универсальный аргумент std::function. Метод настройки 2 объявлен как шаблон, параметром которого является тип назначаемого аргумента.
Описанный инициатор не работает с указателями на функцию и на метод класса: в первом случае необходимо передавать контекст, во втором случае необходимо передавать указатель на экземпляр класса и использовать другой синтаксис для вызова. Как уже рассматривалось в п. 4.2.2, в этих случаях необходимо преобразование вызовов. Однако, поскольку в универсальном аргументе сигнатура может настраиваться, в объекты преобразования также нужно ввести поддержку настройки сигнатуры.