diff options
author | 2015-05-06 07:40:25 -0700 | |
---|---|---|
committer | 2015-05-06 07:40:26 -0700 | |
commit | 0d992db1cf5003340409ed02993266004cc0a57c (patch) | |
tree | 86155d0c358e1fd5b8832846af244851bb93d4a1 /src/core/SkFunction.h | |
parent | 444987fea8e6be1ff89ecce14ef0d934e7d3eb04 (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.h | 104 |
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 |