1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // Mechanism to instantiate classes by name. 18 // 19 // This mechanism is useful if the concrete classes to be instantiated are not 20 // statically known (e.g., if their names are read from a dynamically-provided 21 // config). 22 // 23 // In that case, the first step is to define the API implemented by the 24 // instantiated classes. E.g., 25 // 26 // // In a header file function.h: 27 // 28 // // Abstract function that takes a double and returns a double. 29 // class Function : public RegisterableClass<Function> { 30 // public: 31 // virtual ~Function() {} 32 // virtual double Evaluate(double x) = 0; 33 // }; 34 // 35 // // Should be inside namespace libtextclassifier3::mobile. 36 // SAFTM_DECLARE_CLASS_REGISTRY_NAME(Function); 37 // 38 // Notice the inheritance from RegisterableClass<Function>. RegisterableClass 39 // is defined by this file (registry.h). Under the hood, this inheritanace 40 // defines a "registry" that maps names (zero-terminated arrays of chars) to 41 // factory methods that create Functions. You should give a human-readable name 42 // to this registry. To do that, use the following macro in a .cc file (it has 43 // to be a .cc file, as it defines some static data): 44 // 45 // // Inside function.cc 46 // // Should be inside namespace libtextclassifier3::mobile. 47 // SAFTM_DEFINE_CLASS_REGISTRY_NAME("function", Function); 48 // 49 // Now, let's define a few concrete Functions: e.g., 50 // 51 // class Cos : public Function { 52 // public: 53 // double Evaluate(double x) override { return cos(x); } 54 // SAFTM_DEFINE_REGISTRATION_METHOD("cos", Cos); 55 // }; 56 // 57 // class Exp : public Function { 58 // public: 59 // double Evaluate(double x) override { return exp(x); } 60 // SAFTM_DEFINE_REGISTRATION_METHOD("sin", Sin); 61 // }; 62 // 63 // Each concrete Function implementation should have (in the public section) the 64 // macro 65 // 66 // SAFTM_DEFINE_REGISTRATION_METHOD("name", implementation_class); 67 // 68 // This defines a RegisterClass static method that, when invoked, associates 69 // "name" with a factory method that creates instances of implementation_class. 70 // 71 // Before instantiating Functions by name, we need to tell our system which 72 // Functions we may be interested in. This is done by calling the 73 // Foo::RegisterClass() for each relevant Foo implementation of Function. It is 74 // ok to call Foo::RegisterClass() multiple times (even in parallel): only the 75 // first call will perform something, the others will return immediately. 76 // 77 // Cos::RegisterClass(); 78 // Exp::RegisterClass(); 79 // 80 // Now, let's instantiate a Function based on its name. This get a lot more 81 // interesting if the Function name is not statically known (i.e., 82 // read from an input proto: 83 // 84 // std::unique_ptr<Function> f(Function::Create("cos")); 85 // double result = f->Evaluate(arg); 86 // 87 // NOTE: the same binary can use this mechanism for different APIs. E.g., one 88 // can also have (in the binary with Function, Sin, Cos, etc): 89 // 90 // class IntFunction : public RegisterableClass<IntFunction> { 91 // public: 92 // virtual ~IntFunction() {} 93 // virtual int Evaluate(int k) = 0; 94 // }; 95 // 96 // SAFTM_DECLARE_CLASS_REGISTRY_NAME(IntFunction); 97 // 98 // SAFTM_DEFINE_CLASS_REGISTRY_NAME("int function", IntFunction); 99 // 100 // class Inc : public IntFunction { 101 // public: 102 // int Evaluate(int k) override { return k + 1; } 103 // SAFTM_DEFINE_REGISTRATION_METHOD("inc", Inc); 104 // }; 105 // 106 // RegisterableClass<Function> and RegisterableClass<IntFunction> define their 107 // own registries: each maps string names to implementation of the corresponding 108 // API. 109 // 110 // NOTE: the mechanism described above requires you to explicitly call 111 // RegisterClass() for all relevant classes before instantiating them. You can 112 // do this in the main() function or in any other function that is guaranteed to 113 // run before the code that instantiates those classes. Alternatively, you can 114 // use the macro SAFTM_STATIC_REGISTRATION to perform this registration in a 115 // decentralized fashion. Just use that macro in a .cc file, outside any 116 // function / class, e.g., 117 // 118 // SAFTM_STATIC_REGISTRATION(Cos); 119 // 120 // and make sure you link in all symbols from that .cc file; e.g., in bazel, use 121 // alwayslink = 1 for the corresponding cc_library. Still, please be aware that 122 // using alwayslink = 1 limits the ability of the linker to perform dead code 123 // elimination. 124 125 #ifndef NLP_SAFT_COMPONENTS_COMMON_MOBILE_REGISTRY_H_ 126 #define NLP_SAFT_COMPONENTS_COMMON_MOBILE_REGISTRY_H_ 127 128 #include <stdlib.h> 129 #include <string.h> 130 131 #include <string> 132 #include <vector> 133 134 #include "lang_id/common/lite_base/logging.h" 135 #include "lang_id/common/lite_base/macros.h" 136 137 namespace libtextclassifier3 { 138 namespace mobile { 139 140 namespace internal { 141 // Registry that associates keys (zero-terminated array of chars) with values. 142 // Values are pointers to type T (the template parameter). This is used to 143 // store the association between component names and factory methods that 144 // produce those components; the error messages are focused on that case. 145 // 146 // Internally, this registry uses a linked list of (key, value) pairs. We do 147 // not use an STL map, list, etc because we aim for small code size. 148 template <class T> 149 class ComponentRegistry { 150 public: ComponentRegistry(const char * name)151 explicit ComponentRegistry(const char *name) : name_(name), head_(nullptr) {} 152 153 // Adds a the (key, value) pair to this registry (if the key does not already 154 // exists in this registry) and returns true. If the registry already has a 155 // mapping for key, returns false and does not modify the registry. NOTE: the 156 // error (false) case happens even if the existing value for key is equal with 157 // the new one. 158 // 159 // This method does not take ownership of key, nor of value. Add(const char * key,T * value)160 bool Add(const char *key, T *value) { 161 const Cell *old_cell = FindCell(key); 162 if (old_cell != nullptr) { 163 SAFTM_LOG(ERROR) << "Duplicate component: " << key; 164 return false; 165 } 166 Cell *new_cell = new Cell(key, value, head_); 167 head_ = new_cell; 168 return true; 169 } 170 171 // Returns the value attached to a key in this registry. Returns nullptr on 172 // error (e.g., unknown key). Lookup(const char * key)173 T *Lookup(const char *key) const { 174 const Cell *cell = FindCell(key); 175 if (cell == nullptr) { 176 SAFTM_LOG(ERROR) << "Unknown " << name() << " component: " << key; 177 } 178 return (cell == nullptr) ? nullptr : cell->value(); 179 } 180 Lookup(const string & key)181 T *Lookup(const string &key) const { return Lookup(key.c_str()); } 182 183 // Returns name of this ComponentRegistry. name()184 const char *name() const { return name_; } 185 186 // Fills *names with names of all components registered in this 187 // ComponentRegistry. Previous content of *names is cleared out. GetComponentNames(std::vector<string> * names)188 void GetComponentNames(std::vector<string> *names) { 189 names->clear(); 190 for (const Cell *c = head_; c!= nullptr; c = c->next()) { 191 names->emplace_back(c->key()); 192 } 193 } 194 195 private: 196 // Cell for the singly-linked list underlying this ComponentRegistry. Each 197 // cell contains a key, the value for that key, as well as a pointer to the 198 // next Cell from the list. 199 class Cell { 200 public: 201 // Constructs a new Cell. Cell(const char * key,T * value,Cell * next)202 Cell(const char *key, T *value, Cell *next) 203 : key_(key), value_(value), next_(next) {} 204 key()205 const char *key() const { return key_; } value()206 T *value() const { return value_; } next()207 Cell *next() const { return next_; } 208 209 private: 210 const char *const key_; 211 T *const value_; 212 Cell *const next_; 213 }; 214 215 // Finds Cell for indicated key in the singly-linked list pointed to by head_. 216 // Returns pointer to that first Cell with that key, or nullptr if no such 217 // Cell (i.e., unknown key). 218 // 219 // Caller does NOT own the returned pointer. FindCell(const char * key)220 const Cell *FindCell(const char *key) const { 221 const Cell *c = head_; 222 while (c != nullptr && strcmp(key, c->key()) != 0) { 223 c = c->next(); 224 } 225 return c; 226 } 227 228 // Human-readable description for this ComponentRegistry. For debug purposes. 229 const char *const name_; 230 231 // Pointer to the first Cell from the underlying list of (key, value) pairs. 232 Cell *head_; 233 }; 234 } // namespace internal 235 236 // Base class for registerable classes. 237 template <class T> 238 class RegisterableClass { 239 public: 240 // Factory function type. 241 typedef T *(Factory)(); 242 243 // Registry type. 244 typedef internal::ComponentRegistry<Factory> Registry; 245 246 // Creates a new instance of T. Returns pointer to new instance or nullptr in 247 // case of errors (e.g., unknown component). 248 // 249 // Passes ownership of the returned pointer to the caller. Create(const string & name)250 static T *Create(const string &name) { // NOLINT 251 auto *factory = registry()->Lookup(name); 252 if (factory == nullptr) { 253 SAFTM_LOG(ERROR) << "Unknown RegisterableClass " << name; 254 return nullptr; 255 } 256 return factory(); 257 } 258 259 // Returns registry for class. registry()260 static Registry *registry() { 261 static Registry *registry_for_type_t = new Registry(kRegistryName); 262 return registry_for_type_t; 263 } 264 265 protected: 266 // Factory method for subclass ComponentClass. Used internally by the static 267 // method RegisterClass() defined by SAFTM_DEFINE_REGISTRATION_METHOD. 268 template <class ComponentClass> _internal_component_factory()269 static T *_internal_component_factory() { 270 return new ComponentClass(); 271 } 272 273 private: 274 // Human-readable name for the registry for this class. 275 static const char kRegistryName[]; 276 }; 277 278 // Defines the static method component_class::RegisterClass() that should be 279 // called before trying to instantiate component_class by name. Should be used 280 // inside the public section of the declaration of component_class. See 281 // comments at the top-level of this file. 282 #define SAFTM_DEFINE_REGISTRATION_METHOD(component_name, component_class) \ 283 static void RegisterClass() { \ 284 static bool once = registry()->Add( \ 285 component_name, &_internal_component_factory<component_class>); \ 286 if (!once) { \ 287 SAFTM_LOG(ERROR) << "Problem registering " << component_name; \ 288 } \ 289 SAFTM_DCHECK(once); \ 290 } 291 292 // Defines the human-readable name of the registry associated with base_class. 293 #define SAFTM_DECLARE_CLASS_REGISTRY_NAME(base_class) \ 294 template <> \ 295 const char ::libtextclassifier3::mobile::RegisterableClass<base_class>::kRegistryName[] 296 297 // Defines the human-readable name of the registry associated with base_class. 298 #define SAFTM_DEFINE_CLASS_REGISTRY_NAME(registry_name, base_class) \ 299 template <> \ 300 const char \ 301 ::libtextclassifier3::mobile::RegisterableClass<base_class>::kRegistryName[] \ 302 = registry_name 303 304 // Register component_name, by calling component_class::RegisterClass() on 305 // program start-up, before main. NOTE: this macro should be used in 306 // conjunction with something like alwayslink = 1 from bazel. That is 307 // discouraged, as it prevents the linker from doing dead code elimination, so 308 // please use this macro only in special cases. Instead, if you care about code 309 // size, then you should aim to explicitly call RegisterClass from your code 310 // (e.g., from the main method, or from the constructor of the class that may 311 // need those registered components). 312 #define SAFTM_STATIC_REGISTRATION(component_class) \ 313 static bool SAFTM_UNIQUE_ID(_kRegistrationDummy) = [] { \ 314 component_class::RegisterClass(); \ 315 return true; \ 316 }() 317 318 } // namespace mobile 319 } // namespace nlp_saft 320 321 #endif // NLP_SAFT_COMPONENTS_COMMON_MOBILE_REGISTRY_H_ 322