| 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 |  | 
|---|