• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 std::string & key)181   T *Lookup(const std::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<std::string> * names)188   void GetComponentNames(std::vector<std::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 std::string & name)250   static T *Create(const std::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