blob: f6661aeedc8e2dc04e21e1282fbed472543983b9 [file] [log] [blame]
// Copyright 2017 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#pragma once
#include "android/base/TypeTraits.h"
#include <utility>
namespace android {
namespace base {
//
// FunctionView<> - a class that stores a view of a callable object, similar
// to StringView for strings.
//
// Emulator needs to pass around lots of callbacks. The standard way of doing it
// is via std::function<> class, and it usually works OK. But there are some
// noticeable drawbacks:
//
// 1. std::function<> in most STLs performs heap allocation for all callables
// bigger than a single poiner to a function.
// 2. std::function<> goes through at least two pointers + a vptr call to call
// the stored function.
// 3. std::function<> copies the passed object inside at least once; this also
// means it can't work with non-copyable functors.
//
// FunctionView is an alternative way of passing functors around. Instead of
// storing a copy of the functor inside, it follows the path of StringView and
// merely captures a pointer to the object to call. This allows for a simple,
// fast and lightweight wrapper design; it also dictates the limitations:
//
// 1. FunctionView<> stores a pointer to outside functor. That functor _must_
// outlive the view.
// 2. FunctionView<> has two calls through a function pointer in its call
// operator. That's still better than std::function<>, but slower compared
// to a raw function pointer call with a "void* opaque" context parameter.
//
// Limitation #1 dictates the best use case: a function parameter type for some
// generic callback which doesn't get stored inside an object field but only
// gets called in this call. E.g.:
//
// void someLongOperation(FunctionView<void(int progress)> onProgress) {
// firstStep(onProgress);
// ...
// onProgress(50);
// ...
// lastStep(onProgress);
// onProgress(100);
// }
//
// In this code std::function<> is an overkill as the whole use of |onProgresss|
// callback is scoped and easy to track. An alternative design - making it a
// template parameter (template <class Callback> ... (Callback onProgress))
// forces one to put someLongOperation() + some private functions into the
// header. FunctionView<> is the choice then.
//
// NOTE: Beware of passing temporary functions via FunctionView<>! Temporaries
// live until the end of full expression (usually till the next semicolon), and
// having a FunctionView<> that refers to a dangling pointer is a bug that's
// hard to debug. E.g.:
// FunctionView<...> v = [](){}; // this is fine
// FunctionView<...> v = std::function<...>([](){}); // this will kill you
//
// NOTE2: FunctionView<> has an empty state, like a normal function pointer or
// std::function<>. Don't forget to check it before calling, or use some empty
// function as a sentinel value. Calling empty view will crash your app.
template <class Signature>
class FunctionView;
template <class Ret, class... Args>
class FunctionView<Ret(Args...)> final {
public:
FunctionView() : mTypeErasedFunction() {}
template <class Callable,
class = android::base::enable_if_c<
is_callable_as<Callable, Ret(Args...)>::value &&
!std::is_same<FunctionView,
typename std::remove_reference<
Callable>::type>::value> >
FunctionView(Callable&& c)
: mTypeErasedFunction(
[](const FunctionView* self, Args... args) -> Ret {
// Generate a lambda that remembers the type of the passed
// |Callable|.
return (*reinterpret_cast<
typename std::remove_reference<Callable>::type*>(
self->mCallable))(std::forward<Args>(args)...);
}),
mCallable(reinterpret_cast<intptr_t>(&c)) {}
Ret operator()(Args... args) const {
return mTypeErasedFunction(this, std::forward<Args>(args)...);
}
operator bool() const { return mTypeErasedFunction != nullptr; }
bool empty() const { return !bool(*this); }
private:
using TypeErasedFunc = Ret(const FunctionView*, Args...);
TypeErasedFunc* mTypeErasedFunction;
intptr_t mCallable;
};
} // namespace base
} // namespace android