GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/factory.h
Date: 2026-09-07 02:28:19
Exec Total Coverage
Lines: 29 33 87.9%
Functions: 62 80 77.5%
Branches: 38 70 54.3%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #ifndef GEOGRAM_BASIC_FACTORY
41 #define GEOGRAM_BASIC_FACTORY
42
43 #include <geogram/basic/memory.h>
44 #include <geogram/basic/counted.h>
45 #include <string>
46 #include <map>
47 #include <vector>
48 #include <typeinfo>
49
50 /**
51 * \file geogram/basic/factory.h
52 * \brief Generic factory mechanism
53 */
54
55
56 // Latest clang complains too often about empty \par statements in documentation
57 #ifdef GEO_COMPILER_CLANG
58 #pragma clang diagnostic push
59 #pragma clang diagnostic ignored "-Wdocumentation"
60 #endif
61
62 namespace GEO {
63
64 /**
65 * \brief Repository of unique instances
66 * \details InstanceRepo is the central point to access unique singleton
67 * instances using their type name. Instances are registered at their
68 * creation using function add() and retrieved using get().
69 * \internal
70 * This class avoids a recurrent problem with templated singleton classes:
71 * the C++ template instantiation mechanism does not guarantee that static
72 * variables in template classes have a single occurrence. Storing
73 * templated singleton instances in a central repository guarantees the
74 * instances uniqueness.
75 */
76 class GEOGRAM_API InstanceRepo {
77 public:
78 /**
79 * \brief Type of the Instances stored in the repository
80 */
81 typedef Counted Instance;
82
83 /**
84 * \brief Gets unique instance from the repository
85 * \details This function returns a unique instance of type \p
86 * InstanceType. If the instance is already registered in the
87 * repository, it is returned, otherwise a new instance is created and
88 * registered to the repository using the InstanceType name.
89 * \tparam InstanceType type of the instance
90 * \return a pointer to a \p InstanceType unique instance.
91 */
92 template <class InstanceType>
93 8041 static InstanceType& instance() {
94 8041 const std::string name = typeid(InstanceType).name();
95
1/2
✓ Branch 1 taken 8041 times.
✗ Branch 2 not taken.
8041 Instance* instance = get(name);
96
2/2
✓ Branch 0 taken 618 times.
✓ Branch 1 taken 7423 times.
8041 if(instance == nullptr) {
97
1/2
✓ Branch 1 taken 618 times.
✗ Branch 2 not taken.
618 instance = new InstanceType;
98
1/2
✓ Branch 1 taken 618 times.
✗ Branch 2 not taken.
618 add(name, instance);
99 }
100 8041 return *static_cast<InstanceType*>(instance);
101 }
102
103 private:
104 /**
105 * \brief Registers an instance to the repository
106 * \param[in] name registration key of the instance
107 * \param[in] instance the instance to register
108 */
109 static void add(const std::string& name, Instance* instance);
110
111 /**
112 * \brief Retrieves an instance from the repository
113 * \param[in] name registration key of then instance
114 * \retval the pointer to the stored instance.
115 * \retval a null pointer otherwise
116 */
117 static Instance* get(const std::string& name);
118 };
119
120 /**************************************************************************/
121
122 /**
123 * \brief Factory of typed objects
124 * \details A Factory is a mechanism to create objects without knowing
125 * their types in advance. The object types that a Factory can instantiate
126 * belong to a type hierarchy whose base type is defined by \p Type.
127 * The Factory uses a registry of creator functions bound to user-defined
128 * names. The user-defined names can be used to:
129 * - register a creator function (see register_creator())
130 * - create an object, using the creator function bound to the name (see
131 * create_object())
132 * \tparam FactoryCreator the type of the creator used to create objects
133 * in this Factory. FactoryCreator must define:
134 * - the type CreatorType type of the creation function
135 * - a static template function create(...) used to actually create
136 * objects of a given concrete type
137 * \internal
138 * \todo
139 * The current implementation provides a FactoryCreator for constructor
140 * with no arguments and with 1 argument, ... A better implementation
141 * should use variadic templates when c++11 will be supported.
142 */
143 template <class FactoryCreator>
144
1/2
✓ Branch 1 taken 618 times.
✗ Branch 2 not taken.
618 class Factory : public InstanceRepo::Instance {
145 public:
146 typedef typename FactoryCreator::CreatorType CreatorType;
147
148 /**
149 * \brief Registers a creator.
150 * \details This creates a new creator for objects of type \p
151 * ConcreteType and registers it with the user-defined name \p name.
152 * \param[in] name name of the \p ConcreteType creator in the Factory
153 * \tparam ConcreteType the type of the objects to create
154 * \par Usage example:
155 * \code
156 * struct MyBaseClass { ... };
157 * typedef Factory<MyBaseClass> MyFactory;
158 *
159 * struct MyDerivedClass : MyBaseClass { ... };
160 * MyFactory::register_creator<MyDerivedClass>("my_derived_class");
161 * \endcode
162 * \see RegisterCreator()
163 */
164 template <class ConcreteType>
165 14708 static void register_creator(const std::string& name) {
166 Factory& self = instance();
167 14708 self.registry_[name] =
168 FactoryCreator::template create<ConcreteType>;
169 14708 }
170
171 /**
172 * \brief Finds a creator by name.
173 * \param[in] name a user-defined name identifying
174 * a creator in the Factory
175 * \retval the creator associated to \p name if \p name exists
176 * \retval null pointer otherwise
177 */
178 682 static CreatorType find_creator(const std::string& name) {
179 Factory& self = instance();
180 auto i = self.registry_.find(name);
181
2/2
✓ Branch 0 taken 681 times.
✓ Branch 1 taken 1 times.
682 return i == self.registry_.end() ? nullptr : i->second;
182 }
183
184 /**
185 * \brief Lists all registered creators.
186 * \details This stores the names of the registered creators to output
187 * vector \p names.
188 * \param[out] names output list of registered names
189 */
190 static void list_creators(std::vector<std::string>& names) {
191 Factory& self = instance();
192 for(auto& it : self.registry_) {
193 names.push_back(it.first);
194 }
195 }
196
197 /**
198 * \brief Tests whether the factory has a creator.
199 * \param[in] name name of the creator
200 * \retval true if creator \p name is registered in the factory
201 * \retval false otherwise
202 */
203 5 static bool has_creator(const std::string& name) {
204 Factory& self = instance();
205
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 for(auto& it : self.registry_) {
206
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 5 times.
31 if(it.first == name) {
207 return true;
208 }
209 }
210 return false;
211 }
212
213 /**
214 * \brief Helper class to register a creator
215 * \details Declaring a static instance of this class with appropriate
216 * parameters allows to register creators to the Factory at program
217 * initialization, thus making them available before the program
218 * actually starts.
219 * \tparam ConcreteType the type of the objects to create
220 * \par Usage example:
221 * \code
222 * struct MyBaseClass { ... };
223 * typedef Factory<MyBaseClass> MyFactory;
224 *
225 * struct MyDerivedClass : MyBaseClass { ... };
226 * static MyFactory::RegisterCreator<MyDerivedClass>
227 * register_derived("my_derived_class");
228 * \endcode
229 * \see geo_register_creator()
230 */
231 template <class ConcreteType>
232 struct RegisterCreator {
233 /**
234 * \brief Constructs a registration object.
235 * \details The constructor calls register_creator() to register a
236 * \p ConcreteType creator bound to name \p name.
237 * \param[in] name name of the ConcreteType creator in the Factory
238 * \see Factory::register_creator()
239 */
240 RegisterCreator(const std::string& name) {
241
20/40
✓ Branch 1 taken 650 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 650 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 512 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 512 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 512 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 502 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 502 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 502 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 251 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 251 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 251 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 251 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 251 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 251 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 251 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 251 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 251 times.
✗ Branch 50 not taken.
✓ Branch 52 taken 251 times.
✗ Branch 53 not taken.
✓ Branch 55 taken 251 times.
✗ Branch 56 not taken.
✓ Branch 58 taken 251 times.
✗ Branch 59 not taken.
7354 Factory::template register_creator<ConcreteType>(name);
242 7354 }
243 };
244
245 protected:
246 /**
247 * \brief Factory destructor.
248 */
249 1236 ~Factory() override {
250 1236 }
251
252 private:
253 /**
254 * \brief Gets the Factory unique instance.
255 * \see InstanceRepo
256 */
257 static inline Factory& instance() {
258 8041 return InstanceRepo::instance<Factory>();
259 }
260
261 /**
262 * \brief Registry of object creator functions.
263 */
264 typedef std::map<std::string, CreatorType> Registry;
265 Registry registry_;
266 };
267
268 /**
269 * \brief Factory creator without constructor arguments.
270 * \details This defines the function to create objects with no
271 * constructor arguments.
272 * \tparam Type base type of the created objects
273 */
274 template <class Type>
275 struct FactoryCreator0 {
276 /**
277 * \brief Type of the creation function
278 */
279 typedef Type* (* CreatorType)();
280
281 /**
282 * \brief Creation function
283 * \tparam ConcreteType actual type of the object to create.
284 */
285 template <class ConcreteType>
286 862 static Type* create() {
287
1/2
✓ Branch 2 taken 109 times.
✗ Branch 3 not taken.
862 return new ConcreteType;
288 }
289 };
290
291 /**
292 * \brief Factory for types without constructor arguments.
293 * \details This implements a Factory to create objects with no
294 * constructor arguments.
295 * \tparam Type base type of the created objects
296 * \see FactoryCreator
297 */
298 template <class Type>
299 class Factory0 : public Factory<FactoryCreator0<Type> > {
300 typedef Factory<FactoryCreator0<Type> > BaseClass;
301
302 public:
303 /**
304 * \brief Creates a new object.
305 * \details This creates a new object using the creator function
306 * bound to the specified user-defined \p name.
307 * \param[in] name specifies wihch kind of object to create
308 * \retval a pointer to a new object is \p name is associated to a
309 * creator in this Factory
310 * \retval a null pointer otherwise.
311 */
312 static Type* create_object(const std::string& name) {
313 typename BaseClass::CreatorType creator =
314 432 BaseClass::find_creator(name);
315
2/2
✓ Branch 0 taken 431 times.
✓ Branch 1 taken 1 times.
432 return creator == nullptr ? nullptr : (* creator)();
316 }
317 };
318
319 /**
320 * \brief Factory creator with one argument.
321 * \details This defines the function to create objects with one
322 * argument in the constructor.
323 * \tparam Type base type of the created objects
324 * \tparam Param1 type of the constructor argument
325 */
326 template <class Type, class Param1>
327 struct FactoryCreator1 {
328 /**
329 * \brief Type of the creation function
330 */
331 typedef Type* (* CreatorType)(const Param1&);
332
333 /**
334 * \brief Creation function
335 * \tparam ConcreteType actual type of the object to create.
336 */
337 template <class ConcreteType>
338 500 static Type* create(const Param1& param1) {
339
1/2
✓ Branch 2 taken 250 times.
✗ Branch 3 not taken.
500 return new ConcreteType(param1);
340 }
341 };
342
343 /**
344 * \brief Factory for types with one constructor argument.
345 * \details This implements a Factory to create objects with no
346 * constructor arguments.
347 * \tparam Type base type of the created objects
348 * \tparam Param1 type of the constructor argument
349 * \see FactoryCreator1
350 */
351 template <class Type, class Param1>
352 class Factory1 : public Factory<FactoryCreator1<Type, Param1> > {
353 typedef Factory<FactoryCreator1<Type, Param1> > BaseClass;
354
355 public:
356 /**
357 * \brief Creates a new object with parameter(s).
358 * \details This creates a new object using the creator function
359 * bound to the specified user-defined \p name.
360 * \param[in] name specifies which kind of object to create
361 * \param[in] param1 parameter passed to the object constructor
362 * \retval a pointer to a new object is \p name is associated to a
363 * creator in this Factory
364 * \retval a null pointer otherwise.
365 */
366 static Type* create_object(
367 const std::string& name, const Param1& param1
368 ) {
369 typename BaseClass::CreatorType creator =
370
1/2
✓ Branch 1 taken 250 times.
✗ Branch 2 not taken.
250 BaseClass::find_creator(name);
371
2/4
✓ Branch 0 taken 250 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 250 times.
✗ Branch 4 not taken.
250 return creator == nullptr ? nullptr : (* creator)(param1);
372 }
373 };
374
375 /**
376 * \brief Helper macro to register a creator
377 * \details This declares a static instance of
378 * FactoryType::RegisterCreator to register a \p ConcreteType creator in
379 * \p FactoryType at program initialization time.
380 * \par Usage example:
381 * \code
382 * struct MyBaseClass { ... };
383 * typedef Factory<MyBaseClass> MyFactory;
384 *
385 * struct MyDerivedClass : MyBaseClass { ... };
386 * geo_register_creator(MyFactory, MyDerivedClass, "my_derived_class");
387 * \endcode
388 * \param[in] FactoryType identifies the target Factory
389 * \param[in] ConcreteType the type of the object to create
390 * \param[in] name name of the \p ConcreteType creator in the Factory
391 * \see Factory::RegisterCreator
392 */
393 #define geo_register_creator(FactoryType, ConcreteType, name) \
394 static FactoryType::RegisterCreator<ConcreteType> \
395 CPP_CONCAT(Factory_register_creator_, __LINE__) (name); \
396 geo_argused(CPP_CONCAT(Factory_register_creator_, __LINE__))
397 }
398
399
400 #ifdef GEO_COMPILER_CLANG
401 #pragma clang diagnostic pop
402 #endif
403
404 #endif
405