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

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

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

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

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

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

Интервал:

Закладка:

Сделать
Рис 17 Стирание типов а исходное состояние б состояние после назначения - фото 29

Рис. 17. Стирание типов: а) исходное состояние; б) состояние после назначения нового типа аргумента

Реализация рассмотренной схемы представлена в Листинг 45.

Листинг 45. Класс, реализующий стирание типов

class UniArgument // (1)

{

private:

class Callable // (2)

{

public:

virtual void operator()(int) = 0; // (3)

};

std::unique_ptr callablePointer; // (4)

template

class CallableObject : public Callable // (5)

{

public:

CallableObject(ArgType argument) : storedArgument(argument) { } // (6)

void operator() (int value) override // (7)

{

storedArgument(value); // (8)

}

private:

ArgType storedArgument; // (9)

};

public:

void operator() (int value) // (10)

{

callablePointer->operator()(value); // (11)

}

template

void operator = (ArgType argument) // (12)

{

callablePointer.reset(new CallableObject(argument)); // (13)

}

};

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

В базовом классе перегружен оператор вызова функции 3. Оператор объявлен чисто виртуальным, чтобы опустить его реализацию. Предполагается, что этот оператор будет выполнять обратный вызов, но аргумента вызова здесь нет, он будет храниться в наследуемом классе. Таким образом, реализация смысла не имеет. Более того, если, допустим, нам понадобится, чтобы оператор возвращал результат, то в нем должна присутствовать команда return, и какое тогда возвращать значение?

В строке 4 объявлен указатель на базовый класс, объявленный в 2.

В строке 5 объявлен шаблонный класс, который будет хранить переданный аргумент и вызывать его. Переменная для хранения аргумента объявлена в строке 9, тип переменной задается параметром шаблона. Аргумент назначается в конструкторе 6. Также в этом классе переопределяется оператор вызова функции 7, в котором происходит обратный вызов 8 через сохраненный аргумент.

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

В строке 12 объявлен шаблонный оператор присваивания, который настраивает аргумент. В реализации этого оператора 13 создается новый класс CallableObjectнужного типа, в конструкторе этого класса переданный аргумент сохраняется, после чего переназначается указатель. Таким образом, при вызове оператора 10 будет вызван оператор соответствующего класса 11, и последний осуществит вызов через сохраненный аргумент.

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

Объект вызова– это некоторая конструкция C++, поддерживающая интерфейс вызова в формате функции.

В соответствии с стандартом C++ на сегодняшний день 23 23 На момент написания книги это C++ 20. , в качестве объектов вызова могут использоваться следующие конструкции:

• функции;

• методы класса;

• классы с перегруженным оператором вызова функции;

• лямбда-выражения.

В реализациях инициатора с помощью шаблонов, рассмотренных в предыдущих главах (см. п. 4.2.1, 4.4.1), аргумент вызова совпадает с объектом вызова. При использовании универсального аргумента эти сущности будут различаться: универсальный аргумент хранит в себе объект вызова.

Итак, мы реализовали универсальный аргумент, продемонстрируем теперь, как он может использоваться для реализации обратных вызовов (Листинг 46).

Листинг 46. Использование универсального аргумента

class Executor

{

public:

static void staticCallbackHandler(int eventID, Executor* executor) {}

void callbackHandler(int eventID) {}

void operator() (int eventID) {}

};

void ExternalHandler(int eventID, void* somePointer) {}

int main()

{

UniArgument argument;

Executor executor;

int capturedValue = 0;

using PtrExtFunc = void(*) (int, void*);

argument = CallbackConverter(ExternalHandler, &executor); // (1)

using PtrStaticMethod = void(*) (int, Executor*);

argument = CallbackConverter(Executor::staticCallbackHandler, &executor); //(2)

using PtrMemberMethod = void(Executor::*)(int);

argument = CallbackConverter(&Executor::callbackHandler, &executor); // (3)

argument = executor; // (4)

argument = [capturedValue](int eventID) {/*Body of lambda*/}; // (5)

}

В строке 1 аргументу присваивается указатель на функцию, для преобразования вызовов используется класс CallbackConverterиз Листинг 27 п. 4.2.2. Этот класс инстанциируется соответствующими типами, в конструкторе ему передается функция ExternalHandlerи контекст, в качестве которого выступает указатель на класс Executor.

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

В строке 3 аргументу присваивается указатель на метод-член класса, для преобразования вызовов используется класс CallbackConverterиз Листинг 28 п. 4.2.2. Этот класс инстанциируется соответствующими типами, в конструкторе ему передается указатель на класс и указатель на метод класса.

В строке 4 аргументу присваивается функциональный объект, в строке 5 – лямбда-выражение.

Отметим, что в универсальном аргументе лямбда-выражение сохраняется также просто, как и любой другой тип. Это связано с тем, что как оператор присваивания ( operator =класса UniArgument, Листинг 45 п. 4.5.1), так и класс для хранения аргументов вызова ( CallableObject, там же) реализованы в виде шаблонов. Когда мы вызываем указанный оператор, передавая ему лямбда-выражение, компилятор неявно выведет тип параметра шаблона из переданного аргумента, подобно тому, как это происходит в шаблонной функции для синхронных вызовов. В свою очередь, внутри оператора с помощью newдинамически создается экземпляр CallableObject, инстанциированный соответствующим выведенным типом. Таким образом, явно указывать тип передаваемого аргумента не требуется, компилятор выводит его сам.

4.5.2. Настройка сигнатуры

До сих пор мы предполагали, что функция, реализующая обратный вызов, имеет тип voidи на вход принимает только одно значение eventID, и исходя из этого, делали обратный вызов. А если выясняется, что функция должна иметь дополнительные параметры, нам придется изменять реализацию универсального аргумента и объектов, с ним связанных? А если нам необходимы инициаторы, которые используют функции с различными сигнатурами? Теперь что, для каждой сигнатуры придется реализовать отдельный аргумент? Есть другой путь: настройка сигнатуры вызова через параметры шаблона. Для ее реализации используется частичная специализация шаблона в сочетании с переменным числом параметров (partial template specialization, variadic templates), пример представлен в Листинг 47.

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

Интервал:

Закладка:

Сделать


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

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




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


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


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

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