| /* |
| * Copyright 2020 The WebRTC Project Authors. All rights reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #ifndef RTC_BASE_UNTYPED_FUNCTION_H_ |
| #define RTC_BASE_UNTYPED_FUNCTION_H_ |
| |
| #include <cstddef> |
| #include <cstring> |
| #include <memory> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "rtc_base/system/assume.h" |
| |
| namespace webrtc { |
| namespace webrtc_function_impl { |
| |
| using FunVoid = void(); |
| |
| // Inline storage size is this many machine words. |
| enum : size_t { kInlineStorageWords = 4 }; |
| |
| union VoidUnion { |
| void* void_ptr; |
| FunVoid* fun_ptr; |
| typename std::aligned_storage<kInlineStorageWords * sizeof(uintptr_t)>::type |
| inline_storage; |
| }; |
| |
| // Returns the number of elements of the `inline_storage` array required to |
| // store an object of type T. |
| template <typename T> |
| constexpr size_t InlineStorageSize() { |
| // sizeof(T) / sizeof(uintptr_t), but rounded up. |
| return (sizeof(T) + sizeof(uintptr_t) - 1) / sizeof(uintptr_t); |
| } |
| |
| template <typename T> |
| struct CallHelpers; |
| template <typename RetT, typename... ArgT> |
| struct CallHelpers<RetT(ArgT...)> { |
| // Return type of the three helpers below. |
| using return_type = RetT; |
| // Complete function type of the three helpers below. |
| using function_type = RetT(VoidUnion*, ArgT...); |
| // Helper for calling the `void_ptr` case of VoidUnion. |
| template <typename F> |
| static RetT CallVoidPtr(VoidUnion* vu, ArgT... args) { |
| return (*static_cast<F*>(vu->void_ptr))(std::forward<ArgT>(args)...); |
| } |
| // Helper for calling the `fun_ptr` case of VoidUnion. |
| static RetT CallFunPtr(VoidUnion* vu, ArgT... args) { |
| return (reinterpret_cast<RetT (*)(ArgT...)>(vu->fun_ptr))( |
| std::forward<ArgT>(args)...); |
| } |
| // Helper for calling the `inline_storage` case of VoidUnion. |
| template <typename F> |
| static RetT CallInlineStorage(VoidUnion* vu, ArgT... args) { |
| return (*reinterpret_cast<F*>(&vu->inline_storage))( |
| std::forward<ArgT>(args)...); |
| } |
| }; |
| |
| } // namespace webrtc_function_impl |
| |
| // A class that holds (and owns) any callable. The same function call signature |
| // must be provided when constructing and calling the object. |
| // |
| // The point of not having the call signature as a class template parameter is |
| // to have one single concrete type for all signatures; this reduces binary |
| // size. |
| class UntypedFunction final { |
| public: |
| // Callables of at most this size can be stored inline, if they are trivial. |
| // (Useful in tests and benchmarks; avoid using this in production code.) |
| enum : size_t { |
| kInlineStorageSize = sizeof(webrtc_function_impl::VoidUnion::inline_storage) |
| }; |
| static_assert(kInlineStorageSize == |
| webrtc_function_impl::kInlineStorageWords * |
| sizeof(uintptr_t), |
| ""); |
| |
| // The *UntypedFunctionArgs structs are used to transfer arguments from |
| // PrepareArgs() to Create(). They are trivial, but may own heap allocations, |
| // so make sure to pass them to Create() exactly once! |
| // |
| // The point of doing Create(PrepareArgs(foo)) instead of just Create(foo) is |
| // to separate the code that has to be inlined (PrepareArgs) from the code |
| // that can be noninlined (Create); the *UntypedFunctionArgs types are |
| // designed to efficiently carry the required information from one to the |
| // other. |
| template <size_t N> |
| struct TrivialUntypedFunctionArgs { |
| static_assert(N >= 1, ""); |
| static_assert(N <= webrtc_function_impl::kInlineStorageWords, ""); |
| // We use an uintptr_t array here instead of std::aligned_storage, because |
| // the former can be efficiently passed in registers when using |
| // TrivialUntypedFunctionArgs as a function argument. (We can't do the same |
| // in VoidUnion, because std::aligned_storage but not uintptr_t can be |
| // legally reinterpret_casted to arbitrary types. |
| // TrivialUntypedFunctionArgs, on the other hand, only needs to handle |
| // placement new and memcpy.) |
| alignas(std::max_align_t) uintptr_t inline_storage[N]; |
| webrtc_function_impl::FunVoid* call; |
| }; |
| struct NontrivialUntypedFunctionArgs { |
| void* void_ptr; |
| webrtc_function_impl::FunVoid* call; |
| void (*del)(webrtc_function_impl::VoidUnion*); |
| }; |
| struct FunctionPointerUntypedFunctionArgs { |
| webrtc_function_impl::FunVoid* fun_ptr; |
| webrtc_function_impl::FunVoid* call; |
| }; |
| |
| // Create function for lambdas and other callables that are trivial and small; |
| // it accepts every type of argument except those noted in its enable_if call. |
| template < |
| typename Signature, |
| typename F, |
| typename F_deref = typename std::remove_reference<F>::type, |
| typename std::enable_if< |
| // Not for function pointers; we have another overload for that below. |
| !std::is_function< |
| typename std::remove_pointer<F_deref>::type>::value && |
| |
| // Not for nullptr; we have a constructor for that below. |
| !std::is_same<std::nullptr_t, |
| typename std::remove_cv<F>::type>::value && |
| |
| // Not for UntypedFunction objects; use move construction or |
| // assignment. |
| !std::is_same<UntypedFunction, |
| typename std::remove_cv<F_deref>::type>::value && |
| |
| // Only for trivial callables that will fit in inline storage. |
| std::is_trivially_move_constructible<F_deref>::value && |
| std::is_trivially_destructible<F_deref>::value && |
| sizeof(F_deref) <= kInlineStorageSize>::type* = nullptr, |
| size_t InlineSize = webrtc_function_impl::InlineStorageSize<F_deref>()> |
| static TrivialUntypedFunctionArgs<InlineSize> PrepareArgs(F&& f) { |
| // The callable is trivial and small enough, so we just store its bytes |
| // in the inline storage. |
| TrivialUntypedFunctionArgs<InlineSize> args; |
| new (&args.inline_storage) F_deref(std::forward<F>(f)); |
| args.call = reinterpret_cast<webrtc_function_impl::FunVoid*>( |
| webrtc_function_impl::CallHelpers< |
| Signature>::template CallInlineStorage<F_deref>); |
| return args; |
| } |
| template <size_t InlineSize> |
| static UntypedFunction Create(TrivialUntypedFunctionArgs<InlineSize> args) { |
| webrtc_function_impl::VoidUnion vu; |
| std::memcpy(&vu.inline_storage, args.inline_storage, |
| sizeof(args.inline_storage)); |
| return UntypedFunction(vu, args.call, nullptr); |
| } |
| |
| // Create function for lambdas and other callables that are nontrivial or |
| // large; it accepts every type of argument except those noted in its |
| // enable_if call. |
| template <typename Signature, |
| typename F, |
| typename F_deref = typename std::remove_reference<F>::type, |
| typename std::enable_if< |
| // Not for function pointers; we have another overload for that |
| // below. |
| !std::is_function< |
| typename std::remove_pointer<F_deref>::type>::value && |
| |
| // Not for nullptr; we have a constructor for that below. |
| !std::is_same<std::nullptr_t, |
| typename std::remove_cv<F>::type>::value && |
| |
| // Not for UntypedFunction objects; use move construction or |
| // assignment. |
| !std::is_same<UntypedFunction, |
| typename std::remove_cv<F_deref>::type>::value && |
| |
| // Only for nontrivial callables, or callables that won't fit in |
| // inline storage. |
| !(std::is_trivially_move_constructible<F_deref>::value && |
| std::is_trivially_destructible<F_deref>::value && |
| sizeof(F_deref) <= kInlineStorageSize)>::type* = nullptr> |
| static NontrivialUntypedFunctionArgs PrepareArgs(F&& f) { |
| // The callable is either nontrivial or too large, so we can't keep it |
| // in the inline storage; use the heap instead. |
| NontrivialUntypedFunctionArgs args; |
| args.void_ptr = new F_deref(std::forward<F>(f)); |
| args.call = reinterpret_cast<webrtc_function_impl::FunVoid*>( |
| webrtc_function_impl::CallHelpers<Signature>::template CallVoidPtr< |
| F_deref>); |
| args.del = static_cast<void (*)(webrtc_function_impl::VoidUnion*)>( |
| [](webrtc_function_impl::VoidUnion* vu) { |
| // Assuming that this pointer isn't null allows the |
| // compiler to eliminate a null check in the (inlined) |
| // delete operation. |
| RTC_ASSUME(vu->void_ptr != nullptr); |
| delete reinterpret_cast<F_deref*>(vu->void_ptr); |
| }); |
| return args; |
| } |
| static UntypedFunction Create(NontrivialUntypedFunctionArgs args) { |
| webrtc_function_impl::VoidUnion vu; |
| vu.void_ptr = args.void_ptr; |
| return UntypedFunction(vu, args.call, args.del); |
| } |
| |
| // Create function that accepts function pointers. If the argument is null, |
| // the result is an empty UntypedFunction. |
| template <typename Signature> |
| static FunctionPointerUntypedFunctionArgs PrepareArgs(Signature* f) { |
| FunctionPointerUntypedFunctionArgs args; |
| args.fun_ptr = reinterpret_cast<webrtc_function_impl::FunVoid*>(f); |
| args.call = reinterpret_cast<webrtc_function_impl::FunVoid*>( |
| webrtc_function_impl::CallHelpers<Signature>::CallFunPtr); |
| return args; |
| } |
| static UntypedFunction Create(FunctionPointerUntypedFunctionArgs args) { |
| webrtc_function_impl::VoidUnion vu; |
| vu.fun_ptr = args.fun_ptr; |
| return UntypedFunction(vu, args.fun_ptr == nullptr ? nullptr : args.call, |
| nullptr); |
| } |
| |
| // Prepares arguments and creates an UntypedFunction in one go. |
| template <typename Signature, typename F> |
| static UntypedFunction Create(F&& f) { |
| return Create(PrepareArgs<Signature>(std::forward<F>(f))); |
| } |
| |
| // Default constructor. Creates an empty UntypedFunction. |
| UntypedFunction() : call_(nullptr), delete_(nullptr) {} |
| |
| // Nullptr constructor and assignment. Creates an empty UntypedFunction. |
| UntypedFunction(std::nullptr_t) // NOLINT(runtime/explicit) |
| : call_(nullptr), delete_(nullptr) {} |
| UntypedFunction& operator=(std::nullptr_t) { |
| call_ = nullptr; |
| if (delete_) { |
| delete_(&f_); |
| delete_ = nullptr; |
| } |
| return *this; |
| } |
| |
| // Not copyable. |
| UntypedFunction(const UntypedFunction&) = delete; |
| UntypedFunction& operator=(const UntypedFunction&) = delete; |
| |
| // Move construction and assignment. |
| UntypedFunction(UntypedFunction&& other) |
| : f_(other.f_), call_(other.call_), delete_(other.delete_) { |
| other.delete_ = nullptr; |
| } |
| UntypedFunction& operator=(UntypedFunction&& other) { |
| if (delete_) { |
| delete_(&f_); |
| } |
| f_ = other.f_; |
| call_ = other.call_; |
| delete_ = other.delete_; |
| other.delete_ = nullptr; |
| return *this; |
| } |
| |
| ~UntypedFunction() { |
| if (delete_) { |
| delete_(&f_); |
| } |
| } |
| |
| friend void swap(UntypedFunction& a, UntypedFunction& b) { |
| using std::swap; |
| swap(a.f_, b.f_); |
| swap(a.call_, b.call_); |
| swap(a.delete_, b.delete_); |
| } |
| |
| // Returns true if we have a function, false if we don't (i.e., we're null). |
| explicit operator bool() const { return call_ != nullptr; } |
| |
| template <typename Signature, typename... ArgT> |
| typename webrtc_function_impl::CallHelpers<Signature>::return_type Call( |
| ArgT&&... args) { |
| return reinterpret_cast< |
| typename webrtc_function_impl::CallHelpers<Signature>::function_type*>( |
| call_)(&f_, std::forward<ArgT>(args)...); |
| } |
| |
| // Returns true iff we don't need to call a destructor. This is guaranteed |
| // to hold for a moved-from object. |
| bool IsTriviallyDestructible() { return delete_ == nullptr; } |
| |
| private: |
| UntypedFunction(webrtc_function_impl::VoidUnion f, |
| webrtc_function_impl::FunVoid* call, |
| void (*del)(webrtc_function_impl::VoidUnion*)) |
| : f_(f), call_(call), delete_(del) {} |
| |
| // The callable thing, or a pointer to it. |
| webrtc_function_impl::VoidUnion f_; |
| |
| // Pointer to a dispatch function that knows the type of the callable thing |
| // that's stored in f_, and how to call it. An UntypedFunction object is empty |
| // (null) iff call_ is null. |
| webrtc_function_impl::FunVoid* call_; |
| |
| // Pointer to a function that knows how to delete the callable thing that's |
| // stored in f_. Null if `f_` is trivially deletable. |
| void (*delete_)(webrtc_function_impl::VoidUnion*); |
| }; |
| |
| } // namespace webrtc |
| |
| #endif // RTC_BASE_UNTYPED_FUNCTION_H_ |