| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <typeinfo> | ||
| 4 | #include <typeindex> | ||
| 5 | #include <stdexcept> | ||
| 6 | #include <map> | ||
| 7 | #include <memory> | ||
| 8 | #include <cassert> | ||
| 9 | #include <iostream> | ||
| 10 | |||
| 11 | namespace na64dp { | ||
| 12 | |||
| 13 | namespace errors { | ||
| 14 | |||
| 15 | /// Excpetion thrown by virtual constructor instance when requested name is not | ||
| 16 | /// found among registered entries of certain type. | ||
| 17 | class CtrNotFound : public std::runtime_error { | ||
| 18 | private: | ||
| 19 | char _name[128]; | ||
| 20 | const std::type_info & _typeInfo; | ||
| 21 | public: | ||
| 22 | CtrNotFound( const std::string & name | ||
| 23 | , const std::type_info & typeInfo ) throw(); | ||
| 24 | const char * name() const throw() { return _name; } | ||
| 25 | const std::type_info & type_info() throw() { return _typeInfo; } | ||
| 26 | }; | ||
| 27 | |||
| 28 | /** Excpetion thrown by virtual constructor when there are | ||
| 29 | * no entries defined for requested type. | ||
| 30 | * | ||
| 31 | * \note This exception is thrown when the abstract type for the entities is | ||
| 32 | * defined together with its traits, but no entry was added until the | ||
| 33 | * construction was requested. Contrary, if VCtr is parameterized with | ||
| 34 | * incorrect type you got compiling error instead of this runtime exception as | ||
| 35 | * traits are not defined. | ||
| 36 | */ | ||
| 37 | class NoCtrsOfType : public std::runtime_error { | ||
| 38 | private: | ||
| 39 | const std::type_info & _typeInfo; | ||
| 40 | public: | ||
| 41 | NoCtrsOfType( const std::type_info & typeInfo ) throw(); | ||
| 42 | const std::type_info & type_info() throw() { return _typeInfo; } | ||
| 43 | }; | ||
| 44 | |||
| 45 | }; | ||
| 46 | |||
| 47 | /// Virtual constructor traits; shall define `Constructor` type | ||
| 48 | template<typename T> struct CtrTraits; | ||
| 49 | |||
| 50 | /**\brief Common virtual constructor dispatcher implementation | ||
| 51 | * | ||
| 52 | * The "virtual constructor" is a common idiom that implies instantiation of | ||
| 53 | * classes with some common base by runtime arguments. This class implements | ||
| 54 | * a generalized registry for virtual multiple constructors based on the C++ | ||
| 55 | * type ID of the bases. | ||
| 56 | * | ||
| 57 | * \note Dynamically loaded modules uses this singleton before main logging | ||
| 58 | * is initialized. | ||
| 59 | */ | ||
| 60 | class VCtr { | ||
| 61 | public: | ||
| 62 | /// Abstract base class for all virtual constructors | ||
| 63 | class iCtrRegistry {}; | ||
| 64 | |||
| 65 | /// Virtual constructor registry -- indexes constructors of classes with | ||
| 66 | /// common ancestor by their string identifiers (names) | ||
| 67 | template<typename T> | ||
| 68 | class CtrRegistry : public iCtrRegistry | ||
| 69 | , public std::map< std::string | ||
| 70 | , std::pair<typename CtrTraits<T>::Constructor, std::string> > { | ||
| 71 | public: | ||
| 72 | /// Registers new constructor | ||
| 73 | 2 | bool register_class( const std::string & name | |
| 74 | , typename CtrTraits<T>::Constructor ctr | ||
| 75 | , const std::string & description ) { | ||
| 76 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
|
2 | auto ir = this->emplace( name, std::make_pair(ctr, description) ); |
| 77 | 2 | return ir.second; | |
| 78 | } | ||
| 79 | /// Constructs typed entry | ||
| 80 | template<typename... ArgsT> T * | ||
| 81 | 3 | make( const std::string & name, ArgsT &... args) { | |
| 82 |
1/1✓ Branch 1 taken 3 times.
|
3 | auto it = this->find(name); |
| 83 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
|
3 | if( this->end() == it ) { |
| 84 | 1 | throw errors::CtrNotFound( name, typeid(T) ); | |
| 85 | } | ||
| 86 |
1/1✓ Branch 2 taken 2 times.
|
4 | return it->second.first( args... ); |
| 87 | } | ||
| 88 | }; | ||
| 89 | private: | ||
| 90 | /// Pointer to global self instance of the registry | ||
| 91 | static VCtr * _self; | ||
| 92 | /// Index of all registries | ||
| 93 | std::map< std::type_index, std::unique_ptr<iCtrRegistry> > _regs; | ||
| 94 | public: | ||
| 95 | /**\brief Returns registry instance of certain base type (const) | ||
| 96 | * | ||
| 97 | * Raises `NoCtrsOfType` exception if type does not exist. | ||
| 98 | */ | ||
| 99 | 3 | template<typename T> const CtrRegistry<T> & registry() const { | |
| 100 |
1/1✓ Branch 2 taken 3 times.
|
3 | auto it = _regs.find( typeid(T) ); |
| 101 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
3 | if( _regs.end() == it ) { |
| 102 | // In principle, this exception shall never be thrown | ||
| 103 | ✗ | throw errors::NoCtrsOfType(typeid(T)); | |
| 104 | } | ||
| 105 | 3 | const CtrRegistry<T> * reg = static_cast<const CtrRegistry<T> *>(it->second.get()); | |
| 106 | 3 | return *reg; | |
| 107 | } | ||
| 108 | /**\brief Returns registry instance of certain base type | ||
| 109 | * | ||
| 110 | * Raises `NoCtrsOfType` exception if type does not exist. | ||
| 111 | */ | ||
| 112 | 3 | template<typename T> CtrRegistry<T> & registry() { | |
| 113 | 3 | return const_cast<CtrRegistry<T> &>(const_cast<const VCtr*>(this)->registry<T>()); | |
| 114 | } | ||
| 115 | |||
| 116 | /// Registers new entry in the corresponding registry | ||
| 117 | template<typename T> bool | ||
| 118 | 2 | register_class( const std::string & name | |
| 119 | , typename CtrTraits<T>::Constructor ctr | ||
| 120 | , const std::string & description ) { | ||
| 121 |
1/1✓ Branch 2 taken 2 times.
|
2 | auto it = _regs.find( typeid(T) ); |
| 122 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
|
2 | if( _regs.end() == it ) { |
| 123 |
2/3✓ Branch 1 taken 1 times.
✓ Branch 5 taken 1 times.
✗ Branch 8 not taken.
|
1 | auto ir = _regs.emplace( typeid(T), new CtrRegistry<T>() ); |
| 124 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | assert( ir.second ); |
| 125 | 1 | it = ir.first; | |
| 126 | } | ||
| 127 | 2 | CtrRegistry<T> * regPtr = static_cast<CtrRegistry<T>*>(it->second.get()); | |
| 128 |
1/1✓ Branch 1 taken 2 times.
|
4 | return regPtr->register_class(name, ctr, description); |
| 129 | } | ||
| 130 | |||
| 131 | /// Constructs entity of type `T` with arguments provided by args list. | ||
| 132 | template<typename T, typename... ArgsT> T * | ||
| 133 | 3 | make( const std::string & name, ArgsT & ... args ) { | |
| 134 | 3 | return registry<T>().make( name, args... ); | |
| 135 | } | ||
| 136 | |||
| 137 | /// Returns global instance of the virtual constructor object | ||
| 138 | 5 | static VCtr & self() { | |
| 139 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
5 | if( _self ) return *_self; |
| 140 | 1 | return *( _self = new VCtr() ); | |
| 141 | } | ||
| 142 | }; | ||
| 143 | |||
| 144 | } // namespace na64dp | ||
| 145 | |||
| 146 | /* \defgroup vctr-defs Virtual constructor definitions | ||
| 147 | * | ||
| 148 | * This group contains C/C++ preprocessor macros that define a runtime | ||
| 149 | * extension of a certain type. They typically start with `REGISTER_` prefix | ||
| 150 | * and are used to shorten the definition of new "virtual constructor" | ||
| 151 | * function (see `VCtr`). | ||
| 152 | * | ||
| 153 | * Note that these macros not necessarily accomplish the type registration | ||
| 154 | * task -- one can still invoke the `VCtr<>::register_class()` directly. Though | ||
| 155 | * these macros significantly improve readability, they migt be avoided in | ||
| 156 | * favor of direct calls in case of, say, explicit template instantiation. */ | ||
| 157 |