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/hk/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
👍10🖕1



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/hk/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: |

During the operations, Sebi officials seized various records and documents, including 34 mobile phones, six laptops, four desktops, four tablets, two hard drive disks and one pen drive from the custody of these persons. DFR Lab sent the image through Microsoft Azure's Face Verification program and found that it was "highly unlikely" that the person in the second photo was the same as the first woman. The fact-checker Logically AI also found the claim to be false. The woman, Olena Kurilo, was also captured in a video after the airstrike and shown to have the injuries. The picture was mixed overseas. Hong Kong’s Hang Seng Index fell 1.6%, under pressure from U.S. regulatory scrutiny on New York-listed Chinese companies. Stocks were more buoyant in Europe, where Frankfurt’s DAX surged 1.4%. Investors took profits on Friday while they could ahead of the weekend, explained Tom Essaye, founder of Sevens Report Research. Saturday and Sunday could easily bring unfortunate news on the war front—and traders would rather be able to sell any recent winnings at Friday’s earlier prices than wait for a potentially lower price at Monday’s open.
from hk


Telegram C++95
FROM American