functional.hpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /*************************************************************************
  2. *
  3. * Copyright 2021 Realm Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. **************************************************************************/
  18. #pragma once
  19. #include <functional>
  20. #include <memory>
  21. #include <type_traits>
  22. #include "realm/util/assert.hpp"
  23. #include "realm/util/type_traits.hpp"
  24. namespace realm::util {
  25. template <typename Function>
  26. class UniqueFunction;
  27. /**
  28. * A `UniqueFunction` is a move-only, type-erased functor object similar to `std::function`.
  29. * It is useful in situations where a functor cannot be wrapped in `std::function` objects because
  30. * it is incapable of being copied. Often this happens with C++14 or later lambdas which capture a
  31. * `std::unique_ptr` by move. The interface of `UniqueFunction` is nearly identical to
  32. * `std::function`, except that it is not copyable.
  33. */
  34. template <typename RetType, typename... Args>
  35. class UniqueFunction<RetType(Args...)> {
  36. private:
  37. // `TagTypeBase` is used as a base for the `TagType` type, to prevent it from being an
  38. // aggregate.
  39. struct TagTypeBase {
  40. protected:
  41. TagTypeBase() = default;
  42. };
  43. // `TagType` is used as a placeholder type in parameter lists for `enable_if` clauses. They
  44. // have to be real parameters, not template parameters, due to MSVC limitations.
  45. class TagType : TagTypeBase {
  46. TagType() = default;
  47. friend UniqueFunction;
  48. };
  49. public:
  50. using result_type = RetType;
  51. ~UniqueFunction() noexcept = default;
  52. UniqueFunction() = default;
  53. UniqueFunction(const UniqueFunction&) = delete;
  54. UniqueFunction& operator=(const UniqueFunction&) = delete;
  55. UniqueFunction(UniqueFunction&&) noexcept = default;
  56. UniqueFunction& operator=(UniqueFunction&&) noexcept = default;
  57. void swap(UniqueFunction& that) noexcept
  58. {
  59. using std::swap;
  60. swap(this->impl, that.impl);
  61. }
  62. friend void swap(UniqueFunction& a, UniqueFunction& b) noexcept
  63. {
  64. a.swap(b);
  65. }
  66. template <typename Functor>
  67. /* implicit */
  68. UniqueFunction(
  69. Functor&& functor,
  70. // The remaining arguments here are only for SFINAE purposes to enable this ctor when our
  71. // requirements are met. They must be concrete parameters not template parameters to work
  72. // around bugs in some compilers that we presently use. We may be able to revisit this
  73. // design after toolchain upgrades for C++17.
  74. std::enable_if_t<std::is_invocable_r<RetType, Functor, Args...>::value, TagType> = make_tag(),
  75. std::enable_if_t<std::is_move_constructible<Functor>::value, TagType> = make_tag(),
  76. std::enable_if_t<!std::is_same<std::decay_t<Functor>, UniqueFunction>::value, TagType> = make_tag())
  77. : impl(make_impl(std::forward<Functor>(functor)))
  78. {
  79. }
  80. UniqueFunction(std::nullptr_t) noexcept {}
  81. RetType operator()(Args... args) const
  82. {
  83. REALM_ASSERT(static_cast<bool>(*this));
  84. return impl->call(std::forward<Args>(args)...);
  85. }
  86. explicit operator bool() const noexcept
  87. {
  88. return static_cast<bool>(this->impl);
  89. }
  90. // Needed to make `std::is_convertible<mongo::UniqueFunction<...>, std::function<...>>` be
  91. // `std::false_type`. `mongo::UniqueFunction` objects are not convertible to any kind of
  92. // `std::function` object, since the latter requires a copy constructor, which the former does
  93. // not provide. If you see a compiler error which references this line, you have tried to
  94. // assign a `UniqueFunction` object to a `std::function` object which is impossible -- please
  95. // check your variables and function signatures.
  96. //
  97. // NOTE: This is not quite able to disable all `std::function` conversions on MSVC, at this
  98. // time.
  99. template <typename Signature>
  100. operator std::function<Signature>() const = delete;
  101. private:
  102. // The `TagType` type cannot be constructed as a default function-parameter in Clang. So we use
  103. // a static member function that initializes that default parameter.
  104. static TagType make_tag()
  105. {
  106. return {};
  107. }
  108. struct Impl {
  109. virtual ~Impl() noexcept = default;
  110. virtual RetType call(Args&&... args) = 0;
  111. };
  112. // These overload helpers are needed to squelch problems in the `T ()` -> `void ()` case.
  113. template <typename Functor>
  114. static void call_regular_void(const std::true_type is_void, Functor& f, Args&&... args)
  115. {
  116. // The result of this call is not cast to void, to help preserve detection of
  117. // `[[nodiscard]]` violations.
  118. static_cast<void>(is_void);
  119. f(std::forward<Args>(args)...);
  120. }
  121. template <typename Functor>
  122. static RetType call_regular_void(const std::false_type is_not_void, Functor& f, Args&&... args)
  123. {
  124. static_cast<void>(is_not_void);
  125. return f(std::forward<Args>(args)...);
  126. }
  127. template <typename Functor>
  128. static auto make_impl(Functor&& functor)
  129. {
  130. struct SpecificImpl : Impl {
  131. explicit SpecificImpl(Functor&& func)
  132. : f(std::forward<Functor>(func))
  133. {
  134. }
  135. RetType call(Args&&... args) override
  136. {
  137. return call_regular_void(std::is_void<RetType>(), f, std::forward<Args>(args)...);
  138. }
  139. std::decay_t<Functor> f;
  140. };
  141. return std::make_unique<SpecificImpl>(std::forward<Functor>(functor));
  142. }
  143. std::unique_ptr<Impl> impl;
  144. };
  145. /**
  146. * Helper to pattern-match the signatures for all combinations of const and l-value-qualifed member
  147. * function pointers. We don't currently support r-value-qualified call operators.
  148. */
  149. template <typename>
  150. struct UFDeductionHelper {
  151. };
  152. template <typename Class, typename Ret, typename... Args>
  153. struct UFDeductionHelper<Ret (Class::*)(Args...)> : TypeIdentity<Ret(Args...)> {
  154. };
  155. template <typename Class, typename Ret, typename... Args>
  156. struct UFDeductionHelper<Ret (Class::*)(Args...)&> : TypeIdentity<Ret(Args...)> {
  157. };
  158. template <typename Class, typename Ret, typename... Args>
  159. struct UFDeductionHelper<Ret (Class::*)(Args...) const> : TypeIdentity<Ret(Args...)> {
  160. };
  161. template <typename Class, typename Ret, typename... Args>
  162. struct UFDeductionHelper<Ret (Class::*)(Args...) const&> : TypeIdentity<Ret(Args...)> {
  163. };
  164. /**
  165. * Deduction guides for UniqueFunction<Sig> that pluck the signature off of function pointers and
  166. * non-overloaded, non-generic function objects such as lambdas that don't use `auto` arguments.
  167. */
  168. template <typename Ret, typename... Args>
  169. UniqueFunction(Ret (*)(Args...)) -> UniqueFunction<Ret(Args...)>;
  170. template <typename T, typename Sig = typename UFDeductionHelper<decltype(&T::operator())>::type>
  171. UniqueFunction(T) -> UniqueFunction<Sig>;
  172. template <typename Signature>
  173. bool operator==(const UniqueFunction<Signature>& lhs, std::nullptr_t) noexcept
  174. {
  175. return !lhs;
  176. }
  177. template <typename Signature>
  178. bool operator!=(const UniqueFunction<Signature>& lhs, std::nullptr_t) noexcept
  179. {
  180. return static_cast<bool>(lhs);
  181. }
  182. template <typename Signature>
  183. bool operator==(std::nullptr_t, const UniqueFunction<Signature>& rhs) noexcept
  184. {
  185. return !rhs;
  186. }
  187. template <typename Signature>
  188. bool operator!=(std::nullptr_t, const UniqueFunction<Signature>& rhs) noexcept
  189. {
  190. return static_cast<bool>(rhs);
  191. }
  192. } // namespace realm::util