Telegram Group & Telegram Channel
#story

Самое простое объяснение std::function за 15 минут 😦

Этот пост был написан под влиянием крутого видео от Jason Turner "A Simplified std::function Implementation"

Часто люди не задумываются, как работает std::function. Чаще всего знают, что эта штука - обертка над чем-то, что можно "вызвать" как функцию. Кто-то смутно помнит, что std::function вроде как лезет в динамическую память. cppreference не сильно раскрывает внутренности реализации.

Можно сказать, что в C++ есть два типа объектов, на которых работает семантика вызова как функции. Можно условно назвать их Callable. Это:
1️⃣ Сами функции:
    int foo(int a, int b) { return a + b; }
2️⃣ Объекты типов с определенным operator(), часто их называют "функторы":
    struct foo {
int operator()(int a, int b) { return a + b; }
}
Все остальные Callable являются производными от этих двух типов. В том числе лямбды - компилятор их переделывает в структуры с operator(). Про лямбды есть хорошая книга - https://www.group-telegram.com/us/cxx95.com/48.

А std::function<Signature> должен уметь хранить все возможные Callable с данной сигнатурой.

template<typename Ret, typename... Param>
class function<Ret(Param...)> {
// код реализации
};

Возникает проблема - у std::function должен быть фиксированный размер, но Callable типа 2️⃣ может иметь неопределенный размер. Например, размер структуры у лямбды зависит от того, какие captures он делает.

Поэтому, к сожалению, std::function хранит Callable в куче.
Также нужно использовать виртуальный класс, который для каждого отдельного типа как бы вычислит адрес вызываемого метода:

Виртуальный класс и указатель на кучу:
    struct callable_interface {
virtual Ret call(Param...) = 0;
virtual ~callable_interface() = default;
};
std::unique_ptr<callable_interface> callable_ptr;

Реализация для каждого отдельного типа Callable держит в себе сам объект Callable и метод для вызова operator() по правильному адресу:
    template<typename Callable>
struct callable_impl : callable_interface {
callable_impl(Callable callable_) : callable{std::move(callable_)} {}
Ret call(Param... param) override { return std::invoke(callable, param...); };
Callable callable;
}

Конструктор std::function принимает Callable и создает объект в куче:
    template<typename Callable>
function(Callable callable)
: callable_ptr{std::make_unique<callable_impl<Callable>>(std::move(callable))}
{}

И наконец вызов operator() у самого std::function перенаправляет вызов в содержимый Callable:
    Ret operator()(Param... param) { return callable_ptr->call(param...); }

Вот так выглядит один из способов type erasure в C++ 👍
Please open Telegram to view this post
VIEW IN TELEGRAM



group-telegram.com/cxx95/75
Create:
Last Update:

#story

Самое простое объяснение std::function за 15 минут 😦

Этот пост был написан под влиянием крутого видео от Jason Turner "A Simplified std::function Implementation"

Часто люди не задумываются, как работает std::function. Чаще всего знают, что эта штука - обертка над чем-то, что можно "вызвать" как функцию. Кто-то смутно помнит, что std::function вроде как лезет в динамическую память. cppreference не сильно раскрывает внутренности реализации.

Можно сказать, что в C++ есть два типа объектов, на которых работает семантика вызова как функции. Можно условно назвать их Callable. Это:
1️⃣ Сами функции:

    int foo(int a, int b) { return a + b; }
2️⃣ Объекты типов с определенным operator(), часто их называют "функторы":
    struct foo {
int operator()(int a, int b) { return a + b; }
}
Все остальные Callable являются производными от этих двух типов. В том числе лямбды - компилятор их переделывает в структуры с operator(). Про лямбды есть хорошая книга - https://www.group-telegram.com/us/cxx95.com/48.

А std::function<Signature> должен уметь хранить все возможные Callable с данной сигнатурой.

template<typename Ret, typename... Param>
class function<Ret(Param...)> {
// код реализации
};

Возникает проблема - у std::function должен быть фиксированный размер, но Callable типа 2️⃣ может иметь неопределенный размер. Например, размер структуры у лямбды зависит от того, какие captures он делает.

Поэтому, к сожалению, std::function хранит Callable в куче.
Также нужно использовать виртуальный класс, который для каждого отдельного типа как бы вычислит адрес вызываемого метода:

Виртуальный класс и указатель на кучу:
    struct callable_interface {
virtual Ret call(Param...) = 0;
virtual ~callable_interface() = default;
};
std::unique_ptr<callable_interface> callable_ptr;

Реализация для каждого отдельного типа Callable держит в себе сам объект Callable и метод для вызова operator() по правильному адресу:
    template<typename Callable>
struct callable_impl : callable_interface {
callable_impl(Callable callable_) : callable{std::move(callable_)} {}
Ret call(Param... param) override { return std::invoke(callable, param...); };
Callable callable;
}

Конструктор std::function принимает Callable и создает объект в куче:
    template<typename Callable>
function(Callable callable)
: callable_ptr{std::make_unique<callable_impl<Callable>>(std::move(callable))}
{}

И наконец вызов operator() у самого std::function перенаправляет вызов в содержимый Callable:
    Ret operator()(Param... param) { return callable_ptr->call(param...); }

Вот так выглядит один из способов type erasure в C++ 👍

BY C++95




Share with your friend now:
group-telegram.com/cxx95/75

View MORE
Open in Telegram


Telegram | DID YOU KNOW?

Date: |

There was another possible development: Reuters also reported that Ukraine said that Belarus could soon join the invasion of Ukraine. However, the AFP, citing a Pentagon official, said the U.S. hasn’t yet seen evidence that Belarusian troops are in Ukraine. Telegram Messenger Blocks Navalny Bot During Russian Election The fake Zelenskiy account reached 20,000 followers on Telegram before it was shut down, a remedial action that experts say is all too rare. The regulator said it had received information that messages containing stock tips and other investment advice with respect to selected listed companies are being widely circulated through websites and social media platforms such as Telegram, Facebook, WhatsApp and Instagram. Emerson Brooking, a disinformation expert at the Atlantic Council's Digital Forensic Research Lab, said: "Back in the Wild West period of content moderation, like 2014 or 2015, maybe they could have gotten away with it, but it stands in marked contrast with how other companies run themselves today."
from us


Telegram C++95
FROM American