blob: 67fcd5965328d090084bfbc583213d59dcdfadcd [file] [log] [blame]
/*
* 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 <memory>
#include <type_traits>
#include <utility>
#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 <typename T>
struct CallHelpers;
template <typename RetT, typename... ArgT>
struct CallHelpers<RetT(ArgT...)> {
using return_type = RetT;
template <typename F>
static RetT CallVoidPtr(VoidUnion* vu, ArgT... args) {
return (*static_cast<F*>(vu->void_ptr))(std::forward<ArgT>(args)...);
}
static RetT CallFunPtr(VoidUnion* vu, ArgT... args) {
return (reinterpret_cast<RetT (*)(ArgT...)>(vu->fun_ptr))(
std::forward<ArgT>(args)...);
}
template <typename F>
static RetT CallInlineStorage(VoidUnion* vu, ArgT... args) {
return (*reinterpret_cast<F*>(&vu->inline_storage))(
std::forward<ArgT>(args)...);
}
static RetT DoCall(FunVoid* f, VoidUnion* vu, ArgT... args) {
return reinterpret_cast<RetT (*)(VoidUnion*, ArgT...)>(f)(
vu, 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:
// 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<typename std::remove_pointer<
typename std::remove_reference<F>::type>::type>::value &&
// Not for nullptr; we have another overload for that below.
!std::is_same<std::nullptr_t,
typename std::remove_cv<F>::type>::value &&
// Not for UntypedFunction objects; we have another overload for that.
!std::is_same<UntypedFunction,
typename std::remove_cv<typename std::remove_reference<
F>::type>::type>::value>::type* = nullptr>
static UntypedFunction Create(F&& f) {
using F_deref = typename std::remove_reference<F>::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<F_deref>::value &&
std::is_trivially_destructible<F_deref>::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>(f));
return UntypedFunction(
vu,
reinterpret_cast<webrtc_function_impl::FunVoid*>(
webrtc_function_impl::CallHelpers<
Signature>::template CallInlineStorage<F_deref>),
nullptr);
} else {
// The callable is either nontrivial or too large, so we can't keep it
// in the inline storage; use the heap instead.
return UntypedFunction(
webrtc_function_impl::VoidUnion{.void_ptr =
new F_deref(std::forward<F>(f))},
reinterpret_cast<webrtc_function_impl::FunVoid*>(
webrtc_function_impl::CallHelpers<
Signature>::template CallVoidPtr<F_deref>),
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);
}));
}
}
// Create function that accepts function pointers. If the argument is null,
// the result is an empty UntypedFunction.
template <typename Signature>
static UntypedFunction Create(Signature* f) {
return UntypedFunction(
reinterpret_cast<webrtc_function_impl::FunVoid*>(f),
f ? reinterpret_cast<webrtc_function_impl::FunVoid*>(
webrtc_function_impl::CallHelpers<Signature>::CallFunPtr)
: nullptr,
nullptr);
}
// Default constructor. Creates an empty UntypedFunction.
UntypedFunction() : call_(nullptr), delete_(nullptr) {}
// 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) {
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 webrtc_function_impl::CallHelpers<Signature>::DoCall(
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_FUNCTION_H_