1 | /* |
2 | * Copyright 2019 Google LLC |
3 | * |
4 | * Use of this source code is governed by a BSD-style license that can be |
5 | * found in the LICENSE file. |
6 | */ |
7 | |
8 | #ifndef SkReflected_DEFINED |
9 | #define SkReflected_DEFINED |
10 | |
11 | #include "include/core/SkColor.h" |
12 | #include "include/core/SkRefCnt.h" |
13 | #include "include/private/SkTArray.h" |
14 | |
15 | #include <functional> // std::function |
16 | #include <string.h> |
17 | |
18 | class SkFieldVisitor; |
19 | struct SkPoint; |
20 | class SkString; |
21 | |
22 | /** |
23 | * Classes and macros for a lightweight reflection system. |
24 | * |
25 | * Classes that derive from SkReflected have several features: |
26 | * - Access to an SkReflected::Type instance, via static GetType() or virtual getType() |
27 | * The Type instance can be used to create additional instances (fFactory), get the name |
28 | * of the type, and answer queries of the form "is X derived from Y". |
29 | * - Given a string containing a type name, SkReflected can create an instance of that type. |
30 | * - SkReflected::VisitTypes can be used to enumerate all Types. |
31 | * |
32 | * Together, this simplifies the implementation of serialization and other dynamic type factories. |
33 | * |
34 | * Finally, all SkReflected-derived types must implement visitFields, which provides field-level |
35 | * reflection, in conjunction with SkFieldVisitor. See SkFieldVisitor, below. |
36 | * |
37 | * To create a new reflected class: |
38 | * - Derive the class (directly or indirectly) from SkReflected. |
39 | * - Ensure that the class can be default constructed. |
40 | * - In the public area of the class declaration, add REFLECTED(<ClassName>, <BaseClassName>). |
41 | * If the class is abstract, use REFLECTED_ABSTRACT(<ClassName>, <BaseClassName>) instead. |
42 | * - Add a one-time call to REGISTER_REFLECTED(<ClassName>) at initialization time. |
43 | * - Implement visitFields(), as described below. |
44 | */ |
45 | class SkReflected : public SkRefCnt { |
46 | public: |
47 | typedef sk_sp<SkReflected>(*Factory)(); |
48 | struct Type { |
49 | const char* fName; |
50 | const Type* fBase; |
51 | Factory fFactory; |
52 | bool fRegistered = false; |
53 | |
54 | bool isDerivedFrom(const Type* t) const { |
55 | const Type* base = fBase; |
56 | while (base) { |
57 | if (base == t) { |
58 | return true; |
59 | } |
60 | base = base->fBase; |
61 | } |
62 | return false; |
63 | } |
64 | }; |
65 | |
66 | virtual const Type* getType() const = 0; |
67 | static const Type* GetType() { |
68 | static Type gType{ "SkReflected" , nullptr, nullptr }; |
69 | RegisterOnce(&gType); |
70 | return &gType; |
71 | } |
72 | |
73 | bool isOfType(const Type* t) const { |
74 | const Type* thisType = this->getType(); |
75 | return thisType == t || thisType->isDerivedFrom(t); |
76 | } |
77 | |
78 | static sk_sp<SkReflected> CreateInstance(const char* name) { |
79 | for (const Type* type : gTypes) { |
80 | if (0 == strcmp(name, type->fName)) { |
81 | return type->fFactory(); |
82 | } |
83 | } |
84 | return nullptr; |
85 | } |
86 | |
87 | virtual void visitFields(SkFieldVisitor*) = 0; |
88 | |
89 | static void VisitTypes(std::function<void(const Type*)> visitor); |
90 | |
91 | protected: |
92 | static void RegisterOnce(Type* type) { |
93 | if (!type->fRegistered) { |
94 | gTypes.push_back(type); |
95 | type->fRegistered = true; |
96 | } |
97 | } |
98 | |
99 | private: |
100 | static SkSTArray<16, const Type*, true> gTypes; |
101 | }; |
102 | |
103 | #define REFLECTED(TYPE, BASE) \ |
104 | static sk_sp<SkReflected> CreateProc() { \ |
105 | return sk_sp<SkReflected>(new TYPE()); \ |
106 | } \ |
107 | static const Type* GetType() { \ |
108 | static Type gType{ #TYPE, BASE::GetType(), CreateProc }; \ |
109 | RegisterOnce(&gType); \ |
110 | return &gType; \ |
111 | } \ |
112 | const Type* getType() const override { return GetType(); } |
113 | |
114 | #define REFLECTED_ABSTRACT(TYPE, BASE) \ |
115 | static const Type* GetType() { \ |
116 | static Type gType{ #TYPE, BASE::GetType(), nullptr }; \ |
117 | RegisterOnce(&gType); \ |
118 | return &gType; \ |
119 | } \ |
120 | const Type* getType() const override { return GetType(); } |
121 | |
122 | #define REGISTER_REFLECTED(TYPE) TYPE::GetType() |
123 | |
124 | /////////////////////////////////////////////////////////////////////////////// |
125 | |
126 | /** |
127 | * SkFieldVisitor is an interface that can be implemented by any class to visit all fields of |
128 | * SkReflected types, and of types that implement the visitFields() function. |
129 | * |
130 | * Classes implementing the interface must supply implementations of virtual functions that visit |
131 | * basic types (float, int, bool, SkString), as well as helper methods for entering the scope of |
132 | * an object or array. |
133 | * |
134 | * All visit functions supply a field name, and a non-constant reference to an actual field. |
135 | * This allows visitors to serialize or deserialize collections of objects, or perform edits on |
136 | * existing objects. |
137 | * |
138 | * Classes that implement visitFields (typically derived from SkReflected) should simply call |
139 | * visit() for each of their fields, passing a (unique) field name, and the actual field. If your |
140 | * class has derived fields, it's best to only visit() the fields that you would serialize, then |
141 | * enforce any constraints afterwards. |
142 | * |
143 | * See SkParticleSerialization.h for example visitors that perform serialization to and from JSON. |
144 | */ |
145 | class SkFieldVisitor { |
146 | public: |
147 | virtual ~SkFieldVisitor() {} |
148 | |
149 | // Visit functions for primitive types, to be implemented by derived visitors. |
150 | virtual void visit(const char*, float&) = 0; |
151 | virtual void visit(const char*, int&) = 0; |
152 | virtual void visit(const char*, bool&) = 0; |
153 | virtual void visit(const char*, SkString&) = 0; |
154 | |
155 | // Specialization for SkTArrays. In conjunction with the enterArray/exitArray virtuals, this |
156 | // allows visitors to resize an array (for deserialization), and apply a single edit operation |
157 | // (remove a single element). Each element of the array is visited as normal. |
158 | template <typename T, bool MEM_MOVE> |
159 | void visit(const char* name, SkTArray<T, MEM_MOVE>& arr) { |
160 | arr.resize_back(this->enterArray(name, arr.count())); |
161 | for (int i = 0; i < arr.count(); ++i) { |
162 | this->visit(nullptr, arr[i]); |
163 | } |
164 | this->exitArray().apply(arr); |
165 | } |
166 | |
167 | // Specialization for sk_sp pointers to types derived from SkReflected. Those types are known |
168 | // to implement visitFields. This allows the visitor to modify the contents of the object, or |
169 | // even replace it with an entirely new object. The virtual function uses SkReflected as a |
170 | // common type, but uses SkReflected::Type to communicate the required base-class. In this way, |
171 | // the new object can be verified to match the type of the original (templated) pointer. |
172 | template <typename T> |
173 | void visit(const char* name, sk_sp<T>& obj) { |
174 | this->enterObject(name); |
175 | |
176 | sk_sp<SkReflected> newObj = obj; |
177 | this->visit(newObj, T::GetType()); |
178 | if (newObj != obj) { |
179 | if (!newObj || newObj->isOfType(T::GetType())) { |
180 | obj.reset(static_cast<T*>(newObj.release())); |
181 | } else { |
182 | obj.reset(); |
183 | } |
184 | } |
185 | |
186 | if (obj) { |
187 | obj->visitFields(this); |
188 | } |
189 | this->exitObject(); |
190 | } |
191 | |
192 | protected: |
193 | // Helper struct to allow exitArray to specify a single operation performed on the array. |
194 | struct ArrayEdit { |
195 | enum class Verb { |
196 | kNone, |
197 | kRemove, |
198 | }; |
199 | |
200 | Verb fVerb = Verb::kNone; |
201 | int fIndex = 0; |
202 | |
203 | template <typename T, bool MEM_MOVE> |
204 | void apply(SkTArray<T, MEM_MOVE>& arr) const { |
205 | switch (fVerb) { |
206 | case Verb::kNone: |
207 | break; |
208 | case Verb::kRemove: |
209 | for (int i = fIndex; i < arr.count() - 1; ++i) { |
210 | arr[i] = arr[i + 1]; |
211 | } |
212 | arr.pop_back(); |
213 | break; |
214 | } |
215 | } |
216 | }; |
217 | |
218 | private: |
219 | virtual void enterObject(const char* name) = 0; |
220 | virtual void exitObject() = 0; |
221 | |
222 | virtual int enterArray(const char* name, int oldCount) = 0; |
223 | virtual ArrayEdit exitArray() = 0; |
224 | |
225 | virtual void visit(sk_sp<SkReflected>&, const SkReflected::Type* baseType) = 0; |
226 | }; |
227 | |
228 | #endif // SkReflected_DEFINED |
229 | |