function_ref.h 4.1 KB

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