Home | History | Annotate | Download | only in common
      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:
    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.
    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).
    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 
    181   T *Lookup(const string &key) const { return Lookup(key.c_str()); }
    182 
    183   // Returns name of this ComponentRegistry.
    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.
    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.
    202     Cell(const char *key, T *value, Cell *next)
    203         : key_(key), value_(value), next_(next) {}
    204 
    205     const char *key() const { return key_; }
    206     T *value() const { return value_; }
    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.
    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.
    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.
    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>
    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