aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkFunction.h
diff options
context:
space:
mode:
authorGravatar mtklein <mtklein@chromium.org>2015-05-06 07:40:25 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-05-06 07:40:26 -0700
commit0d992db1cf5003340409ed02993266004cc0a57c (patch)
tree86155d0c358e1fd5b8832846af244851bb93d4a1 /src/core/SkFunction.h
parent444987fea8e6be1ff89ecce14ef0d934e7d3eb04 (diff)
Make SkFunction copyable so it can go in containers.
This totally overhauls the implementation to use ordinary inheritance-based type erasure. I give up for now getting my manual vtable shenanigans to work with MSVC. Still those same "expected ; before ), also expected ) before ;" errors. I added support for uninitialized SkFunctions and operator=(), because it was fairly straightforward with this implementation. The main downside here is that I've removed the inline implementation. All SkFunctions involve a heap allocation, even when just wrapping function pointers. BUG=skia: Review URL: https://codereview.chromium.org/1056673002
Diffstat (limited to 'src/core/SkFunction.h')
-rw-r--r--src/core/SkFunction.h104
1 files changed, 39 insertions, 65 deletions
diff --git a/src/core/SkFunction.h b/src/core/SkFunction.h
index 43438dd9e2..429c6f5ade 100644
--- a/src/core/SkFunction.h
+++ b/src/core/SkFunction.h
@@ -8,94 +8,68 @@
#ifndef SkFunction_DEFINED
#define SkFunction_DEFINED
-// TODO: document
+// TODO: document, more pervasive move support in constructors, small-Fn optimization
+#include "SkTemplates.h"
#include "SkTypes.h"
-#include "SkTLogic.h"
template <typename> class SkFunction;
template <typename R, typename... Args>
-class SkFunction<R(Args...)> : SkNoncopyable {
+class SkFunction<R(Args...)> {
public:
- SkFunction(R (*fn)(Args...)) : fVTable(GetFunctionPointerVTable()) {
- // We've been passed a function pointer. We'll just store it.
- fFunction = reinterpret_cast<void*>(fn);
- }
+ SkFunction() {}
template <typename Fn>
- SkFunction(Fn fn, SK_WHEN_C((sizeof(Fn) > sizeof(void*)), void*) = nullptr)
- : fVTable(GetOutlineVTable<Fn>()) {
- // We've got a functor larger than a pointer. We've go to copy it onto the heap.
- fFunction = SkNEW_ARGS(Fn, (Forward(fn)));
- }
+ SkFunction(const Fn& fn) : fFunction(SkNEW_ARGS(LambdaImpl<Fn>, (fn))) {}
- template <typename Fn>
- SkFunction(Fn fn, SK_WHEN_C((sizeof(Fn) <= sizeof(void*)), void*) = nullptr)
- : fVTable(GetInlineVTable<Fn>()) {
- // We've got a functor that fits in a pointer. We copy it right inline.
- fFunction = NULL; // Quiets a (spurious) warning that fFunction might be uninitialized.
- SkNEW_PLACEMENT_ARGS(&fFunction, Fn, (Forward(fn)));
- }
+ SkFunction(R (*fn)(Args...)) : fFunction(SkNEW_ARGS(FnPtrImpl, (fn))) {}
- ~SkFunction() { fVTable.fCleanUp(fFunction); }
+ SkFunction(const SkFunction& other) { *this = other; }
+ SkFunction& operator=(const SkFunction& other) {
+ if (this != &other) {
+ fFunction.reset(other.fFunction ? other.fFunction->clone() : nullptr);
+ }
+ return *this;
+ }
- R operator()(Args... args) { return fVTable.fCall(fFunction, Forward(args)...); }
+ R operator()(Args... args) const {
+ SkASSERT(fFunction.get());
+ return fFunction->call(Forward(args)...);
+ }
private:
// ~= std::forward. This moves its argument if possible, falling back to a copy if not.
template <typename T> static T&& Forward(T& v) { return (T&&)v; }
- struct VTable {
- R (*fCall)(void*, Args...);
- void (*fCleanUp)(void*);
+ struct Interface {
+ virtual ~Interface() {}
+ virtual R call(Args...) const = 0;
+ virtual Interface* clone() const = 0;
};
- // Used when fFunction is a function pointer of type R(*)(Args...).
- static const VTable& GetFunctionPointerVTable() {
- static const VTable vtable = {
- [](void* fn, Args... args) {
- return reinterpret_cast<R(*)(Args...)>(fn)(Forward(args)...);
- },
- [](void*) { /* Nothing to clean up for function pointers. */ }
- };
- return vtable;
- }
-
- // Used when fFunction is a pointer to a functor of type Fn on the heap (we own it).
template <typename Fn>
- static const VTable& GetOutlineVTable() {
- static const VTable vtable = {
- [](void* fn, Args... args) { return (*static_cast<Fn*>(fn))(Forward(args)...); },
- [](void* fn) { SkDELETE(static_cast<Fn*>(fn)); },
- };
- return vtable;
- }
+ class LambdaImpl final : public Interface {
+ public:
+ LambdaImpl(const Fn& fn) : fFn(fn) {}
+
+ R call(Args... args) const override { return fFn(Forward(args)...); }
+ Interface* clone() const { return SkNEW_ARGS(LambdaImpl<Fn>, (fFn)); }
+ private:
+ Fn fFn;
+ };
- // Used when fFunction _is_ a functor of type Fn, not a pointer to the functor.
- template <typename Fn>
- static const VTable& GetInlineVTable() {
- static const VTable vtable = {
- [](void* fn, Args... args) {
- union { void** p; Fn* f; } pun = { &fn };
- return (*pun.f)(Forward(args)...);
- },
- [](void* fn) {
- union { void** p; Fn* f; } pun = { &fn };
- (*pun.f).~Fn();
- (void)(pun.f); // Otherwise, when ~Fn() is trivial, MSVC complains pun is unused.
- }
- };
- return vtable;
- }
+ class FnPtrImpl final : public Interface {
+ public:
+ FnPtrImpl(R (*fn)(Args...)) : fFn(fn) {}
+ R call(Args... args) const override { return fFn(Forward(args)...); }
+ Interface* clone() const { return SkNEW_ARGS(FnPtrImpl, (fFn)); }
+ private:
+ R (*fFn)(Args...);
+ };
- void* fFunction; // A function pointer, a pointer to a functor, or an inlined functor.
- const VTable& fVTable; // How to call, delete (and one day copy, move) fFunction.
+ SkAutoTDelete<Interface> fFunction;
};
-// TODO:
-// - is it worth moving fCall out of the VTable into SkFunction itself to avoid the indirection?
-// - make SkFunction copyable
-
#endif//SkFunction_DEFINED