fork(1) download
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. #include <assert.h>
  5. #include <exception>
  6. #include <map>
  7. #include <memory>
  8. #include <typeindex>
  9. #include <unordered_map>
  10.  
  11. // Predefine template delegate factory
  12. template < typename R, typename... Args >
  13. class brGenericDelegate ;
  14.  
  15. // C++11 template alias to announce functor definition
  16. template < typename R, typename... Args >
  17. using brGenericDelegateType = std::function< R(Args...) > ;
  18.  
  19. class brDelegate
  20. {
  21. protected:
  22. brDelegate(){}
  23.  
  24. public:
  25. /** @brief Destructor */
  26. virtual ~brDelegate() = default ;
  27.  
  28. /**
  29. @brief Factory to create a delegate based on given functor.
  30. @param func The funtor which should be delegated.
  31. @return std::shared_ptr<brDelegate> The created delegate.
  32. */
  33. template < typename R, typename... Args >
  34. static std::shared_ptr<brDelegate> create( typename brGenericDelegate<R,Args...>::functor func )
  35. {
  36. return std::make_shared<brGenericDelegate<R,Args...>>(func) ;
  37. }
  38.  
  39. /**
  40. @brief Exectues the delegate.
  41.  
  42. This method call invokes the registered functor and returns an object of
  43. type R. Please take note that you have to ensure, that the return type R
  44. of the requested object and signatures of the argument list are equals
  45. to the functor types, otherwise a cast exception will thrown.
  46.  
  47. @throws std::bad_cast exception if type cast fails.
  48. @param args The arguments of the functor used on delegate.
  49. @return T The object of return type R.
  50. */
  51. template < typename R, typename... Args > R run( Args... args ) const
  52. {
  53. using derived_type = brGenericDelegate<R,Args...> ;
  54. return dynamic_cast< const derived_type& >(*this)(args...) ;
  55. }
  56. };
  57.  
  58. template < typename R, typename... Args >
  59. class brGenericDelegate : public brDelegate
  60. {
  61. public:
  62. using functor = brGenericDelegateType< R, Args... >;
  63. brGenericDelegate( functor f ) : fn(f) {}
  64.  
  65. R operator() ( Args... args ) const { return fn(args...) ; }
  66.  
  67. private:
  68. const functor fn ;
  69. };
  70.  
  71.  
  72. class brIOCContainer
  73. {
  74. private:
  75. // Define generic resolver template
  76. template < class R>
  77. class GenericResolver;
  78.  
  79. class Resolver
  80. {
  81. protected:
  82. Resolver(){}
  83.  
  84. public:
  85. /** @brief Destructor */
  86. virtual ~Resolver() = default;
  87.  
  88. template<typename R, typename ... ARGS>
  89. std::shared_ptr<R> run(ARGS&& ... args) const
  90. {
  91. using derived_type = GenericResolver<R>;
  92. auto rs = dynamic_cast< const derived_type& >(*this);
  93. return rs.template run<ARGS...>(std::forward<ARGS>(args)...);
  94. }
  95. };
  96.  
  97. template <typename T>
  98. class GenericResolver : public Resolver
  99. {
  100. public:
  101. GenericResolver(std::shared_ptr<brDelegate> delegate)
  102. :m_delegate(delegate){}
  103.  
  104. virtual ~GenericResolver() = default;
  105.  
  106. template<typename ... ARGS>
  107. std::shared_ptr<T> run(ARGS&& ... args) const
  108. {
  109. return std::make_shared<T>(
  110. m_delegate->run<T, ARGS...>(std::forward<ARGS>(args)...));
  111. }
  112.  
  113. std::shared_ptr<brDelegate> getDelegate(void) const {
  114. return m_delegate;
  115. }
  116.  
  117. private:
  118. std::shared_ptr<brDelegate> m_delegate = nullptr;
  119. };
  120.  
  121. class Component
  122. {
  123. public:
  124. Component(std::type_index type) : m_type(type){}
  125. ~Component(){}
  126.  
  127. std::type_index getType(void) const {
  128. return m_type;
  129. }
  130.  
  131. std::shared_ptr<Resolver> getResolver(void){
  132. return m_resolver;
  133. }
  134.  
  135. void setResolver(std::shared_ptr<Resolver> resolver){
  136. m_resolver = resolver;
  137. }
  138.  
  139. private:
  140. std::type_index m_type;
  141. std::shared_ptr<Resolver> m_resolver = nullptr;
  142. };
  143.  
  144. public:
  145. typedef std::multimap<std::type_index, std::shared_ptr<Component>> RegistryLookup_t;
  146. typedef std::pair<std::type_index, std::shared_ptr<Component>> RegistryEntry_t;
  147.  
  148. public:
  149. brIOCContainer(){}
  150. brIOCContainer(const brIOCContainer& copy){
  151. m_repository = copy.m_repository;
  152. }
  153. virtual ~brIOCContainer(){}
  154.  
  155. template <typename T>
  156. void registerType(void);
  157.  
  158. template <typename T, typename C>
  159. void registerByContract(void);
  160.  
  161. template <typename T, typename ... ARGS>
  162. std::shared_ptr<T> resolve(ARGS&& ... args) const;
  163.  
  164. template <typename T>
  165. bool contains(void) const;
  166.  
  167. template <typename T, typename C>
  168. bool contains(void) const;
  169.  
  170. private:
  171. template <typename T>
  172. void validate(void);
  173.  
  174. RegistryLookup_t::const_iterator find(std::type_index type) const{
  175. auto iter = m_repository.begin();
  176. while(iter!=m_repository.end()){
  177. if((iter->first) == type ||
  178. (iter->second)->getType() == type){
  179. break;
  180. }
  181. iter++;
  182. }
  183. return iter;
  184. }
  185.  
  186. /** Registered elements */
  187. RegistryLookup_t m_repository;
  188. };
  189.  
  190. /**
  191. @brief Register a single type T without abstraction..
  192.  
  193. This registry method ensures, that on each resolve of the type T a new
  194. object instance is created and returned.
  195.  
  196. @throws std::exception if type T has been already registered.
  197. */
  198. template <typename T>
  199. inline void brIOCContainer::registerType(void)
  200. {
  201. // check if type has been already registered
  202. auto iter = this->find(typeid(T));
  203. if(iter!=m_repository.end()){
  204. std::string msg = "[brIOCContainer]:registerType: A type: <";
  205. msg.append(typeid(T).name());
  206. msg.append("> has been already registered!");
  207. throw std::runtime_error(msg.c_str());
  208. }
  209.  
  210. // create delegate for transient object creation
  211. auto delegate = brDelegate::create<T>([]() -> T { return T(); });
  212.  
  213. // create component and register resolver using defined delegate
  214. auto component = std::make_shared<Component>(typeid(T));
  215. component->setResolver(std::make_shared<GenericResolver<T>>(delegate));
  216. m_repository.insert(RegistryEntry_t(typeid(T), component));
  217. }
  218.  
  219. /**
  220. @brief Register a type T as implementation of an interface or other concrete class C.
  221.  
  222. This registry method make it possible to register multiple types of an object which are based
  223. on the same interface or concrete class (by contract).
  224.  
  225. @note You have always resolve this registered objects by their concrete type T.
  226.  
  227. @throws std::exception if contract type C is not base of object type T.
  228. @throws std::xception if object Type T is already in use for contract type C.
  229. */
  230. template <typename T, typename C>
  231. inline void brIOCContainer::registerByContract(void)
  232. {
  233. // check if contract and type definitions are compatible
  234. assert((std::is_base_of<C,T>::value));
  235.  
  236. // check if type is already registered by contract
  237. if((this->contains<T,C>())){
  238. std::string msg = "[brIOCContainer]:registerByContract: An unnamed type: <";
  239. msg.append(typeid(T).name());
  240. msg.append("> has been already registered by contract!");
  241. throw std::runtime_error(msg.c_str());
  242. }
  243.  
  244. // create delegate for transient object creation based on concrete type
  245. auto delegate = brDelegate::create<T>([]() -> T { return T(); });
  246.  
  247. /* Set up component of the concrete type T and register this component
  248. using the contract type */
  249. auto component = std::make_shared<Component>(typeid(T));
  250. component->setResolver(std::make_shared<GenericResolver<T>>(delegate));
  251. m_repository.insert(RegistryEntry_t(typeid(C), component));
  252. }
  253.  
  254. /**
  255. @brief Returns one instance of the specified type T with argument constructor injection.
  256.  
  257. This specialized resolve method is used to create a transient object instance of requested
  258. type T and using given arguments as constructor parameters on creation. Please take note
  259. that you have to ensure that the correct amount and order of the arguments are given.
  260.  
  261. @note
  262. You should use this resolve mechanism if you have dependencies in your resolved instance,
  263. which could is different on each instance, i.e. path to external files.
  264.  
  265. @throws std::exception if argument list doesn't match with one of the constructors of type T.
  266. @throws std::exception if no registered type T could be found.
  267. @throws std::exception if the lifetime scope is not transient.
  268.  
  269. @return std::shared_ptr<T> The instance of the requested type T as shared_ptr.
  270. */
  271. template <typename T, typename ... ARGS>
  272. inline std::shared_ptr<T> brIOCContainer::resolve(ARGS&& ... args) const
  273. {
  274. std::type_index type = typeid(T);
  275. if(std::is_abstract<T>::value){
  276. std::string msg = "[brIOCContainer]:resolve: Cant resolve object of abstract type: ";
  277. msg.append(type.name());
  278. throw std::runtime_error(msg.c_str());
  279. }
  280.  
  281. auto iter = this->find(type);
  282. if(iter==m_repository.end()){
  283. std::string msg = "[brIOCContainer]:resolve: No entry found for requested class type: ";
  284. msg.append(type.name());
  285. throw std::runtime_error(msg.c_str());
  286. }
  287.  
  288. auto resolver = (iter->second)->getResolver();
  289. return resolver->run<T, ARGS...>(std::forward<ARGS>(args)...);
  290. }
  291.  
  292. /**
  293. @brief Returns status if entry of the specified type T is contained.
  294. @return bool True if entry is registered with same type, otherwise false.
  295. */
  296. template <typename T>
  297. inline bool brIOCContainer::contains(void) const
  298. {
  299. std::type_index type = typeid(T);
  300. auto iter = m_repository.find(type);
  301. return iter!=m_repository.end() ? true : false;
  302. }
  303.  
  304. /**
  305. @brief Returns status if entry of the specified type T is registered by contract type C.
  306. @return bool True if entry is registered, otherwise false.
  307. */
  308. template <typename T, typename C>
  309. inline bool brIOCContainer::contains(void) const
  310. {
  311. bool result = false;
  312. std::type_index type = typeid(T);
  313.  
  314. for(auto range = m_repository.equal_range(typeid(C));
  315. range.first != range.second; ++range.first){
  316.  
  317. if(type == (range.first->second)->getType()){
  318. result = true;
  319. break;
  320. }
  321. }
  322. return result;
  323. }
  324.  
  325. class IVehicle
  326. {
  327. public:
  328. IVehicle(){}
  329. virtual ~IVehicle() = default;
  330.  
  331. virtual const std::string& getBrand(void) const = 0;
  332. };
  333.  
  334. class Car : public IVehicle
  335. {
  336. public:
  337. Car():IVehicle(){}
  338. Car(const std::string& name):IVehicle(), m_name(name){}
  339.  
  340. const std::string& getBrand(void) const override{
  341. return m_name;
  342. }
  343.  
  344. private:
  345. std::string m_name = "Mustang GT 500";
  346. };
  347.  
  348. class Truck : public IVehicle
  349. {
  350. public:
  351. Truck():IVehicle(){}
  352. Truck(const std::string& name):IVehicle(), m_name(name){}
  353.  
  354. const std::string& getBrand(void) const override{
  355. return m_name;
  356. }
  357.  
  358. private:
  359. std::string m_name = "MAN TGX EfficientLine 2 in long-haul transport";
  360. };
  361.  
  362.  
  363. int main() {
  364.  
  365. brIOCContainer container;
  366. container.registerType<Truck>();
  367.  
  368. auto truck = container.resolve<Truck>();
  369. std::cout << "Got a truck: " << truck->getBrand() << std::endl;
  370.  
  371. container.registerByContract<Car, IVehicle>();
  372.  
  373. // This resolve is working
  374. auto car = container.resolve<Car>();
  375.  
  376. // This resolve occurs in a compiler failure
  377. //auto car = container.resolve<IVehicle>();
  378. std::cout << "Got a vehicle: " << car->getBrand() << std::endl;
  379.  
  380. return 0;
  381. }
Success #stdin #stdout 0s 3496KB
stdin
Standard input is empty
stdout
Got a truck: MAN TGX EfficientLine 2 in long-haul transport
Got a vehicle: Mustang GT 500