Выбрать главу

4.6.3. Преобразование с настройкой сигнатуры

В п. 4.2.2 реализованы объекты преобразования, которые работали с фиксированной сигнатурой. Используя технику, описанную в Листинг 47 п. 4.5.2, модифицируем их таким образом, чтобы сигнатуру можно было настроить. Для этого в параметрах шаблона вместо задания типов указателей на функцию будем задавать параметры, определяющие сигнатуру, а типы указателей будем выводить из этих параметров.

Рассмотрим вначале указатели на функцию (Листинг 54).

Листинг 54. Преобразование вызовов с настройкой сигнатуры для указателей на функцию

template<typename unused>  // (1)

class CallbackConverter;

template<typename Context, typename Return, typename … ArgumentList>  // (2)

class CallbackConverter<Return(Context, ArgumentList…)>               // (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<typename ClassType, typename Return, typename…ArgumentList>  // (1)

class CallbackConverter<Return(ClassType::*)(ArgumentList…)>          // (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).

4.6.4. Исполнитель

Реализация исполнителя для инициатора с универсальным аргументом (см. Листинг 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<void(void*, int)>(ExternalHandler, &executor));

  // Pointer to the static method

  initiator.setup(CallbackConverter<void(Executor*, int)>(Executor::staticCallbackHandler, &executor));

  // Pointer to the class member method

  initiator.setup(CallbackConverter<void(Executor::*)(int)>(&Executor::callbackHandler, &executor));

  // Functional object

  initiator.setup(executor);

  // Lambda-expression

  initiator.setup([capturedValue](int eventID) {});

}

Если сравнить приведенную реализацию исполнителя для шаблона-инициатора с фиксированным типом аргумента (Листинг 43 и Листинг 44 п. 4.4.3) с приведенной, то можно заметить следующее. В первом случае для каждого типа аргумента приходится объявлять отдельный инициатор, инстанциируя его соответствующим типом. Здесь инициатор объявляется один раз, после чего тип аргумента вызова настраивается в процессе выполнения программы. В результате упрощается разработка, улучшается гибкость и прозрачность кода.

4.6.5. Инициатор для методов класса

До сих пор для вызова методов класса мы использовали преобразование вызовов. Однако, поскольку std::function непосредственно поддерживает вызов методов, появляется возможность реализовать специализированный инициатор для указанного случая. За основу возьмем инициатор из п. 4.6.2 и модифицируем его.