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