}
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, в этих случаях необходимо преобразование вызовов. Однако, поскольку в универсальном аргументе сигнатура может настраиваться, в объекты преобразования также нужно ввести поддержку настройки сигнатуры.
4.6.3. Преобразование с настройкой сигнатуры
В п. 4.2.2 реализованы объекты преобразования, которые работали с фиксированной сигнатурой. Используя технику, описанную в Листинг 47 п. 4.5.2, модифицируем их таким образом, чтобы сигнатуру можно было настроить. Для этого в параметрах шаблона вместо задания типов указателей на функцию будем задавать параметры, определяющие сигнатуру, а типы указателей будем выводить из этих параметров.
Рассмотрим вначале указатели на функцию (Листинг 54).
Листинг 54. Преобразование вызовов с настройкой сигнатуры для указателей на функцию
template // (1)
class CallbackConverter;
template // (2)
class CallbackConverter // (3)
{
public:
using Function = Return(*)(Context, ArgumentList…); // (4)
CallbackConverter(Function argFunction = nullptr, Context argContext = nullptr) // (5)
{
ptrFunction = argFunction; context = argContext;
}
Return operator() (ArgumentList… arguments) // (6)
{
ptrFunction(context, arguments…); // (7)
}
private:
Function ptrFunction; // (8)
Context context; // (9)
};
В строке 1 вводится общая специализация шаблона. В строке 2 объявляется специализация для указателей на функцию, в которой задается тип передаваемого контекста и параметры сигнатуры. В строке 4 выводится тип указателя. В конструкторе 5 осуществляется настройка указателей. В перегруженном операторе 6 осуществляется вызов 7, в который передаются соответствующие аргументы.
Аналогично выполняется специализация для вызова методов класса (Листинг 55).
Листинг 55. Преобразование вызовов с настройкой сигнатуры для указателей на метод класса.
template // (1)
class CallbackConverter // (2)
{
public:
using MemberPointer = Return(ClassType::*)(ArgumentList…); // (3)
CallbackConverter(MemberPointer methodPointer = nullptr, ClassType* classPointer = nullptr) // (4)
{
ptrClass = classPointer; ptrMethod = methodPointer;
}
Return operator()(ArgumentList… arguments) // (5)
{
(ptrClass->*ptrMethod)(arguments…); // (6)
}
private:
ClassType* ptrClass; // (7)
MemberPointer ptrMethod; // (8)
};
Реализация практически повторяет предыдущую, за исключением того, что в объявлениях типов сигнатуры добавляется класс (строки 2 и 3), а перегруженный оператор вызывает метод класса (строка 6).
Реализация исполнителя для инициатора с универсальным аргументом (см. Листинг 53 п. 4.6.2) приведена в Листинг 56, здесь используется CallbackConverterиз Листинг 54 п. 4.6.3.
Листинг 56. Исполнитель для инициатора с оболочкой std::function
class Executor
{
public:
static void staticCallbackHandler(Executor* executor, int eventID) {}
void callbackHandler(int eventID) {}
void operator() (int eventID) {}
};
void ExternalHandler(void* somePointer, int eventID) {}
int main()
{
int capturedValue = 0;
Initiator initiator;
Executor executor;
// Pointer to the external function
initiator.setup(CallbackConverter(ExternalHandler, &executor));
// Pointer to the static method
initiator.setup(CallbackConverter(Executor::staticCallbackHandler, &executor));
// Pointer to the class member method
initiator.setup(CallbackConverter(&Executor::callbackHandler, &executor));
// Functional object
initiator.setup(executor);
// Lambda-expression
initiator.setup([capturedValue](int eventID) {});
}
Если сравнить приведенную реализацию исполнителя для шаблона-инициатора с фиксированным типом аргумента (Листинг 43 и Листинг 44 п. 4.4.3) с приведенной, то можно заметить следующее. В первом случае для каждого типа аргумента приходится объявлять отдельный инициатор, инстанциируя его соответствующим типом. Здесь инициатор объявляется один раз, после чего тип аргумента вызова настраивается в процессе выполнения программы. В результате упрощается разработка, улучшается гибкость и прозрачность кода.
Читать дальше