#pragma once #include #include #include namespace NPrivate { template struct TIsNoexcept; template struct TIsNoexcept { static constexpr bool Value = false; }; template struct TIsNoexcept { static constexpr bool Value = true; }; } // namespace NPrivate template ::Value> class TFunctionRef; template class TFunctionRef { public: using TSignature = Ret(Args...) noexcept(IsNoexcept); private: union TErasedCallable { const void* Functor; void (*Function)(); }; using TProxy = Ret (*)(TErasedCallable callable, Args...); // Making this a lambda inside TFunctionRef ctor caused: // "error: cannot compile this forwarded non-trivially copyable parameter yet" // on clang-win-i686-release. // // Using correct noexcept specifiers here (noexcept(IsNoexcept)) caused miscompilation on clang: // https://github.com/llvm/llvm-project/issues/55280. template static Ret InvokeErasedFunctor(TErasedCallable callable, Args... args) { auto& ref = *static_cast*>(callable.Functor); return static_cast(std::invoke(ref, std::forward(args)...)); } template static Ret InvokeErasedFunction(TErasedCallable callable, Args... args) { auto* function = reinterpret_cast(callable.Function); return static_cast(std::invoke(function, std::forward(args)...)); } template static constexpr bool IsInvocableUsing = std::conditional_t< IsNoexcept, std::is_nothrow_invocable_r, std::is_invocable_r>::value; // clang-format off template static constexpr bool IsSuitableFunctor = IsInvocableUsing && !std::is_function_v && !std::is_same_v, TFunctionRef>; template static constexpr bool IsSuitableFunction = IsInvocableUsing && std::is_function_v; // clang-format on public: // Function ref should not be default constructible. // While the function ref can have empty state (for example, Proxy_ == nullptr), // It does not make sense in common usage cases. TFunctionRef() = delete; // Construct function ref from a functor. template >> TFunctionRef(Functor&& functor) noexcept : Callable_{ .Functor = std::addressof(functor), } , Proxy_{InvokeErasedFunctor} { } // Construct function ref from a function pointer. template >> TFunctionRef(Function* function) noexcept : Callable_{ .Function = reinterpret_cast(function), } , Proxy_{InvokeErasedFunction} { } // Copy ctors & assignment. // Just copy pointers. TFunctionRef(const TFunctionRef& rhs) noexcept = default; TFunctionRef& operator=(const TFunctionRef& rhs) noexcept = default; Ret operator()(Args... args) const noexcept(IsNoexcept) { return Proxy_(Callable_, std::forward(args)...); } private: TErasedCallable Callable_; TProxy Proxy_ = nullptr; }; namespace NPrivate { template ::TSignature> struct TIsNothrowInvocable; template struct TIsNothrowInvocable { static constexpr bool IsNoexcept = std::is_nothrow_invocable_r_v; using TSignature = Ret(Args...) noexcept(IsNoexcept); }; template struct TCallableTraitsWithNoexcept { using TSignature = typename TIsNothrowInvocable::TSignature; }; } // namespace NPrivate template TFunctionRef(Callable&&) -> TFunctionRef::TSignature>; template TFunctionRef(Function*) -> TFunctionRef;