blob: c197fbc977bfe042ca3509a18b3ba58e94843ab4 [file] [log] [blame]
Brian Osman7c979f52019-02-12 13:27:51 -05001/*
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"
Brian Osman112aa2d2019-02-15 10:45:56 -050012#include "SkCurve.h"
Brian Osman7c979f52019-02-12 13:27:51 -050013#include "SkRefCnt.h"
14#include "SkString.h"
15#include "SkTArray.h"
16
17class SkFieldVisitor;
18class SkRandom;
19
Brian Osman23b96c02019-02-19 13:04:40 -050020/**
21 * Classes and macros for a lightweight reflection system.
22 *
23 * Classes that derive from SkReflected have several features:
24 * - Access to an SkReflected::Type instance, via static GetType() or virtual getType()
25 * The Type instance can be used to create additional instances (fFactory), get the name
26 * of the type, and answer queries of the form "is X derived from Y".
27 * - Given a string containing a type name, SkReflected can create an instance of that type.
28 * - SkReflected::VisitTypes can be used to enumerate all Types, or all Types derived from a
29 * particular base class.
30 *
31 * Together, this simplifies the implementation of serialization and other dynamic type factories.
32 *
33 * Finally, all SkReflected-derived types must implement visitFields, which provides field-level
34 * reflection, in conjunction with SkFieldVisitor. See SkFieldVisitor, below.
35 *
36 * To create a new reflected class:
37 * - Derive the class (directly or indirectly) from SkReflected.
38 * - Ensure that the class can be default constructed.
39 * - In the public area of the class declaration, add REFLECTED(<ClassName>, <BaseClassName>).
40 * If the class is abstract, use REFLECTED_ABSTRACT(<ClassName>, <BaseClassName>) instead.
41 * - Add a one-time call to REGISTER_REFLECTED(<ClassName>) at initialization time.
42 * - Implement visitFields(), as described below.
43 */
Brian Osman7c979f52019-02-12 13:27:51 -050044class SkReflected : public SkRefCnt {
45public:
46 typedef sk_sp<SkReflected>(*Factory)();
47 struct Type {
48 const char* fName;
49 const Type* fBase;
50 Factory fFactory;
51
52 bool isDerivedFrom(const Type* t) const {
53 const Type* base = fBase;
54 while (base) {
55 if (base == t) {
56 return true;
57 }
58 base = base->fBase;
59 }
60 return false;
61 }
62 };
63
64 virtual const Type* getType() const = 0;
65 static const Type* GetType() {
66 static Type gType{ "SkReflected", nullptr, nullptr };
67 return &gType;
68 }
69
70 bool isOfType(const Type* t) const {
71 const Type* thisType = this->getType();
72 return thisType == t || thisType->isDerivedFrom(t);
73 }
74
75 static void Register(const Type* type) {
76 gTypes.push_back(type);
77 }
78
79 static sk_sp<SkReflected> CreateInstance(const char* name) {
80 for (const Type* type : gTypes) {
81 if (0 == strcmp(name, type->fName)) {
82 return type->fFactory();
83 }
84 }
85 return nullptr;
86 }
87
88 virtual void visitFields(SkFieldVisitor*) = 0;
89
90 static void VisitTypes(std::function<void(const Type*)> visitor,
91 const Type* baseType = nullptr);
92private:
93 static SkSTArray<16, const Type*, true> gTypes;
94};
95
96#define REFLECTED(TYPE, BASE) \
97 static sk_sp<SkReflected> CreateProc() { \
98 return sk_sp<SkReflected>(new TYPE()); \
99 } \
100 static const Type* GetType() { \
101 static Type gType{ #TYPE, BASE::GetType(), CreateProc }; \
102 return &gType; \
103 } \
104 const Type* getType() const override { return GetType(); }
105
106#define REFLECTED_ABSTRACT(TYPE, BASE) \
107 static const Type* GetType() { \
108 static Type gType{ #TYPE, BASE::GetType(), nullptr }; \
109 return &gType; \
110 } \
111 const Type* getType() const override { return GetType(); }
112
113#define REGISTER_REFLECTED(TYPE) SkReflected::Register(TYPE::GetType())
114
115///////////////////////////////////////////////////////////////////////////////
116
Brian Osman7c979f52019-02-12 13:27:51 -0500117struct SkPoint;
118
Brian Osman23b96c02019-02-19 13:04:40 -0500119/**
120 * SkFieldVisitor is an interface that can be implemented by any class to visit all fields of
121 * SkReflected types, and of types that implement the visitFields() function.
122 *
123 * Classes implementing the interface must supply implementations of virtual functions that visit
124 * basic types (float, int, bool, SkString, etc...), as well as helper methods for entering the
125 * scope of an object or array.
126 *
127 * All visit functions supply a field name, and a non-constant reference to an actual field.
128 * This allows visitors to serialize or deserialize collections of objects, or perform edits on
129 * existing objects.
130 *
131 * Classes that implement visitFields (typically derived from SkReflected) should simply call
132 * visit() for each of their fields, passing a (unique) field name, and the actual field. If your
133 * class has derived fields, it's best to only visit() the fields that you would serialize, then
134 * enforce any constraints afterwards.
135 *
136 * See SkParticleSerialization.h for example visitors that perform serialization to and from JSON.
137 */
Brian Osman7c979f52019-02-12 13:27:51 -0500138class SkFieldVisitor {
139public:
140 virtual ~SkFieldVisitor() {}
141
Brian Osman23b96c02019-02-19 13:04:40 -0500142 // Visit functions for primitive types, to be implemented by derived visitors.
Brian Osman2991cbe2019-02-19 10:45:56 -0500143 virtual void visit(const char*, float&) = 0;
144 virtual void visit(const char*, int&) = 0;
145 virtual void visit(const char*, bool&) = 0;
146 virtual void visit(const char*, SkString&) = 0;
Brian Osman7c979f52019-02-12 13:27:51 -0500147
Brian Osman2991cbe2019-02-19 10:45:56 -0500148 virtual void visit(const char*, SkPoint&) = 0;
149 virtual void visit(const char*, SkColor4f&) = 0;
Brian Osman7c979f52019-02-12 13:27:51 -0500150
Brian Osman23b96c02019-02-19 13:04:40 -0500151 // Specific virtual signature for SkCurve, to allow for heavily customized UI in SkGuiVisitor.
Brian Osman2991cbe2019-02-19 10:45:56 -0500152 virtual void visit(const char* name, SkCurve& c) {
Brian Osman112aa2d2019-02-15 10:45:56 -0500153 this->enterObject(name);
154 c.visitFields(this);
155 this->exitObject();
156 }
157
Brian Osman23b96c02019-02-19 13:04:40 -0500158 // Default visit function for structs with no special behavior. It is assumed that any such
159 // struct implements visitFields(SkFieldVisitor*) to recursively visit each of its fields.
Brian Osman7c979f52019-02-12 13:27:51 -0500160 template <typename T>
161 void visit(const char* name, T& value) {
162 this->enterObject(name);
163 value.visitFields(this);
164 this->exitObject();
165 }
166
Brian Osman23b96c02019-02-19 13:04:40 -0500167 // Specialization for SkTArrays. In conjunction with the enterArray/exitArray virtuals, this
168 // allows visitors to resize an array (for deserialization), and apply a single edit operation
169 // (remove or move a single element). Each element of the array is visited as normal.
Brian Osman5de7ea42019-02-14 13:23:51 -0500170 template <typename T, bool MEM_MOVE>
171 void visit(const char* name, SkTArray<T, MEM_MOVE>& arr) {
172 arr.resize_back(this->enterArray(name, arr.count()));
173 for (int i = 0; i < arr.count(); ++i) {
174 this->visit(nullptr, arr[i]);
Brian Osman7c979f52019-02-12 13:27:51 -0500175 }
Brian Osman5de7ea42019-02-14 13:23:51 -0500176 this->exitArray().apply(arr);
Brian Osman7c979f52019-02-12 13:27:51 -0500177 }
178
Brian Osman23b96c02019-02-19 13:04:40 -0500179 // Specialization for sk_sp pointers to types derived from SkReflected. Those types are known
180 // to implement visitFields. This allows the visitor to modify the contents of the object, or
181 // even replace it with an entirely new object. The virtual function uses SkReflected as a
182 // common type, but uses SkReflected::Type to communicate the required base-class. In this way,
183 // the new object can be verified to match the type of the original (templated) pointer.
Brian Osman7c979f52019-02-12 13:27:51 -0500184 template <typename T>
185 void visit(const char* name, sk_sp<T>& obj) {
186 this->enterObject(name);
187
188 sk_sp<SkReflected> newObj = obj;
189 this->visit(newObj, T::GetType());
190 if (newObj != obj) {
191 if (!newObj || newObj->isOfType(T::GetType())) {
192 obj.reset(static_cast<T*>(newObj.release()));
193 } else {
194 obj.reset();
195 }
196 }
197
198 if (obj) {
199 obj->visitFields(this);
200 }
201 this->exitObject();
202 }
203
204protected:
Brian Osman23b96c02019-02-19 13:04:40 -0500205 // Helper struct to allow exitArray to specify a single operation performed on the array.
Brian Osman5de7ea42019-02-14 13:23:51 -0500206 struct ArrayEdit {
207 enum class Verb {
208 kNone,
209 kRemove,
210 kMoveForward,
211 };
212
213 Verb fVerb = Verb::kNone;
214 int fIndex = 0;
215
216 template <typename T, bool MEM_MOVE>
217 void apply(SkTArray<T, MEM_MOVE>& arr) const {
218 switch (fVerb) {
219 case Verb::kNone:
220 break;
221 case Verb::kRemove:
222 for (int i = fIndex; i < arr.count() - 1; ++i) {
223 arr[i] = arr[i + 1];
224 }
225 arr.pop_back();
226 break;
227 case Verb::kMoveForward:
228 if (fIndex > 0 && fIndex < arr.count()) {
229 std::swap(arr[fIndex - 1], arr[fIndex]);
230 }
231 break;
232 }
233 }
234 };
235
Brian Osman7c979f52019-02-12 13:27:51 -0500236 virtual void enterObject(const char* name) = 0;
237 virtual void exitObject() = 0;
Brian Osman5de7ea42019-02-14 13:23:51 -0500238
239 virtual int enterArray(const char* name, int oldCount) = 0;
240 virtual ArrayEdit exitArray() = 0;
241
Brian Osman7c979f52019-02-12 13:27:51 -0500242 virtual void visit(sk_sp<SkReflected>&, const SkReflected::Type* baseType) = 0;
Brian Osman7c979f52019-02-12 13:27:51 -0500243};
244
245#endif // SkReflected_DEFINED