#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< std::shared_ptr<R>(Args...) > ;
class brDelegate
{
protected:
brDelegate(){}
public:
virtual ~brDelegate() = default ;
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) ;
}
template < typename R, typename... Args > std::shared_ptr<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) {}
std::shared_ptr<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;
};
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>([]() -> std::shared_ptr<T> { return std::make_shared<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));
}
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<C>([]() -> std::shared_ptr<C> {
//return dynamic_pointer_cast<C>(std::make_shared<T>());
return std::make_shared<T>();
});
/* Set up component of the concrete type T and register this component
using the contract type */
auto component = std::make_shared<Component>(typeid(C));
component->setResolver(std::make_shared<GenericResolver<C>>(delegate));
m_repository.insert(RegistryEntry_t(typeid(C), component));
}
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)...);
}
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;
}
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.registerType<Car>();
// This resolve is working
auto car1 = container.resolve<Car>();
std::cout << "Got a vehicle: " << car1->getBrand() << std::endl;
container.registerByContract<Car, IVehicle>();
// This resolve occurs in a compiler failure
auto car2 = container.resolve<IVehicle>();
std::cout << "Got a vehicle: " << car2->getBrand() << std::endl;
return 0;
}