/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Mechanism to instantiate classes by name. // // This mechanism is useful if the concrete classes to be instantiated are not // statically known (e.g., if their names are read from a dynamically-provided // config). // // In that case, the first step is to define the API implemented by the // instantiated classes. E.g., // // // In a header file function.h: // // // Abstract function that takes a double and returns a double. // class Function : public RegisterableClass { // public: // virtual ~Function() {} // virtual double Evaluate(double x) = 0; // }; // // // Should be inside namespace libtextclassifier3::mobile. // SAFTM_DECLARE_CLASS_REGISTRY_NAME(Function); // // Notice the inheritance from RegisterableClass. RegisterableClass // is defined by this file (registry.h). Under the hood, this inheritanace // defines a "registry" that maps names (zero-terminated arrays of chars) to // factory methods that create Functions. You should give a human-readable name // to this registry. To do that, use the following macro in a .cc file (it has // to be a .cc file, as it defines some static data): // // // Inside function.cc // // Should be inside namespace libtextclassifier3::mobile. // SAFTM_DEFINE_CLASS_REGISTRY_NAME("function", Function); // // Now, let's define a few concrete Functions: e.g., // // class Cos : public Function { // public: // double Evaluate(double x) override { return cos(x); } // SAFTM_DEFINE_REGISTRATION_METHOD("cos", Cos); // }; // // class Exp : public Function { // public: // double Evaluate(double x) override { return exp(x); } // SAFTM_DEFINE_REGISTRATION_METHOD("sin", Sin); // }; // // Each concrete Function implementation should have (in the public section) the // macro // // SAFTM_DEFINE_REGISTRATION_METHOD("name", implementation_class); // // This defines a RegisterClass static method that, when invoked, associates // "name" with a factory method that creates instances of implementation_class. // // Before instantiating Functions by name, we need to tell our system which // Functions we may be interested in. This is done by calling the // Foo::RegisterClass() for each relevant Foo implementation of Function. It is // ok to call Foo::RegisterClass() multiple times (even in parallel): only the // first call will perform something, the others will return immediately. // // Cos::RegisterClass(); // Exp::RegisterClass(); // // Now, let's instantiate a Function based on its name. This get a lot more // interesting if the Function name is not statically known (i.e., // read from an input proto: // // std::unique_ptr f(Function::Create("cos")); // double result = f->Evaluate(arg); // // NOTE: the same binary can use this mechanism for different APIs. E.g., one // can also have (in the binary with Function, Sin, Cos, etc): // // class IntFunction : public RegisterableClass { // public: // virtual ~IntFunction() {} // virtual int Evaluate(int k) = 0; // }; // // SAFTM_DECLARE_CLASS_REGISTRY_NAME(IntFunction); // // SAFTM_DEFINE_CLASS_REGISTRY_NAME("int function", IntFunction); // // class Inc : public IntFunction { // public: // int Evaluate(int k) override { return k + 1; } // SAFTM_DEFINE_REGISTRATION_METHOD("inc", Inc); // }; // // RegisterableClass and RegisterableClass define their // own registries: each maps string names to implementation of the corresponding // API. // // NOTE: the mechanism described above requires you to explicitly call // RegisterClass() for all relevant classes before instantiating them. You can // do this in the main() function or in any other function that is guaranteed to // run before the code that instantiates those classes. Alternatively, you can // use the macro SAFTM_STATIC_REGISTRATION to perform this registration in a // decentralized fashion. Just use that macro in a .cc file, outside any // function / class, e.g., // // SAFTM_STATIC_REGISTRATION(Cos); // // and make sure you link in all symbols from that .cc file; e.g., in bazel, use // alwayslink = 1 for the corresponding cc_library. Still, please be aware that // using alwayslink = 1 limits the ability of the linker to perform dead code // elimination. #ifndef NLP_SAFT_COMPONENTS_COMMON_MOBILE_REGISTRY_H_ #define NLP_SAFT_COMPONENTS_COMMON_MOBILE_REGISTRY_H_ #include #include #include #include #include "lang_id/common/lite_base/logging.h" #include "lang_id/common/lite_base/macros.h" namespace libtextclassifier3 { namespace mobile { namespace internal { // Registry that associates keys (zero-terminated array of chars) with values. // Values are pointers to type T (the template parameter). This is used to // store the association between component names and factory methods that // produce those components; the error messages are focused on that case. // // Internally, this registry uses a linked list of (key, value) pairs. We do // not use an STL map, list, etc because we aim for small code size. template class ComponentRegistry { public: explicit ComponentRegistry(const char *name) : name_(name), head_(nullptr) {} // Adds a the (key, value) pair to this registry (if the key does not already // exists in this registry) and returns true. If the registry already has a // mapping for key, returns false and does not modify the registry. NOTE: the // error (false) case happens even if the existing value for key is equal with // the new one. // // This method does not take ownership of key, nor of value. bool Add(const char *key, T *value) { const Cell *old_cell = FindCell(key); if (old_cell != nullptr) { SAFTM_LOG(ERROR) << "Duplicate component: " << key; return false; } Cell *new_cell = new Cell(key, value, head_); head_ = new_cell; return true; } // Returns the value attached to a key in this registry. Returns nullptr on // error (e.g., unknown key). T *Lookup(const char *key) const { const Cell *cell = FindCell(key); if (cell == nullptr) { SAFTM_LOG(ERROR) << "Unknown " << name() << " component: " << key; } return (cell == nullptr) ? nullptr : cell->value(); } T *Lookup(const std::string &key) const { return Lookup(key.c_str()); } // Returns name of this ComponentRegistry. const char *name() const { return name_; } // Fills *names with names of all components registered in this // ComponentRegistry. Previous content of *names is cleared out. void GetComponentNames(std::vector *names) { names->clear(); for (const Cell *c = head_; c!= nullptr; c = c->next()) { names->emplace_back(c->key()); } } private: // Cell for the singly-linked list underlying this ComponentRegistry. Each // cell contains a key, the value for that key, as well as a pointer to the // next Cell from the list. class Cell { public: // Constructs a new Cell. Cell(const char *key, T *value, Cell *next) : key_(key), value_(value), next_(next) {} const char *key() const { return key_; } T *value() const { return value_; } Cell *next() const { return next_; } private: const char *const key_; T *const value_; Cell *const next_; }; // Finds Cell for indicated key in the singly-linked list pointed to by head_. // Returns pointer to that first Cell with that key, or nullptr if no such // Cell (i.e., unknown key). // // Caller does NOT own the returned pointer. const Cell *FindCell(const char *key) const { const Cell *c = head_; while (c != nullptr && strcmp(key, c->key()) != 0) { c = c->next(); } return c; } // Human-readable description for this ComponentRegistry. For debug purposes. const char *const name_; // Pointer to the first Cell from the underlying list of (key, value) pairs. Cell *head_; }; } // namespace internal // Base class for registerable classes. template class RegisterableClass { public: // Factory function type. typedef T *(Factory)(); // Registry type. typedef internal::ComponentRegistry Registry; // Creates a new instance of T. Returns pointer to new instance or nullptr in // case of errors (e.g., unknown component). // // Passes ownership of the returned pointer to the caller. static T *Create(const std::string &name) { // NOLINT auto *factory = registry()->Lookup(name); if (factory == nullptr) { SAFTM_LOG(ERROR) << "Unknown RegisterableClass " << name; return nullptr; } return factory(); } // Returns registry for class. static Registry *registry() { static Registry *registry_for_type_t = new Registry(kRegistryName); return registry_for_type_t; } protected: // Factory method for subclass ComponentClass. Used internally by the static // method RegisterClass() defined by SAFTM_DEFINE_REGISTRATION_METHOD. template static T *_internal_component_factory() { return new ComponentClass(); } private: // Human-readable name for the registry for this class. static const char kRegistryName[]; }; // Defines the static method component_class::RegisterClass() that should be // called before trying to instantiate component_class by name. Should be used // inside the public section of the declaration of component_class. See // comments at the top-level of this file. #define SAFTM_DEFINE_REGISTRATION_METHOD(component_name, component_class) \ static void RegisterClass() { \ static bool once = registry()->Add( \ component_name, &_internal_component_factory); \ if (!once) { \ SAFTM_LOG(ERROR) << "Problem registering " << component_name; \ } \ SAFTM_DCHECK(once); \ } // Defines the human-readable name of the registry associated with base_class. #define SAFTM_DECLARE_CLASS_REGISTRY_NAME(base_class) \ template <> \ const char ::libtextclassifier3::mobile::RegisterableClass::kRegistryName[] // Defines the human-readable name of the registry associated with base_class. #define SAFTM_DEFINE_CLASS_REGISTRY_NAME(registry_name, base_class) \ template <> \ const char \ ::libtextclassifier3::mobile::RegisterableClass::kRegistryName[] \ = registry_name // Register component_name, by calling component_class::RegisterClass() on // program start-up, before main. NOTE: this macro should be used in // conjunction with something like alwayslink = 1 from bazel. That is // discouraged, as it prevents the linker from doing dead code elimination, so // please use this macro only in special cases. Instead, if you care about code // size, then you should aim to explicitly call RegisterClass from your code // (e.g., from the main method, or from the constructor of the class that may // need those registered components). #define SAFTM_STATIC_REGISTRATION(component_class) \ static bool SAFTM_UNIQUE_ID(_kRegistrationDummy) = [] { \ component_class::RegisterClass(); \ return true; \ }() } // namespace mobile } // namespace nlp_saft #endif // NLP_SAFT_COMPONENTS_COMMON_MOBILE_REGISTRY_H_