Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(137)

Side by Side Diff: src/core/SkFunction.h

Issue 1056673002: Make SkFunction copyable so it can go in containers. (Closed) Base URL: https://skia.googlesource.com/skia@master
Patch Set: ben Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tests/FunctionTest.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2015 Google Inc. 2 * Copyright 2015 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #ifndef SkFunction_DEFINED 8 #ifndef SkFunction_DEFINED
9 #define SkFunction_DEFINED 9 #define SkFunction_DEFINED
10 10
11 // TODO: document 11 // TODO: document, more pervasive move support in constructors, small-Fn optimiz ation
12 12
13 #include "SkTemplates.h"
13 #include "SkTypes.h" 14 #include "SkTypes.h"
14 #include "SkTLogic.h"
15 15
16 template <typename> class SkFunction; 16 template <typename> class SkFunction;
17 17
18 template <typename R, typename... Args> 18 template <typename R, typename... Args>
19 class SkFunction<R(Args...)> : SkNoncopyable { 19 class SkFunction<R(Args...)> {
20 public: 20 public:
21 SkFunction(R (*fn)(Args...)) : fVTable(GetFunctionPointerVTable()) { 21 SkFunction() {}
22 // We've been passed a function pointer. We'll just store it. 22
23 fFunction = reinterpret_cast<void*>(fn); 23 template <typename Fn>
24 SkFunction(const Fn& fn) : fFunction(SkNEW_ARGS(LambdaImpl<Fn>, (fn))) {}
25
26 SkFunction(R (*fn)(Args...)) : fFunction(SkNEW_ARGS(FnPtrImpl, (fn))) {}
27
28 SkFunction(const SkFunction& other) { *this = other; }
29 SkFunction& operator=(const SkFunction& other) {
30 if (this != &other) {
31 fFunction.reset(other.fFunction ? other.fFunction->clone() : nullptr );
32 }
33 return *this;
24 } 34 }
25 35
26 template <typename Fn> 36 R operator()(Args... args) const {
27 SkFunction(Fn fn, SK_WHEN_C((sizeof(Fn) > sizeof(void*)), void*) = nullptr) 37 SkASSERT(fFunction.get());
28 : fVTable(GetOutlineVTable<Fn>()) { 38 return fFunction->call(Forward(args)...);
29 // We've got a functor larger than a pointer. We've go to copy it onto the heap.
30 fFunction = SkNEW_ARGS(Fn, (Forward(fn)));
31 } 39 }
32 40
33 template <typename Fn>
34 SkFunction(Fn fn, SK_WHEN_C((sizeof(Fn) <= sizeof(void*)), void*) = nullptr)
35 : fVTable(GetInlineVTable<Fn>()) {
36 // We've got a functor that fits in a pointer. We copy it right inline.
37 fFunction = NULL; // Quiets a (spurious) warning that fFunction might b e uninitialized.
38 SkNEW_PLACEMENT_ARGS(&fFunction, Fn, (Forward(fn)));
39 }
40
41 ~SkFunction() { fVTable.fCleanUp(fFunction); }
42
43 R operator()(Args... args) { return fVTable.fCall(fFunction, Forward(args).. .); }
44
45 private: 41 private:
46 // ~= std::forward. This moves its argument if possible, falling back to a copy if not. 42 // ~= std::forward. This moves its argument if possible, falling back to a copy if not.
47 template <typename T> static T&& Forward(T& v) { return (T&&)v; } 43 template <typename T> static T&& Forward(T& v) { return (T&&)v; }
48 44
49 struct VTable { 45 struct Interface {
50 R (*fCall)(void*, Args...); 46 virtual ~Interface() {}
51 void (*fCleanUp)(void*); 47 virtual R call(Args...) const = 0;
48 virtual Interface* clone() const = 0;
52 }; 49 };
53 50
54 // Used when fFunction is a function pointer of type R(*)(Args...). 51 template <typename Fn>
55 static const VTable& GetFunctionPointerVTable() { 52 class LambdaImpl final : public Interface {
56 static const VTable vtable = { 53 public:
57 [](void* fn, Args... args) { 54 LambdaImpl(const Fn& fn) : fFn(fn) {}
58 return reinterpret_cast<R(*)(Args...)>(fn)(Forward(args)...);
59 },
60 [](void*) { /* Nothing to clean up for function pointers. */ }
61 };
62 return vtable;
63 }
64 55
65 // Used when fFunction is a pointer to a functor of type Fn on the heap (we own it). 56 R call(Args... args) const override { return fFn(Forward(args)...); }
66 template <typename Fn> 57 Interface* clone() const { return SkNEW_ARGS(LambdaImpl<Fn>, (fFn)); }
67 static const VTable& GetOutlineVTable() { 58 private:
68 static const VTable vtable = { 59 Fn fFn;
69 [](void* fn, Args... args) { return (*static_cast<Fn*>(fn))(Forward( args)...); }, 60 };
70 [](void* fn) { SkDELETE(static_cast<Fn*>(fn)); },
71 };
72 return vtable;
73 }
74 61
75 // Used when fFunction _is_ a functor of type Fn, not a pointer to the funct or. 62 class FnPtrImpl final : public Interface {
76 template <typename Fn> 63 public:
77 static const VTable& GetInlineVTable() { 64 FnPtrImpl(R (*fn)(Args...)) : fFn(fn) {}
78 static const VTable vtable = {
79 [](void* fn, Args... args) {
80 union { void** p; Fn* f; } pun = { &fn };
81 return (*pun.f)(Forward(args)...);
82 },
83 [](void* fn) {
84 union { void** p; Fn* f; } pun = { &fn };
85 (*pun.f).~Fn();
86 (void)(pun.f); // Otherwise, when ~Fn() is trivial, MSVC compl ains pun is unused.
87 }
88 };
89 return vtable;
90 }
91 65
66 R call(Args... args) const override { return fFn(Forward(args)...); }
67 Interface* clone() const { return SkNEW_ARGS(FnPtrImpl, (fFn)); }
68 private:
69 R (*fFn)(Args...);
70 };
92 71
93 void* fFunction; // A function pointer, a pointer to a functor, or an inlined functor. 72 SkAutoTDelete<Interface> fFunction;
94 const VTable& fVTable; // How to call, delete (and one day copy, move) fFun ction.
95 }; 73 };
96 74
97 // TODO:
98 // - is it worth moving fCall out of the VTable into SkFunction itself to avoi d the indirection?
99 // - make SkFunction copyable
100
101 #endif//SkFunction_DEFINED 75 #endif//SkFunction_DEFINED
OLDNEW
« no previous file with comments | « no previous file | tests/FunctionTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698