/* * 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_FUNCTION_H_ #define RTC_BASE_FUNCTION_H_ #include #include #include #include "rtc_base/system/assume.h" namespace webrtc { namespace webrtc_function_impl { using FunVoid = void(); union VoidUnion { void* void_ptr; FunVoid* fun_ptr; typename std::aligned_storage<16>::type inline_storage; }; template struct CallHelpers; template struct CallHelpers { // 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 static RetT CallVoidPtr(VoidUnion* vu, ArgT... args) { return (*static_cast(vu->void_ptr))(std::forward(args)...); } // Helper for calling the `fun_ptr` case of VoidUnion. static RetT CallFunPtr(VoidUnion* vu, ArgT... args) { return (reinterpret_cast(vu->fun_ptr))( std::forward(args)...); } // Helper for calling the `inline_storage` case of VoidUnion. template static RetT CallInlineStorage(VoidUnion* vu, ArgT... args) { return (*reinterpret_cast(&vu->inline_storage))( std::forward(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: // Create function for lambdas and other callables; it accepts every type of // argument except those noted in its enable_if call. template < typename Signature, typename F, typename std::enable_if< // Not for function pointers; we have another overload for that below. !std::is_function::type>::type>::value && // Not for nullptr; we have another overload for that below. !std::is_same::type>::value && // Not for UntypedFunction objects; we have another overload for that. !std::is_same::type>::type>::value>::type* = nullptr> static UntypedFunction Create(F&& f) { using F_deref = typename std::remove_reference::type; // TODO(C++17): Use `constexpr if` here. The compiler appears to do the // right thing anyway w.r.t. resolving the branch statically and // eliminating dead code, but it would be good for readability. if (std::is_trivially_move_constructible::value && std::is_trivially_destructible::value && sizeof(F_deref) <= sizeof(webrtc_function_impl::VoidUnion::inline_storage)) { // The callable is trivial and small enough, so we just store its bytes // in the inline storage. webrtc_function_impl::VoidUnion vu; new (&vu.inline_storage) F_deref(std::forward(f)); return UntypedFunction( vu, reinterpret_cast( webrtc_function_impl::CallHelpers< Signature>::template CallInlineStorage), nullptr); } else { // The callable is either nontrivial or too large, so we can't keep it // in the inline storage; use the heap instead. webrtc_function_impl::VoidUnion vu; vu.void_ptr = new F_deref(std::forward(f)); return UntypedFunction( vu, reinterpret_cast( webrtc_function_impl::CallHelpers< Signature>::template CallVoidPtr), static_cast( [](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(vu->void_ptr); })); } } // Create function that accepts function pointers. If the argument is null, // the result is an empty UntypedFunction. template static UntypedFunction Create(Signature* f) { webrtc_function_impl::VoidUnion vu; vu.fun_ptr = reinterpret_cast(f); return UntypedFunction( vu, f ? reinterpret_cast( webrtc_function_impl::CallHelpers::CallFunPtr) : nullptr, nullptr); } // 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 webrtc_function_impl::CallHelpers::return_type Call( ArgT&&... args) { return reinterpret_cast< typename webrtc_function_impl::CallHelpers::function_type*>( call_)(&f_, std::forward(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_FUNCTION_H_