diff options
author | bbiff <bbiff@yandex-team.com> | 2022-08-24 16:44:16 +0300 |
---|---|---|
committer | bbiff <bbiff@yandex-team.com> | 2022-08-24 16:44:16 +0300 |
commit | 7eeb61811ba1588f20d70abeadc7b9308dd785dd (patch) | |
tree | 71e1f4bcfd0bef1563239e2bea85d8de0e5eb275 /util/generic/function_ref.h | |
parent | 653146b941fae8faaf0cf733e371816e7c69e7d2 (diff) | |
download | ydb-7eeb61811ba1588f20d70abeadc7b9308dd785dd.tar.gz |
added limit parameter
Diffstat (limited to 'util/generic/function_ref.h')
-rw-r--r-- | util/generic/function_ref.h | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/util/generic/function_ref.h b/util/generic/function_ref.h new file mode 100644 index 00000000000..c55de8694c9 --- /dev/null +++ b/util/generic/function_ref.h @@ -0,0 +1,122 @@ +#pragma once + +#include <util/generic/function.h> +#include <util/system/yassert.h> + +#include <functional> + +template <typename Signature> +class TFunctionRef; + +template <typename Ret, typename... Args, bool IsNoexcept> +class TFunctionRef<Ret(Args...) noexcept(IsNoexcept)> { +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 <typename Functor> + static Ret InvokeErasedFunctor(TErasedCallable callable, Args... args) { + auto& ref = *static_cast<const std::remove_reference_t<Functor>*>(callable.Functor); + return static_cast<Ret>(std::invoke(ref, std::forward<Args>(args)...)); + } + + template <typename Function> + static Ret InvokeErasedFunction(TErasedCallable callable, Args... args) { + auto* function = reinterpret_cast<Function*>(callable.Function); + return static_cast<Ret>(std::invoke(function, std::forward<Args>(args)...)); + } + + template <class F> + static constexpr bool IsInvocableUsing = std::conditional_t< + IsNoexcept, + std::is_nothrow_invocable_r<Ret, F, Args...>, + std::is_invocable_r<Ret, F, Args...>>::value; + + // clang-format off + template <class Callable> + static constexpr bool IsSuitableFunctor = + IsInvocableUsing<Callable> + && !std::is_function_v<Callable> + && !std::is_same_v<std::remove_cvref_t<Callable>, TFunctionRef>; + + template <class Callable> + static constexpr bool IsSuitableFunction = + IsInvocableUsing<Callable> + && std::is_function_v<Callable>; + // 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 <typename Functor, typename = std::enable_if_t<IsSuitableFunctor<Functor>>> + TFunctionRef(Functor&& functor) noexcept + : Callable_{ + .Functor = std::addressof(functor), + } + , Proxy_{InvokeErasedFunctor<Functor>} + { + } + + // Construct function ref from a function pointer. + template <typename Function, typename = std::enable_if_t<IsSuitableFunction<Function>>> + TFunctionRef(Function* function) noexcept + : Callable_{ + .Function = reinterpret_cast<void (*)()>(function), + } + , Proxy_{InvokeErasedFunction<Function>} + { + } + + // 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>(args)...); + } + +private: + TErasedCallable Callable_; + TProxy Proxy_ = nullptr; +}; + +namespace NPrivate { + + template <typename Callable, typename Signature = typename TCallableTraits<Callable>::TSignature> + struct TIsNothrowInvocable; + + template <typename Callable, typename Ret, typename... Args> + struct TIsNothrowInvocable<Callable, Ret(Args...)> { + static constexpr bool IsNoexcept = std::is_nothrow_invocable_r_v<Ret, Callable, Args...>; + using TSignature = Ret(Args...) noexcept(IsNoexcept); + }; + + template <typename Callable> + struct TCallableTraitsWithNoexcept { + using TSignature = typename TIsNothrowInvocable<Callable>::TSignature; + }; + +} // namespace NPrivate + +template <typename Callable> +TFunctionRef(Callable&&) -> TFunctionRef<typename NPrivate::TCallableTraitsWithNoexcept<Callable>::TSignature>; + +template <typename Function> +TFunctionRef(Function*) -> TFunctionRef<Function>; |