• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkColor.h"
12 #include "SkRefCnt.h"
13 #include "SkTArray.h"
14 
15 #include <string.h>
16 
17 struct SkCurve;
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 
isDerivedFromType54         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;
GetType()67     static const Type* GetType() {
68         static Type gType{ "SkReflected", nullptr, nullptr };
69         RegisterOnce(&gType);
70         return &gType;
71     }
72 
isOfType(const Type * t)73     bool isOfType(const Type* t) const {
74         const Type* thisType = this->getType();
75         return thisType == t || thisType->isDerivedFrom(t);
76     }
77 
CreateInstance(const char * name)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:
RegisterOnce(Type * type)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, etc...), as well as helper methods for entering the
132  * scope of 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:
~SkFieldVisitor()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     virtual void visit(const char*, SkPoint&) = 0;
156     virtual void visit(const char*, SkColor4f&) = 0;
157 
158     // Accommodation for enums, where caller can supply a value <-> string map
159     struct EnumStringMapping {
160         int         fValue;
161         const char* fName;
162     };
163     virtual void visit(const char*, int&, const EnumStringMapping*, int count) = 0;
164 
165     // Specific virtual signature for SkCurve, to allow for heavily customized UI in SkGuiVisitor.
166     virtual void visit(const char* name, SkCurve& c);
167 
168     // Default visit function for structs with no special behavior. It is assumed that any such
169     // struct implements visitFields(SkFieldVisitor*) to recursively visit each of its fields.
170     template <typename T>
visit(const char * name,T & value)171     void visit(const char* name, T& value) {
172         this->enterObject(name);
173         value.visitFields(this);
174         this->exitObject();
175     }
176 
177     // Specialization for SkTArrays. In conjunction with the enterArray/exitArray virtuals, this
178     // allows visitors to resize an array (for deserialization), and apply a single edit operation
179     // (remove or move a single element). Each element of the array is visited as normal.
180     template <typename T, bool MEM_MOVE>
visit(const char * name,SkTArray<T,MEM_MOVE> & arr)181     void visit(const char* name, SkTArray<T, MEM_MOVE>& arr) {
182         arr.resize_back(this->enterArray(name, arr.count()));
183         for (int i = 0; i < arr.count(); ++i) {
184             this->visit(nullptr, arr[i]);
185         }
186         this->exitArray().apply(arr);
187     }
188 
189     // Specialization for sk_sp pointers to types derived from SkReflected. Those types are known
190     // to implement visitFields. This allows the visitor to modify the contents of the object, or
191     // even replace it with an entirely new object. The virtual function uses SkReflected as a
192     // common type, but uses SkReflected::Type to communicate the required base-class. In this way,
193     // the new object can be verified to match the type of the original (templated) pointer.
194     template <typename T>
visit(const char * name,sk_sp<T> & obj)195     void visit(const char* name, sk_sp<T>& obj) {
196         this->enterObject(name);
197 
198         sk_sp<SkReflected> newObj = obj;
199         this->visit(newObj, T::GetType());
200         if (newObj != obj) {
201             if (!newObj || newObj->isOfType(T::GetType())) {
202                 obj.reset(static_cast<T*>(newObj.release()));
203             } else {
204                 obj.reset();
205             }
206         }
207 
208         if (obj) {
209             obj->visitFields(this);
210         }
211         this->exitObject();
212     }
213 
214 protected:
215     // Helper struct to allow exitArray to specify a single operation performed on the array.
216     struct ArrayEdit {
217         enum class Verb {
218             kNone,
219             kRemove,
220             kMoveForward,
221         };
222 
223         Verb fVerb = Verb::kNone;
224         int fIndex = 0;
225 
226         template <typename T, bool MEM_MOVE>
applyArrayEdit227         void apply(SkTArray<T, MEM_MOVE>& arr) const {
228             switch (fVerb) {
229             case Verb::kNone:
230                 break;
231             case Verb::kRemove:
232                 for (int i = fIndex; i < arr.count() - 1; ++i) {
233                     arr[i] = arr[i + 1];
234                 }
235                 arr.pop_back();
236                 break;
237             case Verb::kMoveForward:
238                 if (fIndex > 0 && fIndex < arr.count()) {
239                     std::swap(arr[fIndex - 1], arr[fIndex]);
240                 }
241                 break;
242             }
243         }
244     };
245 
EnumToString(int value,const EnumStringMapping * map,int count)246     static const char* EnumToString(int value, const EnumStringMapping* map, int count) {
247         for (int i = 0; i < count; ++i) {
248             if (map[i].fValue == value) {
249                 return map[i].fName;
250             }
251         }
252         return nullptr;
253     }
StringToEnum(const char * str,const EnumStringMapping * map,int count)254     static int StringToEnum(const char* str, const EnumStringMapping* map, int count) {
255         for (int i = 0; i < count; ++i) {
256             if (0 == strcmp(str, map[i].fName)) {
257                 return map[i].fValue;
258             }
259         }
260         return -1;
261     }
262 
263     virtual void enterObject(const char* name) = 0;
264     virtual void exitObject() = 0;
265 
266     virtual int enterArray(const char* name, int oldCount) = 0;
267     virtual ArrayEdit exitArray() = 0;
268 
269     virtual void visit(sk_sp<SkReflected>&, const SkReflected::Type* baseType) = 0;
270 };
271 
272 #endif // SkReflected_DEFINED
273