aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/yt/memory/function_view.h
blob: 108fd076ce3171102057d5d500ceb4535c736c55 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#pragma once

#include <library/cpp/yt/misc/concepts.h>

namespace NYT {

////////////////////////////////////////////////////////////////////////////////

// Non-owning type-erasure container.
/*
    Example:

    template <class T>
    class TSerializedObject
    {
    public:
        explicit TSerializedObject(T value)
            : Object_(value)
        { }

        void Lock(TFunctionView<void(const T&)> callback)
        {
            auto guard = Guard(SpinLock_);
            callback(Object_);
        }

    private:
        TSpinLock SpinLock_;
        T Object_;
    };

    int main()
    {
        TSerializedObject<int> object(42);

        // object.Lock([] (const int& value) {
        //     fmt::println("Value is {}", value);
        // });
        // ^ CE -- cannot pass rvalue.

        auto callback = [] (const int& value) {
            fmt::println("Value is {}", value);
        };

        object.Lock(callback); // <- prints "Value is 42".
    }
*/
template <class TSignature>
class TFunctionView;

////////////////////////////////////////////////////////////////////////////////

// TODO(arkady-e1ppa): Support pointer-to-member-function?
template <class T, class TSignature>
concept CTypeErasable =
    CInvocable<T, TSignature> &&
    (!std::same_as<T, TFunctionView<TSignature>>);

////////////////////////////////////////////////////////////////////////////////

template <class TResult, bool NoExcept, class... TArgs>
class TFunctionView<TResult(TArgs...) noexcept(NoExcept)>
{
public:
    using TSignature = TResult(TArgs...) noexcept(NoExcept);

    TFunctionView() = default;

    template <CTypeErasable<TSignature> TConcrete>
    TFunctionView(TConcrete& concreteRef) noexcept;

    template <CTypeErasable<TSignature> TConcrete>
    TFunctionView(TConcrete* concretePtr) noexcept;

    TResult operator()(TArgs... args) noexcept(NoExcept);

    explicit operator bool() const noexcept;

    TFunctionView Release() noexcept;

    bool IsValid() const noexcept;
    void Reset() noexcept;

    bool operator==(const TFunctionView& other) const & = default;

private:
    // NB: Technically, this is UB according to C standard, which
    // was not changed for C++ standard.
    // This is so because it is allowed to have
    // function pointers to be modelled by entities
    // different from object pointers.
    // No reasonable system architecture (e.g. x86 or ARM
    // or any other POSIX compliant one) does this.
    // No reasonable compiler (clang/gcc) does anything with this.
    // Accounting for such requirement would cause this class
    // to have std::variant-like storage which would make this class
    // weight more. Thus, we have decided to keep it this way,
    // since we are safe on x86 or ARM + clang.
    using TErasedPtr = void*;
    using TErasedInvoke = TResult(*)(TArgs..., TErasedPtr);

    TErasedPtr Ptr_ = nullptr;
    TErasedInvoke Invoke_ = nullptr;

    template <class TConcrete>
    static TResult ConcreteInvoke(TArgs... args, TErasedPtr ptr) noexcept(NoExcept);
};

////////////////////////////////////////////////////////////////////////////////

} // namespace NYT

#define FUNCTION_VIEW_INL_H_
#include "function_view-inl.h"
#undef FUNCTION_VIEW_INL_H_