Виталий Ткаченко - Обратные вызовы в C++

Тут можно читать онлайн Виталий Ткаченко - Обратные вызовы в C++ - бесплатно полную версию книги (целиком) без сокращений. Жанр: comp-programming, издательство Array SelfPub.ru, год 2021. Здесь Вы можете читать полную версию (весь текст) онлайн без регистрации и SMS на сайте лучшей интернет библиотеки ЛибКинг или прочесть краткое содержание (суть), предисловие и аннотацию. Так же сможете купить и скачать торрент в электронном формате fb2, найти и слушать аудиокнигу на русском языке или узнать сколько частей в серии и всего страниц в публикации. Читателям доступно смотреть обложку, картинки, описание и отзывы (комментарии) о произведении.

Виталий Ткаченко - Обратные вызовы в C++ краткое содержание

Обратные вызовы в C++ - описание и краткое содержание, автор Виталий Ткаченко, читайте бесплатно онлайн на сайте электронной библиотеки LibKing.Ru
В практике разработки ПО зачастую встает задача динамической модификации программного кода в зависимости от текущих или настраиваемых значений параметров. Для решения этой задачи широко используются обратные вызовы. В языке C++ обратные вызовы реализуются различными способами, и далеко не всегда очевидно, какой из них лучший для конкретной ситуации. В книге рассмотрены теоретические и практические аспекты организации обратных вызовов, проанализированы достоинства и недостатки различных реализаций, выработаны рекомендации по выбору в зависимости от требований к проектируемому ПО. В первую очередь книга предназначена для программистов среднего (middle) уровня, т.е. тех, кто уже достаточно хорошо знает язык C++, но хотел бы расширить и углубить свои знания в области проектирования и дизайна. В определенной степени она также будет интересна опытным разработчикам, с одной стороны, как систематизация знаний, с другой стороны, как источник идей и методов для решения практических задач.

Обратные вызовы в C++ - читать онлайн бесплатно полную версию (весь текст целиком)

Обратные вызовы в C++ - читать книгу онлайн бесплатно, автор Виталий Ткаченко
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать

FO fo;

int eventID = 0;

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

auto callbackToMethod = std::bind(&FO::callbackHandler, fo, std::placeholders::_1);

StaticDistributorVoid distributor(ExternalHandler, fo, callbackToMethod, lambda); // (1)

distributor(eventID); // (2)

}

Как видим, использование очень простое: в строке 1 объявляется распределитель, в конструктор передаются объекты вызова, через перегруженный оператор 2 производятся вызовы сохраненных объектов.

5.5.2. Распределение с возвратом результатов

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

Листинг 77. Распределитель для статического набора с возвратом результатов

template // (1)

class StaticDistributorReturn

{

public:

StaticDistributorReturn(CallObjects… objects) : callObjects(objects…) {} // (2)

auto& tuple() { return callObjects; } // (3)

template // (4)

auto operator() (CallData… callData)

{

return DistributeReturn(callObjects, callData…);

}

private:

std::tuple callObjects; // (5)

};

В строке 4 объявлен перегруженный оператор с возвращаемым типом auto. Указанный тип будет выведен из значения, возвращаемого соответствующей распределяющей функцией. (реализацию см. в Листинг 73 п. 5.4.1).

Пример использования распределителя приведен в Листинг 78.

Листинг 78. Использование распределителя для статического набора с возвратом результатов

struct FO

{

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

int callbackHandler(int eventID) { return 0; }

};

struct SResult

{

unsigned int code;

const char* description;

};

SResult ExternalHandler(int eventID)

{

return SResult{ 1, "this is an error" };

}

int main()

{

FO fo;

int eventID = 0;

auto lambda = [](int eventID) { return 0.0; };

auto callbackToMethod = std::bind(&FO::callbackHandler, fo, std::placeholders::_1);

StaticDistributorReturn distributor(ExternalHandler, fo, callbackToMethod, lambda); // (1)

auto [resExtHandler, resFoOperator, resFoMethod, resLambda] = distributor(eventID); // (2)

}

В строке 1 объявляется распределитель, в конструктор передаются объекты вызова. Через перегруженный оператор 2 производятся вызовы хранимых объектов, результаты возвращаются с помощью структурных привязок.

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

5.5.3. Параметризация возвращаемого значения

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

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

template // (1)

class StaticDistributor

{

public:

StaticDistributor(CallObjects… objects) : callObjects(objects…) {} // (2)

auto& tuple() { return callObjects; } // (3)

template

auto operator() (CallData… callData) // (4)

{

#define callObject std::get<0>(callObjects) // (5)

#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<0>(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;

};

SResult ExternalReturn(int eventID)

{

return SResult{ 1, "this is an error" };

}

void ExternalVoid(int eventID)

{

}

int main()

{

int eventID = 0;

FOReturn foRet;

FOVoid foVoid;

auto lambdaRet = [](int eventID) { return 0.0; };

auto lambdaVoid = [](int eventID) {};

using FunPtrRet = SResult(*)(int);

using LambdaTypeRet = decltype(lambdaRet);

using FunPtrVoid = void(*)(int);

using LambdaTypeVoid = decltype(lambdaVoid);

StaticDistributor distributor1(foRet, ExternalReturn, lambdaRet); // (1)

StaticDistributor distributor2(foVoid, ExternalVoid, lambdaVoid); // (2)

auto results = distributor1(eventID);

distributor2(eventID);

}

Как видим, в обоих случаях объявляется один и тот же распределитель, а из свойств объектов распределения будет генерироваться соответствующий перегруженный оператор.

Читать дальше
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать


Виталий Ткаченко читать все книги автора по порядку

Виталий Ткаченко - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки LibKing.




Обратные вызовы в C++ отзывы


Отзывы читателей о книге Обратные вызовы в C++, автор: Виталий Ткаченко. Читайте комментарии и мнения людей о произведении.


Понравилась книга? Поделитесь впечатлениями - оставьте Ваш отзыв или расскажите друзьям

Напишите свой комментарий
x