В строке 11 запускается процесс итерации путем инстанциирования шаблона TupleIterator. Аргументами шаблона выступают: количество объектов вызова (строка 12), вычисляется с помощью операции sizeof применительно к соответствующему пакету параметров; кортеж объектов вызова (строка 13); данные, передаваемые в вызов (строка 14). В строке 15 вызывается стартовая функция итерации с передачей соответствующих аргументов. Как видим, начальное значение индекса равно количеству объектов вызова, которое затем с каждой новой итерацией будет уменьшаться на единицу, в то время как пересчитываемый индекс, соответственно, увеличивается.

<p>5.3.4. Способ 3: объекты и данные в кортежах</p>

При использовании данного способа реализация практически повторяет рассмотренную в предыдущем параграфе, только вместо пакета данных будет использоваться кортеж (Листинг 70).

Листинг 70. Распределение при упаковке объектов и данных в кортежи

template          // (1)

struct TupleIterator3

{

  static void IterateTupleItem(CallObjects& callObjects, CallData& callData)  // (2)

  {

      const std::size_t idx = std::tuple_size_v – Index;         // (3)

      std::apply(std::get(callObjects), callData);                       // (4)

      TupleIterator3::IterateTupleItem(callObjects, callData);  // (5)

  }

};

template  // (6)

struct TupleIterator3<0, CallObjects, CallData>    // (7)

{

  static void IterateTupleItem(CallObjects& callObjects, CallData& callData)  // (8)

  {

  }

};

template                                     // (9)

void Distribute3(std::tuple callObjects, std::tuple callData)  // (10)

{

  TupleIterator3               // (11)

  <

  sizeof…(CallObjects),      // (12)

  std::tuple,  // (13)

  std::tuple      // (14)

  >

  ::IterateTupleItem(callObjects, callData);  // (15)

}

По сравнению с Листинг 69 п. 5.3.3 изменения здесь следующие. Входными параметрами распределяющей функции (строка 10) являются кортеж объектов и кортеж данных (ранее параметр для данных задавался пакетом). В объявлениях шаблонов структур для обхода кортежа (строки 1, 6) параметр, определяющий данные вызова, объявляется как тип (ранее это был пакет). Вызов объекта (строка 4) осуществляется через std::apply (ранее объект вызывался непосредственно). И еще здесь изменены имена структур, чтобы избежать конфликта имен с предыдущей реализацией.

<p>5.3.5. Сравнение способов</p>

В Листинг 71 приведен пример распределения вызовов с использованием различных способов настройки сигнатуры, в качестве данных выступают два числовых значения.

Листинг 71. Распределение вызовов с заданной сигнатурой

void ExternalHandler(int eventID, int contextID) {}

struct FO

{

void callbackHandler(int eventID, int contextID) {}

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

};

int main()

{

int eventID = 0, contextID = 1;

FO fo;

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

auto cb2cl = std::bind(&FO::callbackHandler, fo, _1, _2);

Distribute1(std::tuple(eventID, contextID), ExternalHandler, fo, cb2cl, lambda);

Distribute2(std::tuple(ExternalHandler, fo, cb2cl, lambda), eventID, contextID);

Distribute3(std::tuple(ExternalHandler, fo, cb2cl, lambda), std::tuple(eventID, contextID));

}

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

<p>5.3.6. Настройка сигнатуры для перенаправления</p>
Перейти на страницу:

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