В рассмотренных выше примерах мы предполагали, что все получатели используют одну и ту же сигнатуру вызова. Но что делать, если они имеют разные сигнатуры? Нам необходимо разработать какой-то объект, который бы обеспечивал следующее: настройку входной сигнатуры, в которую передаются данные вызова; настройку выходной сигнатуры, которая определяется получателем; преобразование одной сигнатуры в другую. По сути дела, необходимо обеспечить перенаправление вызовов, что решается с помощью инструментов STL, а именно – объектов связывания (см. п. 4.6.2). В этом случае в функцию распределителя вместо объекта-получателя передается объект-связывание, который осуществляет перенаправление вызова с заданной сигнатурой. Пример реализации приведен в Листинг 72; здесь в качестве распределяющей функции используется реализация из Листинг 69 п. 5.3.3.

Листинг 72. Перенаправление вызовов с настройкой сигнатуры

void NativeHandler(int eventID)

{

}

void ExternalHandler(int eventID, int contextID)

{

}

struct FO

{

  void operator() (int eventID, int contextID) {}

  void callbackHandler(int eventID, int contextID) {}

};

int main()

{

  int eventID = 0, contextID = 0;

  FO fo;

  auto lambda = [](int eventID, int contextID) {};

  Distribute2(std::tuple(   // (1)

    NativeHandler,          // (2)

    std::bind(ExternalHandler, std::placeholders::_1, contextID),           // (3)

    std::bind(&FO:: callbackHandler, fo, std::placeholders::_1, contextID), // (4)

    std::bind(&FO::operator(), fo, std::placeholders::_1, contextID),       // (5)

    std::bind(lambda, std::placeholders::_1, contextID)                     // (6)

    ),

    eventID // (7)

  );

}

Входными аргументами распределяющей функции служат кортеж объектов вызова (объявлен в строке 1) и данные вызова (строка 7). В строке 2 в кортеж передается объект вызова с сигнатурой, совпадающей с исходной. В строке 3 передается объект связывания (результат вызова std::bind), в котором исходный вызов перенаправляется в назначенную функцию ExternalHandler. В строке 4 объект связывания перенаправляет вызов в метод-член структуры, в строке 5 – в перегруженный оператор, в строке 6 – в лямбда-выражение.

<p>5.4. Возврат результатов выполнения</p><p>5.4.1. Получение возвращаемых значений</p>

До сих пор мы считали, что функции, реализующие код вызова, не возвращают результатов. Однако в некоторых случаях необходимо получить результаты выполнения вызовов. Очевидно, что в этом случае их должна вернуть распределяющая функция. Как же сформировать возвращаемые значение?

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

Но сформировать набор результатов выполнения не так-то просто. Мы не можем перечислить в списке аргументов запрос объекта по индексу и его вызов, ведь количество объектов заранее не известно. Поэтому предварительно необходимо сформировать последовательность индексов, которая разворачивается в контексте запроса и вызова объекта. Реализация приведена в Листинг 73.

Листинг 73. Распределение вызовов с возвратом результатов

template               // (1)

auto  DistributeReturnImpl(std::tuple& callObjects, std::index_sequence, CallData… callData)  // (2)

{

  return std::tuple(std::get(callObjects)(callData…)…);                         // (3)

}

template                               // (4)

auto DistributeReturn(std::tuple callObjects, CallData… callData)  // (5)

{

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

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