#include <iostream>
using namespace std;
#include <assert.h>
#include <exception>
#include <map>
#include <memory>
#include <typeindex>
#include <unordered_map>
// Predefine template delegate factory
template < typename R, typename... Args >
class brGenericDelegate ;
// C++11 template alias to announce functor definition
template < typename R, typename... Args >
using brGenericDelegateType = std::function< R(Args...) > ;
class brDelegate
{
protected:
brDelegate(){}
public:
/** @brief Destructor */
virtual ~brDelegate() = default ;
/**
@brief Factory to create a delegate based on given functor.
@param func The funtor which should be delegated.
@return std::shared_ptr<brDelegate> The created delegate.
*/
template < typename R, typename... Args >
static std::shared_ptr<brDelegate> create( typename brGenericDelegate<R,Args...>::functor func )
{
return std::make_shared<brGenericDelegate<R,Args...>>(func) ;
}
/**
@brief Exectues the delegate.
This method call invokes the registered functor and returns an object of
type R. Please take note that you have to ensure, that the return type R
of the requested object and signatures of the argument list are equals
to the functor types, otherwise a cast exception will thrown.
@throws std::bad_cast exception if type cast fails.
@param args The arguments of the functor used on delegate.
@return T The object of return type R.
*/
template < typename R, typename... Args > R run( Args... args ) const
{
using derived_type = brGenericDelegate<R,Args...> ;
return dynamic_cast< const derived_type& >(*this)(args...) ;
}
};
template < typename R, typename... Args >
class brGenericDelegate : public brDelegate
{
public:
using functor = brGenericDelegateType< R, Args... >;
brGenericDelegate( functor f ) : fn(f) {}
R operator() ( Args... args ) const { return fn(args...) ; }
private:
const functor fn ;
};
class brIOCContainer
{
private:
// Define generic resolver template
template < class R>
class GenericResolver;
class Resolver
{
protected:
Resolver(){}
public:
/** @brief Destructor */
virtual ~Resolver() = default;
template<typename R, typename ... ARGS>
std::shared_ptr<R> run(ARGS&& ... args) const
{
using derived_type = GenericResolver<R>;
auto rs = dynamic_cast< const derived_type& >(*this);
return rs.template run<ARGS...>(std::forward<ARGS>(args)...);
}
};
template <typename T>
class GenericResolver : public Resolver
{
public:
GenericResolver(std::shared_ptr<brDelegate> delegate)
:m_delegate(delegate){}
virtual ~GenericResolver() = default;
template<typename ... ARGS>
std::shared_ptr<T> run(ARGS&& ... args) const
{
return std::make_shared<T>(
m_delegate->run<T, ARGS...>(std::forward<ARGS>(args)...));
}
std::shared_ptr<brDelegate> getDelegate(void) const {
return m_delegate;
}
private:
std::shared_ptr<brDelegate> m_delegate = nullptr;
};
class Component
{
public:
Component(std::type_index type) : m_type(type){}
~Component(){}
std::type_index getType(void) const {
return m_type;
}
std::shared_ptr<Resolver> getResolver(void){
return m_resolver;
}
void setResolver(std::shared_ptr<Resolver> resolver){
m_resolver = resolver;
}
private:
std::type_index m_type;
std::shared_ptr<Resolver> m_resolver = nullptr;
};
public:
typedef std::multimap<std::type_index, std::shared_ptr<Component>> RegistryLookup_t;
typedef std::pair<std::type_index, std::shared_ptr<Component>> RegistryEntry_t;
public:
brIOCContainer(){}
brIOCContainer(const brIOCContainer& copy){
m_repository = copy.m_repository;
}
virtual ~brIOCContainer(){}
template <typename T>
void registerType(void);
template <typename T, typename C>
void registerByContract(void);
template <typename T, typename ... ARGS>
std::shared_ptr<T> resolve(ARGS&& ... args) const;
template <typename T>
bool contains(void) const;
template <typename T, typename C>
bool contains(void) const;
private:
template <typename T>
void validate(void);
RegistryLookup_t::const_iterator find(std::type_index type) const{
auto iter = m_repository.begin();
while(iter!=m_repository.end()){
if((iter->first) == type ||
(iter->second)->getType() == type){
break;
}
iter++;
}
return iter;
}
/** Registered elements */
RegistryLookup_t m_repository;
};
/**
@brief Register a single type T without abstraction..
This registry method ensures, that on each resolve of the type T a new
object instance is created and returned.
@throws std::exception if type T has been already registered.
*/
template <typename T>
inline void brIOCContainer::registerType(void)
{
// check if type has been already registered
auto iter = this->find(typeid(T));
if(iter!=m_repository.end()){
std::string msg = "[brIOCContainer]:registerType: A type: <";
msg.append(typeid(T).name());
msg.append("> has been already registered!");
throw std::runtime_error(msg.c_str());
}
// create delegate for transient object creation
auto delegate = brDelegate::create<T>([]() -> T { return T(); });
// create component and register resolver using defined delegate
auto component = std::make_shared<Component>(typeid(T));
component->setResolver(std::make_shared<GenericResolver<T>>(delegate));
m_repository.insert(RegistryEntry_t(typeid(T), component));
}
/**
@brief Register a type T as implementation of an interface or other concrete class C.
This registry method make it possible to register multiple types of an object which are based
on the same interface or concrete class (by contract).
@note You have always resolve this registered objects by their concrete type T.
@throws std::exception if contract type C is not base of object type T.
@throws std::xception if object Type T is already in use for contract type C.
*/
template <typename T, typename C>
inline void brIOCContainer::registerByContract(void)
{
// check if contract and type definitions are compatible
assert((std::is_base_of<C,T>::value));
// check if type is already registered by contract
if((this->contains<T,C>())){
std::string msg = "[brIOCContainer]:registerByContract: An unnamed type: <";
msg.append(typeid(T).name());
msg.append("> has been already registered by contract!");
throw std::runtime_error(msg.c_str());
}
// create delegate for transient object creation based on concrete type
auto delegate = brDelegate::create<T>([]() -> T { return T(); });
/* Set up component of the concrete type T and register this component
using the contract type */
auto component = std::make_shared<Component>(typeid(T));
component->setResolver(std::make_shared<GenericResolver<T>>(delegate));
m_repository.insert(RegistryEntry_t(typeid(C), component));
}
/**
@brief Returns one instance of the specified type T with argument constructor injection.
This specialized resolve method is used to create a transient object instance of requested
type T and using given arguments as constructor parameters on creation. Please take note
that you have to ensure that the correct amount and order of the arguments are given.
@note
You should use this resolve mechanism if you have dependencies in your resolved instance,
which could is different on each instance, i.e. path to external files.
@throws std::exception if argument list doesn't match with one of the constructors of type T.
@throws std::exception if no registered type T could be found.
@throws std::exception if the lifetime scope is not transient.
@return std::shared_ptr<T> The instance of the requested type T as shared_ptr.
*/
template <typename T, typename ... ARGS>
inline std::shared_ptr<T> brIOCContainer::resolve(ARGS&& ... args) const
{
std::type_index type = typeid(T);
if(std::is_abstract<T>::value){
std::string msg = "[brIOCContainer]:resolve: Cant resolve object of abstract type: ";
msg.append(type.name());
throw std::runtime_error(msg.c_str());
}
auto iter = this->find(type);
if(iter==m_repository.end()){
std::string msg = "[brIOCContainer]:resolve: No entry found for requested class type: ";
msg.append(type.name());
throw std::runtime_error(msg.c_str());
}
auto resolver = (iter->second)->getResolver();
return resolver->run<T, ARGS...>(std::forward<ARGS>(args)...);
}
/**
@brief Returns status if entry of the specified type T is contained.
@return bool True if entry is registered with same type, otherwise false.
*/
template <typename T>
inline bool brIOCContainer::contains(void) const
{
std::type_index type = typeid(T);
auto iter = m_repository.find(type);
return iter!=m_repository.end() ? true : false;
}
/**
@brief Returns status if entry of the specified type T is registered by contract type C.
@return bool True if entry is registered, otherwise false.
*/
template <typename T, typename C>
inline bool brIOCContainer::contains(void) const
{
bool result = false;
std::type_index type = typeid(T);
for(auto range = m_repository.equal_range(typeid(C));
range.first != range.second; ++range.first){
if(type == (range.first->second)->getType()){
result = true;
break;
}
}
return result;
}
class IVehicle
{
public:
IVehicle(){}
virtual ~IVehicle() = default;
virtual const std::string& getBrand(void) const = 0;
};
class Car : public IVehicle
{
public:
Car():IVehicle(){}
Car(const std::string& name):IVehicle(), m_name(name){}
const std::string& getBrand(void) const override{
return m_name;
}
private:
std::string m_name = "Mustang GT 500";
};
class Truck : public IVehicle
{
public:
Truck():IVehicle(){}
Truck(const std::string& name):IVehicle(), m_name(name){}
const std::string& getBrand(void) const override{
return m_name;
}
private:
std::string m_name = "MAN TGX EfficientLine 2 in long-haul transport";
};
int main() {
brIOCContainer container;
container.registerType<Truck>();
auto truck = container.resolve<Truck>();
std::cout << "Got a truck: " << truck->getBrand() << std::endl;
container.registerByContract<Car, IVehicle>();
// This resolve is working
auto car = container.resolve<Car>();
// This resolve occurs in a compiler failure
//auto car = container.resolve<IVehicle>();
std::cout << "Got a vehicle: " << car->getBrand() << std::endl;
return 0;
}