function-view.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. /* Copyright (C) 2017-2022 Free Software Foundation, Inc.
  2. This file is part of GDB.
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  13. #ifndef COMMON_FUNCTION_VIEW_H
  14. #define COMMON_FUNCTION_VIEW_H
  15. /* function_view is a polymorphic type-erasing wrapper class that
  16. encapsulates a non-owning reference to arbitrary callable objects.
  17. A way to put it is that function_view is to std::function like
  18. std::string_view is to std::string. While std::function stores a
  19. type-erased callable object internally, function_view holds a
  20. type-erased reference to an external callable object.
  21. This is meant to be used as callback type of a function that:
  22. #1 - Takes a callback as parameter.
  23. #2 - Wants to support arbitrary callable objects as callback type
  24. (e.g., stateful function objects, lambda closures, free
  25. functions).
  26. #3 - Does not store the callback anywhere; instead the function
  27. just calls the callback directly or forwards it to some
  28. other function that calls it.
  29. #4 - Can't be, or we don't want it to be, a template function
  30. with the callable type as template parameter. For example,
  31. when the callback is a parameter of a virtual member
  32. function, or when putting the function template in a header
  33. would expose too much implementation detail.
  34. Note that the C-style "function pointer" + "void *data" callback
  35. parameter idiom fails requirement #2 above. Please don't add new
  36. uses of that idiom. I.e., something like this wouldn't work;
  37. typedef bool (iterate_over_foos_cb) (foo *f, void *user_data),
  38. void iterate_over_foos (iterate_over_foos_cb *callback, void *user_data);
  39. foo *find_foo_by_type (int type)
  40. {
  41. foo *found = nullptr;
  42. iterate_over_foos ([&] (foo *f, void *data)
  43. {
  44. if (foo->type == type)
  45. {
  46. found = foo;
  47. return true; // stop iterating
  48. }
  49. return false; // continue iterating
  50. }, NULL);
  51. return found;
  52. }
  53. The above wouldn't compile, because lambdas with captures can't be
  54. implicitly converted to a function pointer (because a capture means
  55. some context data must be passed to the lambda somehow).
  56. C++11 gave us std::function as type-erased wrapper around arbitrary
  57. callables, however, std::function is not an ideal fit for transient
  58. callbacks such as the use case above. For this use case, which is
  59. quite pervasive, a function_view is a better choice, because while
  60. function_view is light and does not require any heap allocation,
  61. std::function is a heavy-weight object with value semantics that
  62. generally requires a heap allocation on construction/assignment of
  63. the target callable. In addition, while it is possible to use
  64. std::function in such a way that avoids most of the overhead by
  65. making sure to only construct it with callables of types that fit
  66. std::function's small object optimization, such as function
  67. pointers and std::reference_wrapper callables, that is quite
  68. inconvenient in practice, because restricting to free-function
  69. callables would imply no state/capture/closure, which we need in
  70. most cases, and std::reference_wrapper implies remembering to use
  71. std::ref/std::cref where the callable is constructed, with the
  72. added inconvenience that std::ref/std::cref have deleted rvalue-ref
  73. overloads, meaning you can't use unnamed/temporary lambdas with
  74. them.
  75. Note that because function_view is a non-owning view of a callable,
  76. care must be taken to ensure that the callable outlives the
  77. function_view that calls it. This is not really a problem for the
  78. use case function_view is intended for, such as passing a temporary
  79. function object / lambda to a function that accepts a callback,
  80. because in those cases, the temporary is guaranteed to be live
  81. until the called function returns.
  82. Calling a function_view with no associated target is undefined,
  83. unlike with std::function, which throws std::bad_function_call.
  84. This is by design, to avoid the otherwise necessary NULL check in
  85. function_view::operator().
  86. Since function_view objects are small (a pair of pointers), they
  87. should generally be passed around by value.
  88. Usage:
  89. Given this function that accepts a callback:
  90. void
  91. iterate_over_foos (gdb::function_view<void (foo *)> callback)
  92. {
  93. for (auto &foo : foos)
  94. callback (&foo);
  95. }
  96. you can call it like this, passing a lambda as callback:
  97. iterate_over_foos ([&] (foo *f)
  98. {
  99. process_one_foo (f);
  100. });
  101. or like this, passing a function object as callback:
  102. struct function_object
  103. {
  104. void operator() (foo *f)
  105. {
  106. if (s->check ())
  107. process_one_foo (f);
  108. }
  109. // some state
  110. state *s;
  111. };
  112. state mystate;
  113. function_object matcher {&mystate};
  114. iterate_over_foos (matcher);
  115. or like this, passing a function pointer as callback:
  116. iterate_over_foos (process_one_foo);
  117. You can find unit tests covering the whole API in
  118. unittests/function-view-selftests.c. */
  119. namespace gdb {
  120. namespace fv_detail {
  121. /* Bits shared by all function_view instantiations that do not depend
  122. on the template parameters. */
  123. /* Storage for the erased callable. This is a union in order to be
  124. able to save both a function object (data) pointer or a function
  125. pointer without triggering undefined behavior. */
  126. union erased_callable
  127. {
  128. /* For function objects. */
  129. void *data;
  130. /* For function pointers. */
  131. void (*fn) ();
  132. };
  133. } /* namespace fv_detail */
  134. /* Use partial specialization to get access to the callable's
  135. signature. */
  136. template<class Signature>
  137. struct function_view;
  138. template<typename Res, typename... Args>
  139. class function_view<Res (Args...)>
  140. {
  141. template<typename From, typename To>
  142. using CompatibleReturnType
  143. = Or<std::is_void<To>,
  144. std::is_same<From, To>,
  145. std::is_convertible<From, To>>;
  146. /* True if Func can be called with Args, and either the result is
  147. Res, convertible to Res or Res is void. */
  148. template<typename Callable,
  149. typename Res2 = typename std::result_of<Callable &(Args...)>::type>
  150. struct IsCompatibleCallable : CompatibleReturnType<Res2, Res>
  151. {};
  152. /* True if Callable is a function_view. Used to avoid hijacking the
  153. copy ctor. */
  154. template <typename Callable>
  155. struct IsFunctionView
  156. : std::is_same<function_view, typename std::decay<Callable>::type>
  157. {};
  158. public:
  159. /* NULL by default. */
  160. constexpr function_view () noexcept
  161. : m_erased_callable {},
  162. m_invoker {}
  163. {}
  164. /* Default copy/assignment is fine. */
  165. function_view (const function_view &) = default;
  166. function_view &operator= (const function_view &) = default;
  167. /* This is the main entry point. Use SFINAE to avoid hijacking the
  168. copy constructor and to ensure that the target type is
  169. compatible. */
  170. template
  171. <typename Callable,
  172. typename = Requires<Not<IsFunctionView<Callable>>>,
  173. typename = Requires<IsCompatibleCallable<Callable>>>
  174. function_view (Callable &&callable) noexcept
  175. {
  176. bind (callable);
  177. }
  178. /* Construct a NULL function_view. */
  179. constexpr function_view (std::nullptr_t) noexcept
  180. : m_erased_callable {},
  181. m_invoker {}
  182. {}
  183. /* Clear a function_view. */
  184. function_view &operator= (std::nullptr_t) noexcept
  185. {
  186. m_invoker = nullptr;
  187. return *this;
  188. }
  189. /* Return true if the wrapper has a target, false otherwise. Note
  190. we check M_INVOKER instead of M_ERASED_CALLABLE because we don't
  191. know which member of the union is active right now. */
  192. constexpr explicit operator bool () const noexcept
  193. { return m_invoker != nullptr; }
  194. /* Call the callable. */
  195. Res operator () (Args... args) const
  196. { return m_invoker (m_erased_callable, std::forward<Args> (args)...); }
  197. private:
  198. /* Bind this function_view to a compatible function object
  199. reference. */
  200. template <typename Callable>
  201. void bind (Callable &callable) noexcept
  202. {
  203. m_erased_callable.data = (void *) std::addressof (callable);
  204. m_invoker = [] (fv_detail::erased_callable ecall, Args... args)
  205. noexcept (noexcept (callable (std::forward<Args> (args)...))) -> Res
  206. {
  207. auto &restored_callable = *static_cast<Callable *> (ecall.data);
  208. /* The explicit cast to Res avoids a compile error when Res is
  209. void and the callable returns non-void. */
  210. return (Res) restored_callable (std::forward<Args> (args)...);
  211. };
  212. }
  213. /* Bind this function_view to a compatible function pointer.
  214. Making this a separate function allows avoiding one indirection,
  215. by storing the function pointer directly in the storage, instead
  216. of a pointer to pointer. erased_callable is then a union in
  217. order to avoid storing a function pointer as a data pointer here,
  218. which would be undefined. */
  219. template<class Res2, typename... Args2>
  220. void bind (Res2 (*fn) (Args2...)) noexcept
  221. {
  222. m_erased_callable.fn = reinterpret_cast<void (*) ()> (fn);
  223. m_invoker = [] (fv_detail::erased_callable ecall, Args... args)
  224. noexcept (noexcept (fn (std::forward<Args> (args)...))) -> Res
  225. {
  226. auto restored_fn = reinterpret_cast<Res2 (*) (Args2...)> (ecall.fn);
  227. /* The explicit cast to Res avoids a compile error when Res is
  228. void and the callable returns non-void. */
  229. return (Res) restored_fn (std::forward<Args> (args)...);
  230. };
  231. }
  232. /* Storage for the erased callable. */
  233. fv_detail::erased_callable m_erased_callable;
  234. /* The invoker. This is set to a capture-less lambda by one of the
  235. 'bind' overloads. The lambda restores the right type of the
  236. callable (which is passed as first argument), and forwards the
  237. args. */
  238. Res (*m_invoker) (fv_detail::erased_callable, Args...);
  239. };
  240. /* Allow comparison with NULL. Defer the work to the in-class
  241. operator bool implementation. */
  242. template<typename Res, typename... Args>
  243. constexpr inline bool
  244. operator== (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept
  245. { return !static_cast<bool> (f); }
  246. template<typename Res, typename... Args>
  247. constexpr inline bool
  248. operator== (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept
  249. { return !static_cast<bool> (f); }
  250. template<typename Res, typename... Args>
  251. constexpr inline bool
  252. operator!= (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept
  253. { return static_cast<bool> (f); }
  254. template<typename Res, typename... Args>
  255. constexpr inline bool
  256. operator!= (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept
  257. { return static_cast<bool> (f); }
  258. } /* namespace gdb */
  259. #endif