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

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

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

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

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

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

Интервал:

Закладка:

Сделать

{

[](int eventID) {/*this is a body of lambda*/}; // (1)

//The following object will be generated implicitly by the compiler from lambda declaration

class Closure // (2)

{

public:

void operator() (int eventID) // (3)

{

call_invoker(eventID);

}

static void call_invoker(int eventID) { /*this is a body of lambda*/ } // (4)

using function_pointer = void(*)(int); // (5)

operator function_pointer() const // (6)

{

return call_invoker;

}

};

//Conversion the closure object to the function pointer

Closure cl; // (7)

using pointer_to_function = void(*)(int); // (8)

pointer_to_function fptr = cl; // (9)

//Conversion a lambda to the function pointer

fptr = [](int eventID) {/*this is a body of lambda*/}; // (10)

}

В строке 1 объявлено лямбда-выражение, в строке 2 объявлен объект-замыкание. Подчеркнем: этот объект здесь всего лишь для демонстрации, чтобы показать, как он будет сгенерирован компилятором. В реальном коде такой объект объявлять не нужно, компилятор его создаст при объявлении лямбда-выражения.

В строке 3 объявлен перегруженный оператор, который вызывает статическую функцию 4. В той функции размещается код лямбда-выражения.

В строке 5 объявлен тип указателя на функцию, в строке 6 объявлен оператор преобразования типа. Реализация оператора возвращает указатель на статическую функцию 4.

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

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

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

Если необходимо хранить лямбда-выражение в локальной переменной, можно использовать тип auto. Это означает, что компилятор подставит соответствующий тип, который будет сгенерирован из объявления лямбда-выражения (см. Листинг 40).

Листинг 40. Сохранение лямбда-выражения в локальной переменной

int capture = 10;

auto lambda = [capture](int eventID) {/*this is a body of lambda*/};

lambda(10); //lambda call

Однако указанный способ не будет работать, когда требуется сохранить лямбда-выражение в классе. Мы не можем объявить переменную – член класса с типом auto, потому что это означало бы объявление переменной заранее не определенного типа, что не допускается.

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

Добавим в реализацию инициатора, описанного в Листинг 37 п. 4.4.1, два конструктора. Один конструктор будет с переменной – аргументом обратного вызова для инициализации члена класса. Другой конструктор будет без аргументов (конструктор по умолчанию), чтобы оставить возможность отложенной настройки (Листинг 41).

Листинг 41. Инициатор с дополнительными конструкторами

template

class Initiator

{

public:

Initiator() {}

Initiator(const CallbackArgument& argument) : callbackHandler(argument) {}

void setup(const CallbackArgument& argument)

{

callbackHandler = argument;

}

void run()

{

int eventID = 0;

//Some actions

callbackHandler(eventID);

}

private:

CallbackArgument callbackHandler;

};

Для любых типов аргументов обратного вызова, кроме лямбда-выражений, допускается использование обоих конструкторов. Для лямбда-выражений допускается использование только конструктора с аргументом, при попытке использования конструктора по умолчанию компилятор выдаст ошибку. Кроме того, в этом случае нельзя будет вызвать метод setup – также будет сгенерирована ошибка. Таким образом, использование инициатора с лямбда-выражением не предполагает динамической модификации: настройка происходит один раз в конструкторе при инстанциировании шаблона, и больше изменить ее нельзя 20 20 Указанная проблема решается при использовании универсального аргумента, о чем пойдет речь в следующей главе .

А какой тип аргумента нам указывать при инстанциировании шаблона, ведь тип лямбда-выражения является анонимным? Для этой цели мы будем использовать ключевое слово decltype, которое возвращает тип объявленной переменной (см. Листинг 42).

Листинг 42.Инстанциирование шаблона асинхронного обратного вызова для лямбда-выражения

int capture = 10;

auto lambda = [capture](int eventID) {/*this is a body of lambda*/};

Initiator callbackLambda1 (lambda); // Ok, initialization in constructor

Initiator callbackLambda = lambda; // Ok, implicit constructor call

Initiator callbackLambda2; //Error: attempting to reference a deleted function

callbackLambda.setup(lambda); //Error: ‘operator’ = attempting to reference a deleted function

callbackLambda.run();

4.4.3. Исполнитель

В Листинг 43 приведены примеры реализации исполнителя для различных типов аргументов. Объявления класса CallbackConverterпредставлены в Листинг 27 и Листинг 28 п. 4.2.2, инициатор используется из Листинг 41 п. 4.4.2.

Листинг 43. Исполнитель для шаблона-инициатора с различными типами аргумента

class Executor // (1)

{

public:

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

void callbackHandler(int eventID) {}

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

Интервал:

Закладка:

Сделать


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

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




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


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


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

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