You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
322 lines
12 KiB
322 lines
12 KiB
/*
|
|
* 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<Function> {
|
|
// 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<Function>. 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<Function> 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<IntFunction> {
|
|
// 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<Function> and RegisterableClass<IntFunction> 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 <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#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 T>
|
|
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<std::string> *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 T>
|
|
class RegisterableClass {
|
|
public:
|
|
// Factory function type.
|
|
typedef T *(Factory)();
|
|
|
|
// Registry type.
|
|
typedef internal::ComponentRegistry<Factory> 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 <class ComponentClass>
|
|
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<component_class>); \
|
|
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<base_class>::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<base_class>::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_
|