Chromium Code Reviews| Index: webrtc/base/function_view.h |
| diff --git a/webrtc/base/function_view.h b/webrtc/base/function_view.h |
| index 6a7c9109ca6d906ff53601799c65b208917200e4..861bccff3e09166020c65298b7caa74354f4a37f 100644 |
| --- a/webrtc/base/function_view.h |
| +++ b/webrtc/base/function_view.h |
| @@ -14,16 +14,27 @@ |
| #include <type_traits> |
| #include <utility> |
| +#include "webrtc/base/checks.h" |
| + |
| // Just like std::function, FunctionView will wrap any callable and hide its |
| // actual type, exposing only its signature. But unlike std::function, |
| // FunctionView doesn't own its callable---it just points to it. Thus, it's a |
| // good choice mainly as a function argument when the callable argument will |
| // not be called again once the function has returned. |
| // |
| -// TODO(kwiberg): FunctionView doesn't work with function pointers, just with |
| -// lambdas. It's trivial to work around this by wrapping the function pointer |
| -// in a stateless lambda, but it's tedious so it'd be nice to not have to do |
| -// it. |
| +// Its constructors are implicit, so that callers won't have to convert lambdas |
| +// and other callables to FunctionView<Blah(Blah, Blah)> explicitly. This is |
| +// safe because FunctionView is only a reference to the real callable. |
| +// |
| +// Example use: |
| +// |
| +// void SomeFunction(rtc::FunctionView<int(int)> index_transform); |
| +// ... |
| +// SomeFunction([](int i) { return 2 * i + 1; }); |
| +// |
| +// Note: FunctionView is tiny (essentially just two pointers) and trivially |
| +// copyable, so it's probably cheaper to pass it by value than by const |
| +// reference. |
| namespace rtc { |
| @@ -33,36 +44,85 @@ class FunctionView; // Undefined. |
| template <typename RetT, typename... ArgT> |
| class FunctionView<RetT(ArgT...)> final { |
| public: |
| - // This constructor is implicit, so that callers won't have to convert |
| - // lambdas and other callables to FunctionView<Blah(Blah, Blah)> explicitly. |
| - // This is safe because FunctionView is only a reference to the real |
| - // callable. |
| - // |
| - // We jump through some template metaprogramming hoops to ensure that this |
| - // constructor does *not* accept FunctionView arguments. That way, copy |
| - // construction, assignment, swap etc. will all do the obvious thing (because |
| - // they use the implicitly-declared copy constructor and copy assignment), |
| - // and we will never get a FunctionView object that points to another |
| - // FunctionView. |
| - template <typename F, |
| - typename std::enable_if<!std::is_same< |
| - FunctionView, |
| - typename std::remove_cv<typename std::remove_reference< |
| - F>::type>::type>::value>::type* = nullptr> |
| + // Constructor for lambdas and other callables; it accepts every type of |
| + // argument except those noted in its enable_if call. |
| + template < |
| + typename F, |
| + typename std::enable_if< |
| + // Not for function pointers; we have another constructor for that |
| + // below. |
| + !std::is_function<typename std::remove_pointer< |
| + typename std::remove_reference<F>::type>::type>::value && |
| + |
| + // Not for nullptr; we have another constructor for that below. |
| + !std::is_same<std::nullptr_t, |
| + typename std::remove_cv<F>::type>::value && |
| + |
| + // Not for FunctionView objects; we have another constructor for that |
| + // (the implicitly declared copy constructor). |
| + !std::is_same<FunctionView, |
| + typename std::remove_cv<typename std::remove_reference< |
| + F>::type>::type>::value>::type* = nullptr> |
| + FunctionView(F&& f) |
| + : call_(CallVoidPtr<typename std::remove_reference<F>::type>) { |
| + f_.void_ptr = &f; |
| + } |
| + |
| + // Constructor that accepts function pointers. If the argument is null, the |
| + // result is an empty FunctionView. |
| + template < |
| + typename F, |
| + typename std::enable_if<std::is_function<typename std::remove_pointer< |
| + typename std::remove_reference<F>::type>::type>::value>::type* = |
| + nullptr> |
| FunctionView(F&& f) |
| - : f_(&f), call_(Call<typename std::remove_reference<F>::type>) {} |
| + : call_(f ? CallFunPtr<typename std::remove_pointer<F>::type> : nullptr) { |
|
kwiberg-webrtc
2016/09/29 07:52:40
We incur the overhead of testing for null both if
ossu
2016/10/03 11:28:12
Won't this all be inlined and then the compiler ca
kwiberg-webrtc
2016/10/03 11:46:31
That's... probably right. Why didn't I think of th
|
| + f_.fun_ptr = reinterpret_cast<void (*)()>(f); |
| + } |
| + |
| + // Constructor that accepts nullptr. It creates an empty FunctionView. |
| + template <typename F, |
| + typename std::enable_if<std::is_same< |
| + std::nullptr_t, |
| + typename std::remove_cv<F>::type>::value>::type* = nullptr> |
| + FunctionView(F&& f) : call_(nullptr) {} |
| + |
| + // Default constructor. Creates an empty FunctionView. |
| + FunctionView() : call_(nullptr) {} |
| RetT operator()(ArgT... args) const { |
| + RTC_DCHECK(call_); |
| return call_(f_, std::forward<ArgT>(args)...); |
| } |
| + // Returns true if we have a function, false if we don't (i.e., we're null). |
| + explicit operator bool() const { return !!call_; } |
| + |
| private: |
| + union VoidUnion { |
| + void* void_ptr; |
| + void (*fun_ptr)(); |
| + }; |
| + |
| + template <typename F> |
| + static RetT CallVoidPtr(VoidUnion vu, ArgT... args) { |
| + return (*static_cast<F*>(vu.void_ptr))(std::forward<ArgT>(args)...); |
| + } |
| template <typename F> |
| - static RetT Call(void* f, ArgT... args) { |
| - return (*static_cast<F*>(f))(std::forward<ArgT>(args)...); |
| + static RetT CallFunPtr(VoidUnion vu, ArgT... args) { |
| + return (reinterpret_cast<typename std::add_pointer<F>::type>(vu.fun_ptr))( |
| + std::forward<ArgT>(args)...); |
| } |
| - void* f_; |
| - RetT (*call_)(void* f, ArgT... args); |
| + |
| + // A pointer to the callable thing, with type information erased. It's a |
| + // union because we have to use separate types depending on if the callable |
| + // thing is a function pointer or something else. |
| + 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. A FunctionView object is empty |
| + // (null) iff call_ is null. |
| + RetT (*call_)(VoidUnion, ArgT...); |
| }; |
| } // namespace rtc |