1/*!
2 * Copyright (c) 2015 by Contributors
3 * \file registry.h
4 * \brief Registry utility that helps to build registry singletons.
5 */
6#ifndef DMLC_REGISTRY_H_
7#define DMLC_REGISTRY_H_
8
9#include <map>
10#include <string>
11#include <vector>
12#include "./base.h"
13#include "./logging.h"
14#include "./parameter.h"
15#include "./type_traits.h"
16
17namespace dmlc {
18/*!
19 * \brief Registry class.
20 * Registry can be used to register global singletons.
21 * The most commonly use case are factory functions.
22 *
23 * \tparam EntryType Type of Registry entries,
24 * EntryType need to name a name field.
25 */
26template<typename EntryType>
27class Registry {
28 public:
29 /*! \return list of entries in the registry(excluding alias) */
30 inline static const std::vector<const EntryType*>& List() {
31 return Get()->const_list_;
32 }
33 /*! \return list all names registered in the registry, including alias */
34 inline static std::vector<std::string> ListAllNames() {
35 const std::map<std::string, EntryType*> &fmap = Get()->fmap_;
36 typename std::map<std::string, EntryType*>::const_iterator p;
37 std::vector<std::string> names;
38 for (p = fmap.begin(); p !=fmap.end(); ++p) {
39 names.push_back(p->first);
40 }
41 return names;
42 }
43 /*!
44 * \brief Find the entry with corresponding name.
45 * \param name name of the function
46 * \return the corresponding function, can be NULL
47 */
48 inline static const EntryType *Find(const std::string &name) {
49 const std::map<std::string, EntryType*> &fmap = Get()->fmap_;
50 typename std::map<std::string, EntryType*>::const_iterator p = fmap.find(name);
51 if (p != fmap.end()) {
52 return p->second;
53 } else {
54 return NULL;
55 }
56 }
57 /*!
58 * \brief Add alias to the key_name
59 * \param key_name The original entry key
60 * \param alias The alias key.
61 */
62 inline void AddAlias(const std::string& key_name,
63 const std::string& alias) {
64 EntryType* e = fmap_.at(key_name);
65 if (fmap_.count(alias)) {
66 CHECK_EQ(e, fmap_.at(alias))
67 << "Trying to register alias " << alias << " for key " << key_name
68 << " but " << alias << " is already taken";
69 } else {
70 fmap_[alias] = e;
71 }
72 }
73 /*!
74 * \brief Internal function to register a name function under name.
75 * \param name name of the function
76 * \return ref to the registered entry, used to set properties
77 */
78 inline EntryType &__REGISTER__(const std::string& name) {
79 std::lock_guard<std::mutex> guard(registering_mutex);
80 if (fmap_.count(name) > 0) {
81 return *fmap_[name];
82 }
83 EntryType *e = new EntryType();
84 e->name = name;
85 fmap_[name] = e;
86 const_list_.push_back(e);
87 entry_list_.push_back(e);
88 return *e;
89 }
90 /*!
91 * \brief Internal function to either register or get registered entry
92 * \param name name of the function
93 * \return ref to the registered entry, used to set properties
94 */
95 inline EntryType &__REGISTER_OR_GET__(const std::string& name) {
96 if (fmap_.count(name) == 0) {
97 return __REGISTER__(name);
98 } else {
99 return *fmap_.at(name);
100 }
101 }
102 /*!
103 * \brief get a singleton of the Registry.
104 * This function can be defined by DMLC_REGISTRY_ENABLE.
105 * \return get a singleton
106 */
107 static Registry *Get();
108
109 private:
110 /*! \brief list of entry types */
111 std::vector<EntryType*> entry_list_;
112 /*! \brief list of entry types */
113 std::vector<const EntryType*> const_list_;
114 /*! \brief map of name->function */
115 std::map<std::string, EntryType*> fmap_;
116 /*! \brief lock guarding the registering*/
117 std::mutex registering_mutex;
118 /*! \brief constructor */
119 Registry() {}
120 /*! \brief destructor */
121 ~Registry() {
122 for (size_t i = 0; i < entry_list_.size(); ++i) {
123 delete entry_list_[i];
124 }
125 }
126};
127
128/*!
129 * \brief Common base class for function registry.
130 *
131 * \code
132 * // This example demonstrates how to use Registry to create a factory of trees.
133 * struct TreeFactory :
134 * public FunctionRegEntryBase<TreeFactory, std::function<Tree*()> > {
135 * };
136 *
137 * // in a independent cc file
138 * namespace dmlc {
139 * DMLC_REGISTRY_ENABLE(TreeFactory);
140 * }
141 * // register binary tree constructor into the registry.
142 * DMLC_REGISTRY_REGISTER(TreeFactory, TreeFactory, BinaryTree)
143 * .describe("Constructor of BinaryTree")
144 * .set_body([]() { return new BinaryTree(); });
145 * \endcode
146 *
147 * \tparam EntryType The type of subclass that inheritate the base.
148 * \tparam FunctionType The function type this registry is registerd.
149 */
150template<typename EntryType, typename FunctionType>
151class FunctionRegEntryBase {
152 public:
153 /*! \brief name of the entry */
154 std::string name;
155 /*! \brief description of the entry */
156 std::string description;
157 /*! \brief additional arguments to the factory function */
158 std::vector<ParamFieldInfo> arguments;
159 /*! \brief Function body to create ProductType */
160 FunctionType body;
161 /*! \brief Return type of the function */
162 std::string return_type;
163
164 /*!
165 * \brief Set the function body.
166 * \param body Function body to set.
167 * \return reference to self.
168 */
169 inline EntryType &set_body(FunctionType body) {
170 this->body = body;
171 return this->self();
172 }
173 /*!
174 * \brief Describe the function.
175 * \param description The description of the factory function.
176 * \return reference to self.
177 */
178 inline EntryType &describe(const std::string &description) {
179 this->description = description;
180 return this->self();
181 }
182 /*!
183 * \brief Add argument information to the function.
184 * \param name Name of the argument.
185 * \param type Type of the argument.
186 * \param description Description of the argument.
187 * \return reference to self.
188 */
189 inline EntryType &add_argument(const std::string &name,
190 const std::string &type,
191 const std::string &description) {
192 ParamFieldInfo info;
193 info.name = name;
194 info.type = type;
195 info.type_info_str = info.type;
196 info.description = description;
197 arguments.push_back(info);
198 return this->self();
199 }
200 /*!
201 * \brief Append list if arguments to the end.
202 * \param args Additional list of arguments.
203 * \return reference to self.
204 */
205 inline EntryType &add_arguments(const std::vector<ParamFieldInfo> &args) {
206 arguments.insert(arguments.end(), args.begin(), args.end());
207 return this->self();
208 }
209 /*!
210 * \brief Set the return type.
211 * \param type Return type of the function, could be Symbol or Symbol[]
212 * \return reference to self.
213 */
214 inline EntryType &set_return_type(const std::string &type) {
215 return_type = type;
216 return this->self();
217 }
218
219 protected:
220 /*!
221 * \return reference of self as derived type
222 */
223 inline EntryType &self() {
224 return *(static_cast<EntryType*>(this));
225 }
226};
227
228/*!
229 * \def DMLC_REGISTRY_ENABLE
230 * \brief Macro to enable the registry of EntryType.
231 * This macro must be used under namespace dmlc, and only used once in cc file.
232 * \param EntryType Type of registry entry
233 */
234#define DMLC_REGISTRY_ENABLE(EntryType) \
235 template<> \
236 Registry<EntryType > *Registry<EntryType >::Get() { \
237 static Registry<EntryType > inst; \
238 return &inst; \
239 } \
240
241/*!
242 * \brief Generic macro to register an EntryType
243 * There is a complete example in FactoryRegistryEntryBase.
244 *
245 * \param EntryType The type of registry entry.
246 * \param EntryTypeName The typename of EntryType, must do not contain namespace :: .
247 * \param Name The name to be registered.
248 * \sa FactoryRegistryEntryBase
249 */
250#define DMLC_REGISTRY_REGISTER(EntryType, EntryTypeName, Name) \
251 static DMLC_ATTRIBUTE_UNUSED EntryType & __make_ ## EntryTypeName ## _ ## Name ## __ = \
252 ::dmlc::Registry<EntryType>::Get()->__REGISTER__(#Name) \
253
254/*!
255 * \brief (Optional) Declare a file tag to current file that contains object registrations.
256 *
257 * This will declare a dummy function that will be called by register file to
258 * incur a link dependency.
259 *
260 * \param UniqueTag The unique tag used to represent.
261 * \sa DMLC_REGISTRY_LINK_TAG
262 */
263#define DMLC_REGISTRY_FILE_TAG(UniqueTag) \
264 int __dmlc_registry_file_tag_ ## UniqueTag ## __() { return 0; }
265
266/*!
267 * \brief (Optional) Force link to all the objects registered in file tag.
268 *
269 * This macro must be used in the same file as DMLC_REGISTRY_ENABLE and
270 * in the same namespace as DMLC_REGISTRY_FILE_TAG
271 *
272 * DMLC_REGISTRY_FILE_TAG and DMLC_REGISTRY_LINK_TAG are optional macros for registration.
273 * They are used to encforce link of certain file into during static linking.
274 *
275 * This is mainly used to solve problem during statically link a library which contains backward registration.
276 * Specifically, this avoids the objects in these file tags to be ignored by compiler.
277 *
278 * For dynamic linking, this problem won't occur as everything is loaded by default.
279 *
280 * Use of this is optional as it will create an error when a file tag do not exist.
281 * An alternative solution is always ask user to enable --whole-archieve during static link.
282 *
283 * \code
284 * // in file objective_registry.cc
285 * DMLC_REGISTRY_ENABLE(MyObjective);
286 * DMLC_REGISTRY_LINK_TAG(regression_op);
287 * DMLC_REGISTRY_LINK_TAG(rank_op);
288 *
289 * // in file regression_op.cc
290 * // declare tag of this file.
291 * DMLC_REGISTRY_FILE_TAG(regression_op);
292 * DMLC_REGISTRY_REGISTER(MyObjective, logistic_reg, logistic_reg);
293 * // ...
294 *
295 * // in file rank_op.cc
296 * // declare tag of this file.
297 * DMLC_REGISTRY_FILE_TAG(rank_op);
298 * DMLC_REGISTRY_REGISTER(MyObjective, pairwiserank, pairwiserank);
299 *
300 * \endcode
301 *
302 * \param UniqueTag The unique tag used to represent.
303 * \sa DMLC_REGISTRY_ENABLE, DMLC_REGISTRY_FILE_TAG
304 */
305#define DMLC_REGISTRY_LINK_TAG(UniqueTag) \
306 int __dmlc_registry_file_tag_ ## UniqueTag ## __(); \
307 static int DMLC_ATTRIBUTE_UNUSED __reg_file_tag_ ## UniqueTag ## __ = \
308 __dmlc_registry_file_tag_ ## UniqueTag ## __();
309} // namespace dmlc
310#endif // DMLC_REGISTRY_H_
311