diff --git a/Exception.h b/Exception.h index 4fdfcf8..f9248ed 100644 --- a/Exception.h +++ b/Exception.h @@ -5,6 +5,7 @@ #pragma once #include "StdString.h" +#include #define YAUL_COPYVARARGS(fmt) va_list argList; va_start(argList, fmt); set(fmt, argList); va_end(argList) #define YAUL_STANDARD_EXCEPTION(E) \ @@ -46,6 +47,7 @@ namespace YaulCommons CStdString tmps; tmps.FormatV(fmt, argList); message = tmps; + std::cout << "EXCEPTION:" << getExceptionType() << ":" << getMessage() << std::endl; } /** @@ -60,6 +62,7 @@ namespace YaulCommons public: inline const char* getMessage() const { return message.c_str(); } + inline const char* getExceptionType() const { return classname.c_str(); } }; } diff --git a/di.cpp b/di.cpp index a906289..5fcff92 100644 --- a/di.cpp +++ b/di.cpp @@ -12,16 +12,6 @@ namespace di { namespace internal { - const std::string RequirementBase::toString() const - { - CStdString msg; - if (specifiesId) - msg.Format("(%s requires %s with id %s)",instance.getTypeInfo().name(), parameter.getTypeInfo().name(), requiredId.c_str()); - else - msg.Format("(%s requires %s)",instance.getTypeInfo().name(), parameter.getTypeInfo().name()); - return msg; - } - void* InstanceBase::convertTo(const TypeBase& typeToConvertTo) const throw (DependencyInjectionException) { const void* obj = getConcrete(); @@ -41,7 +31,7 @@ namespace di return NULL; } - bool InstanceBase::canConvertTo(const TypeBase& typeToConvertTo) const + bool InstanceBase::canConvertTo(const internal::TypeBase& typeToConvertTo) const { for(std::vector::const_iterator it = providesTheseTypes.begin(); it != providesTheseTypes.end(); it++) { @@ -55,28 +45,31 @@ namespace di } } - internal::InstanceBase* Context::find(const std::type_info& typeInfo) + internal::InstanceBase* Context::find(const internal::TypeBase& typeInfo, const char* id, bool exact) { for(std::vector::iterator it = instances.begin(); it != instances.end(); it++) { internal::InstanceBase* instance = (*it); - if (instance->type.getTypeInfo() == typeInfo) + if (((exact && instance->type.getTypeInfo() == typeInfo.getTypeInfo()) || + (!exact && instance->canConvertTo(typeInfo))) && + (id == NULL || instance->id == id)) return instance; } return NULL; } - internal::InstanceBase* Context::find(const char* id, const std::type_info& typeInfo) + void Context::findAll(std::vector& ret, const internal::TypeBase& typeInfo, const char* id, bool exact) { for(std::vector::iterator it = instances.begin(); it != instances.end(); it++) { internal::InstanceBase* instance = (*it); - if (instance->type.getTypeInfo() == typeInfo && instance->id == id) - return instance; + if (((exact && instance->type.getTypeInfo() == typeInfo.getTypeInfo()) || + (!exact && instance->canConvertTo(typeInfo))) && + (id == NULL || instance->id == id)) + ret.push_back(instance); } - return NULL; } void Context::resetInstances() @@ -126,9 +119,10 @@ namespace di } } - // clear out the instances. - resetInstances(); } + + // clear out the instances. + resetInstances(); } void Context::clear() @@ -199,38 +193,7 @@ namespace di for (std::vector::iterator rit = requirements.begin(); rit != requirements.end(); rit++) { internal::RequirementBase* requirement = (*rit); - - // now go through all of the instances and see which one satisfies - // the requirements. Make sure that no more than one does. - std::vector satisfies; - - for(std::vector::iterator it2 = instances.begin(); it2 != instances.end(); it2++) - { - internal::InstanceBase* comp = (*it2); - - if (requirement->isSatisfiedBy(comp)) - satisfies.push_back(comp); - } - - if (satisfies.size() < 1) - { - resetInstances(); - throw DependencyInjectionException("Cannot satisfy the requirement of \"%s\" which requires \"%s\".", instance->toString().c_str(), requirement->toString().c_str()); - } - else if (!requirement->satisfiedWithSet() && satisfies.size() > 1) - { - resetInstances(); - throw DependencyInjectionException("Ambiguous requirement of \"%s\" for \"%s\".", instance->toString().c_str(), requirement->toString().c_str()); - } - - // ok then, we need to plug in the dependency - if (requirement->satisfiedWithSet()) - requirement->satisfyWithSet(satisfies); - else - { - internal::InstanceBase* dep = satisfies.front(); - requirement->satisfyWith(dep); - } + requirement->satisfy(instance,this); } } diff --git a/di.h b/di.h index 7e23518..a74d7a6 100644 --- a/di.h +++ b/di.h @@ -226,10 +226,9 @@ namespace di */ template class Type : public internal::TypeBase { - const char* objId; public: - inline Type() : internal::TypeBase(typeid(T)), objId(NULL) {} - inline Type(const char* id) : internal::TypeBase(typeid(T)), objId(id) {} + inline Type() : internal::TypeBase(typeid(T)) {} + inline Type(const char* id) : internal::TypeBase(id,typeid(T)) {} virtual ~Type() {} /** @@ -250,11 +249,9 @@ namespace di typedef T* type; - inline T* find(Context* context) const throw (DependencyInjectionException); + inline T* findProvides(Context* context) const throw (DependencyInjectionException); inline bool available(Context* context) const; - - inline const char* getId() const { return objId; } - }; + }; /** * A Constant value can be supplied to satisfy constructor requirements during @@ -270,10 +267,13 @@ namespace di inline Constant(T val) : instance(val) {} inline Constant(const Constant& o) : instance(o.instance) {} - inline const T& find(Context* context) { return instance; } + inline const T& findProvides(Context* context) { return instance; } inline bool available(Context* context) { return true; } }; + // Nothing to see here, move along ... + #include "internal/direquirement.h" + /** * This template allows the declaration of object instances in a context. */ @@ -339,9 +339,9 @@ namespace di * dependency. Using a Ref you can alternatively supply a name for the * object that this instance requires. */ - template inline Instance& requires(const Type& dependency, typename internal::Requirement::Setter setter) + template inline Instance& requires(const Type& dependency, typename internal::Setter::type setter) { - requirements.push_back(new internal::Requirement(dependency.getId(), this,setter)); + requirements.push_back(new internal::Requirement(dependency,setter)); return *this; } @@ -349,9 +349,9 @@ namespace di * Use this method to declare that this instance requires a particular * dependency. */ - template inline Instance& requiresAll(const Type& dependency, typename internal::Requirement::SetterAll setter) + template inline Instance& requiresAll(const Type& dependency, typename internal::SetterAll::type setter) { - requirements.push_back(new internal::Requirement(this,setter)); + requirements.push_back(new internal::RequirementAll(dependency,setter)); return *this; } @@ -385,7 +385,6 @@ namespace di * Returns the underlying instance. */ inline T* get() { return (T*)getConcrete(); } - }; // Still nothing to see here, move along ... @@ -410,8 +409,9 @@ namespace di public: - internal::InstanceBase* find(const std::type_info& typeInfo); - internal::InstanceBase* find(const char* id, const std::type_info& typeInfo); + internal::InstanceBase* find(const internal::TypeBase& typeInfo,const char* id = NULL, bool exact = true); + + void findAll(std::vector& ret, const internal::TypeBase& typeInfo,const char* id = NULL, bool exact = true); virtual ~Context() { clear(); } @@ -557,26 +557,14 @@ namespace di */ void clear(); - /** - * Allows retrieving an object by its type. If there is more than - * one instance that is of this type, it will simply return the - * first one it finds in the context. - */ - template T* get(Type typeToFind) - { - internal::InstanceBase* ret = find(typeToFind.getTypeInfo()); - - return ret != NULL ? ((Instance*)ret)->get() : NULL; - } - /** * Allows retrieving an object by its type and Id. If there is more than * one instance that is of this type, it will simply return the * first one it finds in the context. */ - template T* get(const char* id, Type typeToFind) + template T* get(const Type& typeToFind, const char* id = NULL) { - internal::InstanceBase* ret = find(id,typeToFind.getTypeInfo()); + internal::InstanceBase* ret = find(typeToFind,id); return ret != NULL ? ((Instance*)ret)->get() : NULL; } @@ -593,17 +581,8 @@ namespace di inline bool isStarted() { return curPhase == started; } }; - template inline T* Type::find(Context* context) const throw (DependencyInjectionException) - { - internal::InstanceBase* ip1 = (objId == NULL ? context->find(this->getTypeInfo()) : context->find(objId,this->getTypeInfo())); - return (type)ip1->convertTo(*this); - } - - template inline bool Type::available(Context* context) const - { - internal::InstanceBase* inst = (objId == NULL ? context->find(this->getTypeInfo()) : context->find(objId,this->getTypeInfo())); - return inst != NULL && inst->instantiated(); - } + // Nothing to see here, move along ... + #include "internal/diimpl.h" } diff --git a/internal/dibase.h b/internal/dibase.h index baf3df2..fbbd736 100644 --- a/internal/dibase.h +++ b/internal/dibase.h @@ -34,27 +34,32 @@ namespace internal friend class FactoryBase; protected: + const char* objId; const std::type_info* type; - inline TypeBase(const std::type_info& type_) : type(&type_) + inline TypeBase(const std::type_info& type_) : objId(NULL), type(&type_) { #ifdef DI__DEPENDENCY_INJECTION_DEBUG std::cout << "Creating type:" << type_.name() << std::endl; #endif } - inline TypeBase(const TypeBase& other) : type(other.type) {} - inline TypeBase& operator=(const TypeBase& other) { type=other.type; return *this; } - - public: + inline TypeBase(const char* id, const std::type_info& type_) : objId(id), type(&type_) + { +#ifdef DI__DEPENDENCY_INJECTION_DEBUG + std::cout << "Creating type:" << type_.name() << std::endl; +#endif + } - inline bool operator==(const TypeBase& other) const { return (*type) == (*(other.type)); } + inline TypeBase(const TypeBase& other) : objId(other.objId), type(other.type) {} + inline TypeBase& operator=(const TypeBase& other) { objId = other.objId; type=other.type; return *this; } - inline bool operator!=(const TypeBase& other) const { return (*type) != (*(other.type)); } + public: + inline bool sameType(const TypeBase& other) const { return (*type) == (*(other.type)); } inline const std::string toString() const { return type->name(); } - inline const std::type_info& getTypeInfo() const { return (*type); } + inline const char* getId() const { return objId; } }; /** @@ -66,10 +71,8 @@ namespace internal friend class InstanceBase; protected: inline explicit TypeConverterBase(const std::type_info& type) : TypeBase(type) {} - virtual void* doConvert(void*) = 0; - - inline bool isTypeToConvertTo(const TypeBase& to) { return (*this) == to; } + inline bool isTypeToConvertTo(const TypeBase& to) { return (*this).sameType(to); } }; /** @@ -144,82 +147,20 @@ namespace internal friend class di::Context; protected: - TypeBase instance; - TypeBase parameter; - - std::string requiredId; - bool specifiesId; - - inline RequirementBase(const char* id, const std::type_info& obj, const std::type_info& param) : - instance(obj), parameter(param), specifiesId(id != NULL) { if (id != NULL) requiredId = id; } - + inline RequirementBase() { } virtual ~RequirementBase() {} - inline bool isSatisfiedBy(const InstanceBase* dep) - { - return specifiesId ? (dep->hasId ? (requiredId == dep->id && dep->canConvertTo(parameter)) : false) : dep->canConvertTo(parameter); - } - - virtual const std::string toString() const; - - virtual void satisfyWith(InstanceBase* dependency) throw (DependencyInjectionException) = 0; - virtual bool satisfiedWithSet() = 0; - virtual void satisfyWithSet(const std::vector& dep) throw (DependencyInjectionException) = 0; + virtual void satisfy(InstanceBase* instance, Context* context) throw (DependencyInjectionException) = 0; }; - /** - * This template is used to declare a requirement of an object that - * needs to be fulfilled in the container. - */ - template class Requirement : public internal::RequirementBase + template struct Setter { - friend class di::Instance; - public: - typedef void (T::*Setter)(D*); - typedef void (T::*SetterAll)(const std::vector); - - private: - Setter setter; - SetterAll setterAll; - InstanceBase* requirementOf; - - inline Requirement(InstanceBase* thingWithSetter, SetterAll func) : - RequirementBase(NULL, typeid(T), typeid(D)), setter(NULL), - setterAll(func), requirementOf(thingWithSetter) {} - inline Requirement(const char* id, InstanceBase* thingWithSetter, Setter func) : - RequirementBase(id,typeid(T), typeid(D)), setter(func), setterAll(NULL), - requirementOf(thingWithSetter) {} - - inline T* getRequirementOf() { return (T*)requirementOf->getConcrete(); } - - inline void injectRequirement(D* objectToInject) { ((*getRequirementOf()).*(setter))(objectToInject); } - inline void injectRequirementSet(const std::vector& objectToInject) { ((*getRequirementOf()).*(setterAll))(objectToInject); } - - inline virtual void satisfyWith(InstanceBase* dep) throw (DependencyInjectionException) - { -#ifdef DI__DEPENDENCY_INJECTION_DEBUG - std::cout << "requirement:" << toString() << " is satisfied by " << dep->toString() << std::endl; -#endif - D* actualDep = (D*)dep->convertTo(parameter); - if (actualDep == NULL) - throw DependencyInjectionException("Can't satisfy a requirement for '%s' with '%s.'",toString().c_str(),dep->toString().c_str()); - injectRequirement(actualDep); - } - - inline virtual bool satisfiedWithSet() { return setterAll != NULL; } + typedef void (T::*type)(D); + }; - inline virtual void satisfyWithSet(const std::vector& dep) throw (DependencyInjectionException) - { -#ifdef DI__DEPENDENCY_INJECTION_DEBUG - std::cout << "requirement:" << toString() << " is satisfied by: " << std::endl; - for (std::vector::const_iterator depIt = dep.begin(); depIt != dep.end(); depIt++) - std::cout << " " << (*depIt)->toString() << std::endl; -#endif - std::vector param; - for (std::vector::const_iterator depIt = dep.begin(); depIt != dep.end(); depIt++) - param.push_back((D*)(*depIt)->convertTo(parameter)); - injectRequirementSet(param); - } + template struct SetterAll + { + typedef void (T::*type)(const std::vector); }; class FactoryBase @@ -233,6 +174,5 @@ namespace internal virtual bool dependenciesSatisfied(Context* context) = 0; }; - } diff --git a/internal/difactories.h b/internal/difactories.h index 92870c6..670c0df 100644 --- a/internal/difactories.h +++ b/internal/difactories.h @@ -47,7 +47,7 @@ namespace internal inline virtual void* create(Context* context) throw (DependencyInjectionException) { - return new M(p1.find(context)); + return new M(p1.findProvides(context)); } }; @@ -71,7 +71,7 @@ namespace internal inline virtual void* create(Context* context) throw (DependencyInjectionException) { - return new M(p1.find(context),p2.find(context)); + return new M(p1.findProvides(context),p2.findProvides(context)); } }; @@ -96,7 +96,7 @@ namespace internal inline virtual void* create(Context* context) throw (DependencyInjectionException) { - return new M(p1.find(context),p2.find(context),p3.find(context)); + return new M(p1.findProvides(context),p2.findProvides(context),p3.findProvides(context)); } }; @@ -121,7 +121,7 @@ namespace internal inline virtual void* create(Context* context) throw (DependencyInjectionException) { - return new M(p1.find(context),p2.find(context),p3.find(context),p4.find(context)); + return new M(p1.findProvides(context),p2.findProvides(context),p3.findProvides(context),p4.findProvides(context)); } }; //======================================================================= @@ -136,4 +136,5 @@ namespace internal inline StaticSetterCaller(StaticSetter settr) : setter(settr) {} inline void set(T* instance) { (*setter)(instance); }; }; + } diff --git a/internal/diimpl.h b/internal/diimpl.h new file mode 100644 index 0000000..b1c625d --- /dev/null +++ b/internal/diimpl.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 + */ + +#pragma once + +// This file should NEVER be included independently. It is part of the internals of +// the di.h file and simply separated +#ifndef DI__DEPENDENCY_INJECTION__H +#error "Please don't include \"diimpl.h\" directly." +#endif + +namespace internal +{ + template inline void Requirement::satisfy(InstanceBase* instance, Context* context) throw (DependencyInjectionException) + { +#ifdef DI__DEPENDENCY_INJECTION_DEBUG + std::cout << "requirement:" << toString() << " is satisfied by " << dep->toString() << std::endl; +#endif + std::vector satisfiedBy; + context->findAll(satisfiedBy,parameter,parameter.getId(),false); + if (satisfiedBy.size() == 0) + throw DependencyInjectionException("Cannot satisfy the requirement of \"%s\" which requires \"%s\".", instance->toString().c_str(), parameter.toString().c_str()); + if (satisfiedBy.size() > 1) + throw DependencyInjectionException("Ambiguous requirement of \"%s\" for \"%s\".", instance->toString().c_str(), parameter.toString().c_str()); + InstanceBase* dep = satisfiedBy.front(); + (((T*)instance->getConcrete())->*(setter)) ((D*)dep->getConcrete()); + } + + template inline void RequirementAll::satisfy(InstanceBase* instance, Context* context) throw (DependencyInjectionException) + { +#ifdef DI__DEPENDENCY_INJECTION_DEBUG + std::cout << "requirement:" << toString() << " is satisfied by " << dep->toString() << std::endl; +#endif + std::vector satisfiedBy; + context->findAll(satisfiedBy,parameter,parameter.getId(),false); + if (satisfiedBy.size() == 0) + throw DependencyInjectionException("Cannot satisfy the requirement of \"%s\" which requires \"%s\".", instance->toString().c_str(), parameter.toString().c_str()); + std::vector instances; + for(std::vector::iterator it = satisfiedBy.begin(); it != satisfiedBy.end(); it++) + instances.push_back((D*)(*it)->getConcrete()); + (((T*)instance->getConcrete())->*(setter)) (instances); + } +} + +template inline T* Type::findProvides(Context* context) const throw (DependencyInjectionException) +{ + internal::InstanceBase* inst = context->find(*this,objId,false); + return inst ? (type)(inst->convertTo(*this)) : NULL; +} + +template inline bool Type::available(Context* context) const +{ + internal::InstanceBase* inst = context->find(*this,objId,false); + return inst != NULL && inst->instantiated(); +} + diff --git a/internal/direquirement.h b/internal/direquirement.h new file mode 100644 index 0000000..f5fc3cc --- /dev/null +++ b/internal/direquirement.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2011 + */ + +#pragma once + +// This file should NEVER be included independently. It is part of the internals of +// the di.h file and simply separated +#ifndef DI__DEPENDENCY_INJECTION__H +#error "Please don't include \"direquirement.h\" directly." +#endif + +namespace internal +{ + template class Requirement : public internal::RequirementBase + { + friend class di::Instance; + + typename Setter::type setter; + Type parameter; + + inline Requirement(const Type& ty, typename Setter::type func) : setter(func), parameter(ty) {} + protected: + inline virtual void satisfy(InstanceBase* instance, Context* context) throw (DependencyInjectionException); + }; + + template class RequirementAll : public internal::RequirementBase + { + friend class di::Instance; + + typename SetterAll::type setter; + Type parameter; + + inline RequirementAll(const Type& ty, typename SetterAll::type func) : setter(func), parameter(ty) {} + protected: + inline virtual void satisfy(InstanceBase* instance, Context* context) throw (DependencyInjectionException); + }; +} + diff --git a/test/TestConstructorDi.cpp b/test/TestConstructorDi.cpp index 8f7f668..0193ca3 100644 --- a/test/TestConstructorDi.cpp +++ b/test/TestConstructorDi.cpp @@ -14,7 +14,11 @@ namespace constructorDiTests { class MyBean; - class Foo + class IFoo + { + }; + + class Foo : public IFoo { public: inline Foo() {} @@ -28,6 +32,7 @@ namespace constructorDiTests int ival; std::string name; + inline MyBean(IFoo* ptr) : foo((Foo*)ptr), ival(-1) {} inline MyBean(Foo* ptr) : foo(ptr), ival(-1) {} inline MyBean(int i) : foo(NULL), ival(i) {} inline MyBean(const char* n) : foo(NULL), ival(-1), name(n) {} @@ -49,6 +54,20 @@ namespace constructorDiTests context.stop(); } + TEST(ciProvides) + { + Context context; + context.hasInstance(Type()).provides(Type()); + context.hasInstance(Type(),Type()); + context.start(); + MyBean* mybean = context.get(Type()); + CHECK(mybean != NULL); + CHECK(mybean->foo != NULL); + CHECK(mybean->ival == -1); + CHECK(mybean->foo == context.get(Type())); + context.stop(); + } + TEST(ciFailed) { Context context; diff --git a/test/TestDi.cpp b/test/TestDi.cpp index bd60185..7b4cb50 100644 --- a/test/TestDi.cpp +++ b/test/TestDi.cpp @@ -35,11 +35,24 @@ namespace rudamentaryTests public: inline void setMyBean(IMyBean* test_) { test = test_; } + inline void setMyBean(MyBean* test_) { test = test_; } inline void call() { test->func(); } }; + TEST(TestDiSimple) + { + destCalled = false; + Context context; + context.hasInstance(Type()); + context.hasInstance(Type()).requires(Type(),&Bean::setMyBean); + context.start(); + context.stop(); + CHECK(destCalled); + } + TEST(TestDi) { + destCalled = false; Context context; context.hasInstance(Type()).provides(Type()); context.hasInstance(Type()).requires(Type(),&Bean::setMyBean); @@ -50,6 +63,7 @@ namespace rudamentaryTests TEST(TestDiMissingRequirement) { + destCalled = false; bool failure = false; Context context; context.hasInstance(Type()).requires(Type(),&Bean::setMyBean); @@ -191,8 +205,8 @@ namespace simpleexample context.start(); Foo* foo = context.get(Type()); - Bar* bar1 = context.get("bar1",Type()); - Bar* bar2 = context.get("bar2",Type()); + Bar* bar1 = context.get(Type(),"bar1"); + Bar* bar2 = context.get(Type(),"bar2"); CHECK(foo != NULL); CHECK(foo->bar == bar1 || foo->bar == bar2); @@ -532,3 +546,4 @@ namespace otherTests } } +