Skip to content

CPP-KT/function-task

Repository files navigation

function

Класс function представляет собой полиморфную оболочку для функциональных объектов, а именно для тех, чей тип удовлетворяет Callable и CopyConstructible. Это может быть указатель на функцию, лямбда, класс с перегруженным operator() и т.д. Хранимый функциональный объект в терминах стандарта принято называть target.

Напоминание идеи с лекции

Храним полем unique_ptr<concept>, где concept — класс с виртуальными функциями (например, operator()). При конструировании из функционального объекта типа F создаём в динамической памяти model<F>, где хранится сам F, а также определены те самые виртуальные функции.

Small-object optimization

Функциональные объекты часто не имеют состояния вообще, или имеют, но небольшое. Поэтому возникает естественное желание легковесные объекты хранить непосредственно внутри function, вместо указателя на динамическую память.

К сожалению, нам больше не подойдёт unique_ptr, ведь в случае small object не будет указателя на динамическую память. Во-первых, не очень понятно, какой у такого unique_ptr'а должен быть Deleter. Во-вторых, а как тогда правильно мувать small object'ы?

Идея реализации

Разделим хранилище и логику на два поля:

  • выровненный массив байт, где будет лежать либо сам функциональный объект (в случае small object), либо указатель на него;
  • указатель на дескриптор — набор вспомогательных функций для оперирования хранилищем (может быть выражен классом с виртуальными функциями).

В зависимости от типа функционального объекта, будем хранить указатели на дескрипторы с разными динамическими типами.

Не имеет смысл когда-либо иметь два инстанса дескриптора с одинаковым динамическим типом — они эквивалентны. Мы можем этим воспользоваться, чтобы избежать каких-либо динамических аллокаций для дескрипторов — будем просто хранить по одному инстансу каждого дескриптора статически, и ссылаться на них.

Прочие требования

Дефолтный конструктор создаёт пустой (empty) function, который не хранит в себе никакой функциональный объект, а при вызове operator() кидает bad_function_call. Для него удобно сделать отдельный дескриптор.

В отличие от стандартной библиотеки, ваш function должен гарантировать небросающие мувающие операции (конструктор и оператор присваивания). К сожалению, в общем случае это невозможно при SOO, поэтому используйте эту оптимизацию только для тех T, у которых мув и сам не бросает.

Метод F* target<F>() должен возвращать указатель на текущий функциональный объект, если его динамический тип совпадает с F, и nullptr иначе.

Работа с буфером для SOO должна быть корректна с точки зрения алиасинга и выравнивания.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published