Обратные вызовы в C++ | страница 75



>#define callObjType decltype(callObject)             // (6)

>#define callObjInstance std::declval()  // (7)

>#define testCall callObjInstance(callData…)        // (8)

>#define retType decltype(testCall)                   // (9)


>  //if constexpr (std::is_same_v(callObjects))>()(callData…))>)  // (10)

>  if constexpr (std::is_same_v)         // (11)

>    return Distribute2(callObjects, callData…);      // (12)

>  else                              

>    return DistributeReturn(callObjects, callData…);  // (13)

>  }

>private:

>  std::tuple callObjects;

>>};


В строках 1 – 4 код идентичен реализации распределителя в предыдущих случаях (Листинг 75 п. 5.5.1, Листинг 77 п. 5.5.2). Интерес представляет реализация перегруженного оператора (строка 4).

Макросы в строках 5 – 9 предназначены только для облегчения понимания кода, без них конструкция получается запутанной (строка 10).

В строке 5 мы получаем объект вызова, для которого будет проверяться, возвращает ли он значение. Мы запрашиваем нулевой элемент кортежа, поскольку предполагается, что кортеж содержит хотя-бы один объект (иначе зачем распределять вызовы для пустого кортежа?).

В строке 6 определяется тип объекта, который мы запросили. В строке 7 объявляется мета-экземпляр объекта соответствующего типа. Мы говорим «мета-экземпляр», потому что реально объект не создается, но его характеристики используются компилятором для анализа. Конструкция declval необходима, чтобы не было ошибки в случае, если объект не имеет конструктора по умолчанию.

В строке 8 производится мета-вызов с передачей параметров.  Мета-вызов здесь имеет тот же смысл, что и мета-экземпляр, т. е. в реальности вызов не производится, а используется для анализа. В строке 9 определяется тип значения, возвращаемого мета-вызовом.

В строке 11 проверяется, является ли тип возвращаемого значения void, и в этом случае вызывается распределяющая функция без возврата результатов (строка 12). В противном случае вызывается распределяющая функция, возвращающая результаты (строка 13).


Использование распределителя с условной компиляцией приведено в Листинг 80.

Листинг 80. Условная компиляция в зависимости от типа возвращаемого значения

>struct FOReturn

>{

>  int operator() (int eventID) {return 10;}

>};


>struct FOVoid

>{

>  void operator() (int eventID) {  /*do something*/  }

>};


>struct SResult

>{

>  unsigned int code;

>  const char* description;