| /* | |
| * 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 <cstdint> | |
| #include <cstring> | |
| #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; | |
| // std::max_align_t satisfies alignment requirements for every type. | |
| alignas(std::max_align_t) char inline_storage[kInlineStorageWords * | |
| sizeof(uintptr_t)]; | |
| }; | |
| // 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_ |