| /* Copyright (C) 2017-2020 Free Software Foundation, Inc. | |
| This file is part of GDB. | |
| This program is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
| #ifndef COMMON_FUNCTION_VIEW_H | |
| #define COMMON_FUNCTION_VIEW_H | |
| /* function_view is a polymorphic type-erasing wrapper class that | |
| encapsulates a non-owning reference to arbitrary callable objects. | |
| A way to put it is that function_view is to std::function like | |
| std::string_view is to std::string. While std::function stores a | |
| type-erased callable object internally, function_view holds a | |
| type-erased reference to an external callable object. | |
| This is meant to be used as callback type of a function that: | |
| #1 - Takes a callback as parameter. | |
| #2 - Wants to support arbitrary callable objects as callback type | |
| (e.g., stateful function objects, lambda closures, free | |
| functions). | |
| #3 - Does not store the callback anywhere; instead the function | |
| just calls the callback directly or forwards it to some | |
| other function that calls it. | |
| #4 - Can't be, or we don't want it to be, a template function | |
| with the callable type as template parameter. For example, | |
| when the callback is a parameter of a virtual member | |
| function, or when putting the function template in a header | |
| would expose too much implementation detail. | |
| Note that the C-style "function pointer" + "void *data" callback | |
| parameter idiom fails requirement #2 above. Please don't add new | |
| uses of that idiom. I.e., something like this wouldn't work; | |
| typedef bool (iterate_over_foos_cb) (foo *f, void *user_data), | |
| void iterate_over_foos (iterate_over_foos_cb *callback, void *user_data); | |
| foo *find_foo_by_type (int type) | |
| { | |
| foo *found = nullptr; | |
| iterate_over_foos ([&] (foo *f, void *data) | |
| { | |
| if (foo->type == type) | |
| { | |
| found = foo; | |
| return true; // stop iterating | |
| } | |
| return false; // continue iterating | |
| }, NULL); | |
| return found; | |
| } | |
| The above wouldn't compile, because lambdas with captures can't be | |
| implicitly converted to a function pointer (because a capture means | |
| some context data must be passed to the lambda somehow). | |
| C++11 gave us std::function as type-erased wrapper around arbitrary | |
| callables, however, std::function is not an ideal fit for transient | |
| callbacks such as the use case above. For this use case, which is | |
| quite pervasive, a function_view is a better choice, because while | |
| function_view is light and does not require any heap allocation, | |
| std::function is a heavy-weight object with value semantics that | |
| generally requires a heap allocation on construction/assignment of | |
| the target callable. In addition, while it is possible to use | |
| std::function in such a way that avoids most of the overhead by | |
| making sure to only construct it with callables of types that fit | |
| std::function's small object optimization, such as function | |
| pointers and std::reference_wrapper callables, that is quite | |
| inconvenient in practice, because restricting to free-function | |
| callables would imply no state/capture/closure, which we need in | |
| most cases, and std::reference_wrapper implies remembering to use | |
| std::ref/std::cref where the callable is constructed, with the | |
| added inconvenience that std::ref/std::cref have deleted rvalue-ref | |
| overloads, meaning you can't use unnamed/temporary lambdas with | |
| them. | |
| Note that because function_view is a non-owning view of a callable, | |
| care must be taken to ensure that the callable outlives the | |
| function_view that calls it. This is not really a problem for the | |
| use case function_view is intended for, such as passing a temporary | |
| function object / lambda to a function that accepts a callback, | |
| because in those cases, the temporary is guaranteed to be live | |
| until the called function returns. | |
| Calling a function_view with no associated target is undefined, | |
| unlike with std::function, which throws std::bad_function_call. | |
| This is by design, to avoid the otherwise necessary NULL check in | |
| function_view::operator(). | |
| Since function_view objects are small (a pair of pointers), they | |
| should generally be passed around by value. | |
| Usage: | |
| Given this function that accepts a callback: | |
| void | |
| iterate_over_foos (gdb::function_view<void (foo *)> callback) | |
| { | |
| for (auto &foo : foos) | |
| callback (&foo); | |
| } | |
| you can call it like this, passing a lambda as callback: | |
| iterate_over_foos ([&] (foo *f) | |
| { | |
| process_one_foo (f); | |
| }); | |
| or like this, passing a function object as callback: | |
| struct function_object | |
| { | |
| void operator() (foo *f) | |
| { | |
| if (s->check ()) | |
| process_one_foo (f); | |
| } | |
| // some state | |
| state *s; | |
| }; | |
| state mystate; | |
| function_object matcher {&mystate}; | |
| iterate_over_foos (matcher); | |
| or like this, passing a function pointer as callback: | |
| iterate_over_foos (process_one_foo); | |
| You can find unit tests covering the whole API in | |
| unittests/function-view-selftests.c. */ | |
| namespace gdb { | |
| namespace fv_detail { | |
| /* Bits shared by all function_view instantiations that do not depend | |
| on the template parameters. */ | |
| /* Storage for the erased callable. This is a union in order to be | |
| able to save both a function object (data) pointer or a function | |
| pointer without triggering undefined behavior. */ | |
| union erased_callable | |
| { | |
| /* For function objects. */ | |
| void *data; | |
| /* For function pointers. */ | |
| void (*fn) (); | |
| }; | |
| } /* namespace fv_detail */ | |
| /* Use partial specialization to get access to the callable's | |
| signature. */ | |
| template<class Signature> | |
| struct function_view; | |
| template<typename Res, typename... Args> | |
| class function_view<Res (Args...)> | |
| { | |
| template<typename From, typename To> | |
| using CompatibleReturnType | |
| = Or<std::is_void<To>, | |
| std::is_same<From, To>, | |
| std::is_convertible<From, To>>; | |
| /* True if Func can be called with Args, and either the result is | |
| Res, convertible to Res or Res is void. */ | |
| template<typename Callable, | |
| typename Res2 = typename std::result_of<Callable &(Args...)>::type> | |
| struct IsCompatibleCallable : CompatibleReturnType<Res2, Res> | |
| {}; | |
| /* True if Callable is a function_view. Used to avoid hijacking the | |
| copy ctor. */ | |
| template <typename Callable> | |
| struct IsFunctionView | |
| : std::is_same<function_view, typename std::decay<Callable>::type> | |
| {}; | |
| public: | |
| /* NULL by default. */ | |
| constexpr function_view () noexcept | |
| : m_erased_callable {}, | |
| m_invoker {} | |
| {} | |
| /* Default copy/assignment is fine. */ | |
| function_view (const function_view &) = default; | |
| function_view &operator= (const function_view &) = default; | |
| /* This is the main entry point. Use SFINAE to avoid hijacking the | |
| copy constructor and to ensure that the target type is | |
| compatible. */ | |
| template | |
| <typename Callable, | |
| typename = Requires<Not<IsFunctionView<Callable>>>, | |
| typename = Requires<IsCompatibleCallable<Callable>>> | |
| function_view (Callable &&callable) noexcept | |
| { | |
| bind (callable); | |
| } | |
| /* Construct a NULL function_view. */ | |
| constexpr function_view (std::nullptr_t) noexcept | |
| : m_erased_callable {}, | |
| m_invoker {} | |
| {} | |
| /* Clear a function_view. */ | |
| function_view &operator= (std::nullptr_t) noexcept | |
| { | |
| m_invoker = nullptr; | |
| return *this; | |
| } | |
| /* Return true if the wrapper has a target, false otherwise. Note | |
| we check M_INVOKER instead of M_ERASED_CALLABLE because we don't | |
| know which member of the union is active right now. */ | |
| constexpr explicit operator bool () const noexcept | |
| { return m_invoker != nullptr; } | |
| /* Call the callable. */ | |
| Res operator () (Args... args) const | |
| { return m_invoker (m_erased_callable, std::forward<Args> (args)...); } | |
| private: | |
| /* Bind this function_view to a compatible function object | |
| reference. */ | |
| template <typename Callable> | |
| void bind (Callable &callable) noexcept | |
| { | |
| m_erased_callable.data = (void *) std::addressof (callable); | |
| m_invoker = [] (fv_detail::erased_callable ecall, Args... args) | |
| noexcept (noexcept (callable (std::forward<Args> (args)...))) -> Res | |
| { | |
| auto &restored_callable = *static_cast<Callable *> (ecall.data); | |
| /* The explicit cast to Res avoids a compile error when Res is | |
| void and the callable returns non-void. */ | |
| return (Res) restored_callable (std::forward<Args> (args)...); | |
| }; | |
| } | |
| /* Bind this function_view to a compatible function pointer. | |
| Making this a separate function allows avoiding one indirection, | |
| by storing the function pointer directly in the storage, instead | |
| of a pointer to pointer. erased_callable is then a union in | |
| order to avoid storing a function pointer as a data pointer here, | |
| which would be undefined. */ | |
| template<class Res2, typename... Args2> | |
| void bind (Res2 (*fn) (Args2...)) noexcept | |
| { | |
| m_erased_callable.fn = reinterpret_cast<void (*) ()> (fn); | |
| m_invoker = [] (fv_detail::erased_callable ecall, Args... args) | |
| noexcept (noexcept (fn (std::forward<Args> (args)...))) -> Res | |
| { | |
| auto restored_fn = reinterpret_cast<Res2 (*) (Args2...)> (ecall.fn); | |
| /* The explicit cast to Res avoids a compile error when Res is | |
| void and the callable returns non-void. */ | |
| return (Res) restored_fn (std::forward<Args> (args)...); | |
| }; | |
| } | |
| /* Storage for the erased callable. */ | |
| fv_detail::erased_callable m_erased_callable; | |
| /* The invoker. This is set to a capture-less lambda by one of the | |
| 'bind' overloads. The lambda restores the right type of the | |
| callable (which is passed as first argument), and forwards the | |
| args. */ | |
| Res (*m_invoker) (fv_detail::erased_callable, Args...); | |
| }; | |
| /* Allow comparison with NULL. Defer the work to the in-class | |
| operator bool implementation. */ | |
| template<typename Res, typename... Args> | |
| constexpr inline bool | |
| operator== (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept | |
| { return !static_cast<bool> (f); } | |
| template<typename Res, typename... Args> | |
| constexpr inline bool | |
| operator== (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept | |
| { return !static_cast<bool> (f); } | |
| template<typename Res, typename... Args> | |
| constexpr inline bool | |
| operator!= (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept | |
| { return static_cast<bool> (f); } | |
| template<typename Res, typename... Args> | |
| constexpr inline bool | |
| operator!= (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept | |
| { return static_cast<bool> (f); } | |
| } /* namespace gdb */ | |
| #endif |