function_ref.h 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. #pragma once
  2. #include <util/generic/function.h>
  3. #include <util/system/yassert.h>
  4. #include <functional>
  5. namespace NPrivate {
  6. template <typename Signature>
  7. struct TIsNoexcept;
  8. template <typename Ret, typename... Args>
  9. struct TIsNoexcept<Ret(Args...)> {
  10. static constexpr bool Value = false;
  11. };
  12. template <typename Ret, typename... Args>
  13. struct TIsNoexcept<Ret(Args...) noexcept> {
  14. static constexpr bool Value = true;
  15. };
  16. } // namespace NPrivate
  17. template <typename Signature, bool IsNoexcept = NPrivate::TIsNoexcept<Signature>::Value>
  18. class TFunctionRef;
  19. template <typename Ret, typename... Args, bool IsNoexcept>
  20. class TFunctionRef<Ret(Args...) noexcept(IsNoexcept), IsNoexcept> {
  21. public:
  22. using TSignature = Ret(Args...) noexcept(IsNoexcept);
  23. private:
  24. union TErasedCallable {
  25. const void* Functor;
  26. void (*Function)();
  27. };
  28. using TProxy = Ret (*)(TErasedCallable callable, Args...);
  29. // Making this a lambda inside TFunctionRef ctor caused:
  30. // "error: cannot compile this forwarded non-trivially copyable parameter yet"
  31. // on clang-win-i686-release.
  32. //
  33. // Using correct noexcept specifiers here (noexcept(IsNoexcept)) caused miscompilation on clang:
  34. // https://github.com/llvm/llvm-project/issues/55280.
  35. template <typename Functor>
  36. static Ret InvokeErasedFunctor(TErasedCallable callable, Args... args) {
  37. auto& ref = *static_cast<const std::remove_reference_t<Functor>*>(callable.Functor);
  38. return static_cast<Ret>(std::invoke(ref, std::forward<Args>(args)...));
  39. }
  40. template <typename Function>
  41. static Ret InvokeErasedFunction(TErasedCallable callable, Args... args) {
  42. auto* function = reinterpret_cast<Function*>(callable.Function);
  43. return static_cast<Ret>(std::invoke(function, std::forward<Args>(args)...));
  44. }
  45. template <class F>
  46. static constexpr bool IsInvocableUsing = std::conditional_t<
  47. IsNoexcept,
  48. std::is_nothrow_invocable_r<Ret, F, Args...>,
  49. std::is_invocable_r<Ret, F, Args...>>::value;
  50. // clang-format off
  51. template <class Callable>
  52. static constexpr bool IsSuitableFunctor =
  53. IsInvocableUsing<Callable>
  54. && !std::is_function_v<Callable>
  55. && !std::is_same_v<std::remove_cvref_t<Callable>, TFunctionRef>;
  56. template <class Callable>
  57. static constexpr bool IsSuitableFunction =
  58. IsInvocableUsing<Callable>
  59. && std::is_function_v<Callable>;
  60. // clang-format on
  61. public:
  62. // Function ref should not be default constructible.
  63. // While the function ref can have empty state (for example, Proxy_ == nullptr),
  64. // It does not make sense in common usage cases.
  65. TFunctionRef() = delete;
  66. // Construct function ref from a functor.
  67. template <typename Functor, typename = std::enable_if_t<IsSuitableFunctor<Functor>>>
  68. TFunctionRef(Functor&& functor) noexcept
  69. : Callable_{
  70. .Functor = std::addressof(functor),
  71. }
  72. , Proxy_{InvokeErasedFunctor<Functor>}
  73. {
  74. }
  75. // Construct function ref from a function pointer.
  76. template <typename Function, typename = std::enable_if_t<IsSuitableFunction<Function>>>
  77. TFunctionRef(Function* function) noexcept
  78. : Callable_{
  79. .Function = reinterpret_cast<void (*)()>(function),
  80. }
  81. , Proxy_{InvokeErasedFunction<Function>}
  82. {
  83. }
  84. // Copy ctors & assignment.
  85. // Just copy pointers.
  86. TFunctionRef(const TFunctionRef& rhs) noexcept = default;
  87. TFunctionRef& operator=(const TFunctionRef& rhs) noexcept = default;
  88. Ret operator()(Args... args) const noexcept(IsNoexcept) {
  89. return Proxy_(Callable_, std::forward<Args>(args)...);
  90. }
  91. private:
  92. TErasedCallable Callable_;
  93. TProxy Proxy_ = nullptr;
  94. };
  95. namespace NPrivate {
  96. template <typename Callable, typename Signature = typename TCallableTraits<Callable>::TSignature>
  97. struct TIsNothrowInvocable;
  98. template <typename Callable, typename Ret, typename... Args>
  99. struct TIsNothrowInvocable<Callable, Ret(Args...)> {
  100. static constexpr bool IsNoexcept = std::is_nothrow_invocable_r_v<Ret, Callable, Args...>;
  101. using TSignature = Ret(Args...) noexcept(IsNoexcept);
  102. };
  103. template <typename Callable>
  104. struct TCallableTraitsWithNoexcept {
  105. using TSignature = typename TIsNothrowInvocable<Callable>::TSignature;
  106. };
  107. } // namespace NPrivate
  108. template <typename Callable>
  109. TFunctionRef(Callable&&) -> TFunctionRef<typename NPrivate::TCallableTraitsWithNoexcept<Callable>::TSignature>;
  110. template <typename Function>
  111. TFunctionRef(Function*) -> TFunctionRef<Function>;