diff options
Diffstat (limited to 'gdb/gdbsupport/function-view.h')
-rw-r--r-- | gdb/gdbsupport/function-view.h | 323 |
1 files changed, 0 insertions, 323 deletions
diff --git a/gdb/gdbsupport/function-view.h b/gdb/gdbsupport/function-view.h deleted file mode 100644 index 4e205b8..0000000 --- a/gdb/gdbsupport/function-view.h +++ /dev/null @@ -1,323 +0,0 @@ -/* 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 |