blob: 104b4826ccc0a5bbd01e5d9f0d4676debce9d93c [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkColor.h"
12#include "include/core/SkRefCnt.h"
13#include "include/private/SkTArray.h"
Brian Osman7c979f52019-02-12 13:27:51 -050014
Mike Klein334a6422019-08-27 08:12:15 -050015#include <functional> // std::function
Brian Osmane5d532e2019-02-26 14:58:40 -050016#include <string.h>
17
Brian Osman7c979f52019-02-12 13:27:51 -050018class SkFieldVisitor;
Brian Osmand0c1bd42019-03-06 16:56:58 -050019struct SkPoint;
20class SkString;
Brian Osman7c979f52019-02-12 13:27:51 -050021
Brian Osman23b96c02019-02-19 13:04:40 -050022/**
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.
Brian Osmanb77d5022019-03-06 11:08:48 -050030 * - SkReflected::VisitTypes can be used to enumerate all Types.
Brian Osman23b96c02019-02-19 13:04:40 -050031 *
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 */
Brian Osman7c979f52019-02-12 13:27:51 -050045class SkReflected : public SkRefCnt {
46public:
47 typedef sk_sp<SkReflected>(*Factory)();
48 struct Type {
49 const char* fName;
50 const Type* fBase;
51 Factory fFactory;
Brian Osman93c47cc2019-03-06 15:00:36 -050052 bool fRegistered = false;
Brian Osman7c979f52019-02-12 13:27:51 -050053
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 };
Brian Osman93c47cc2019-03-06 15:00:36 -050069 RegisterOnce(&gType);
Brian Osman7c979f52019-02-12 13:27:51 -050070 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
Brian Osman7c979f52019-02-12 13:27:51 -050078 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
Brian Osmanb77d5022019-03-06 11:08:48 -050089 static void VisitTypes(std::function<void(const Type*)> visitor);
90
Brian Osman93c47cc2019-03-06 15:00:36 -050091protected:
92 static void RegisterOnce(Type* type) {
93 if (!type->fRegistered) {
94 gTypes.push_back(type);
95 type->fRegistered = true;
96 }
97 }
98
Brian Osman7c979f52019-02-12 13:27:51 -050099private:
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 }; \
Brian Osman93c47cc2019-03-06 15:00:36 -0500109 RegisterOnce(&gType); \
Brian Osman7c979f52019-02-12 13:27:51 -0500110 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 }; \
Brian Osman93c47cc2019-03-06 15:00:36 -0500117 RegisterOnce(&gType); \
Brian Osman7c979f52019-02-12 13:27:51 -0500118 return &gType; \
119 } \
120 const Type* getType() const override { return GetType(); }
121
Brian Osman93c47cc2019-03-06 15:00:36 -0500122#define REGISTER_REFLECTED(TYPE) TYPE::GetType()
Brian Osman7c979f52019-02-12 13:27:51 -0500123
124///////////////////////////////////////////////////////////////////////////////
125
Brian Osman23b96c02019-02-19 13:04:40 -0500126/**
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
Brian Osman9dac0d82019-12-02 16:52:51 -0500131 * basic types (float, int, bool, SkString), as well as helper methods for entering the scope of
132 * an object or array.
Brian Osman23b96c02019-02-19 13:04:40 -0500133 *
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 */
Brian Osman7c979f52019-02-12 13:27:51 -0500145class SkFieldVisitor {
146public:
147 virtual ~SkFieldVisitor() {}
148
Brian Osman23b96c02019-02-19 13:04:40 -0500149 // Visit functions for primitive types, to be implemented by derived visitors.
Brian Osman2991cbe2019-02-19 10:45:56 -0500150 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;
Brian Osman7c979f52019-02-12 13:27:51 -0500154
Brian Osman23b96c02019-02-19 13:04:40 -0500155 // 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
Brian Osman9dac0d82019-12-02 16:52:51 -0500157 // (remove a single element). Each element of the array is visited as normal.
Brian Osman5de7ea42019-02-14 13:23:51 -0500158 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]);
Brian Osman7c979f52019-02-12 13:27:51 -0500163 }
Brian Osman5de7ea42019-02-14 13:23:51 -0500164 this->exitArray().apply(arr);
Brian Osman7c979f52019-02-12 13:27:51 -0500165 }
166
Brian Osman23b96c02019-02-19 13:04:40 -0500167 // 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.
Brian Osman7c979f52019-02-12 13:27:51 -0500172 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
192protected:
Brian Osman23b96c02019-02-19 13:04:40 -0500193 // Helper struct to allow exitArray to specify a single operation performed on the array.
Brian Osman5de7ea42019-02-14 13:23:51 -0500194 struct ArrayEdit {
195 enum class Verb {
196 kNone,
197 kRemove,
Brian Osman5de7ea42019-02-14 13:23:51 -0500198 };
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;
Brian Osman5de7ea42019-02-14 13:23:51 -0500214 }
215 }
216 };
217
Brian Osman9dac0d82019-12-02 16:52:51 -0500218private:
Brian Osman7c979f52019-02-12 13:27:51 -0500219 virtual void enterObject(const char* name) = 0;
220 virtual void exitObject() = 0;
Brian Osman5de7ea42019-02-14 13:23:51 -0500221
222 virtual int enterArray(const char* name, int oldCount) = 0;
223 virtual ArrayEdit exitArray() = 0;
224
Brian Osman7c979f52019-02-12 13:27:51 -0500225 virtual void visit(sk_sp<SkReflected>&, const SkReflected::Type* baseType) = 0;
Brian Osman7c979f52019-02-12 13:27:51 -0500226};
227
228#endif // SkReflected_DEFINED