Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

First commit includes DI framework with supporting classes. Requried …

…boost::shared_ptr installed.
  • Loading branch information...
commit adf6477c6a6e25a3cb67ce52521c5a4265055a02 0 parents
Jim Carroll authored
11 Exception.cpp
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2011
+ */
+
+#include "Exception.h"
+
+namespace YaulCommons
+{
+ ILogger* Exception::logger = NULL;
+}
+
80 Exception.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011
+ */
+
+#pragma once
+
+#include "StdString.h"
+#include "ilog.h"
+
+#define YAUL_COPYVARARGS(fmt) va_list argList; va_start(argList, fmt); set(fmt, argList); va_end(argList)
+#define YAUL_STANDARD_EXCEPTION(E) \
+ class E : public YaulCommons::Exception \
+ { \
+ public: \
+ inline E(const char* message,...) : Exception(#E) { YAUL_COPYVARARGS(message); } \
+ \
+ inline E(const E& other) : Exception(other) {} \
+ }
+
+namespace YaulCommons
+{
+ /**
+ * This class is the superclass for all exceptions
+ * It provides a means for the bindings to retrieve error messages as needed.
+ */
+ class Exception
+ {
+ private:
+ friend class LogSetter<Exception>;
+ static ILogger* logger;
+
+ std::string classname;
+ std::string message;
+
+ inline void logThrowMessage()
+ {
+ if (logger)
+ logger->Log(LOGERROR,"EXCEPTION Thrown (%s) : %s", classname.c_str(), message.c_str());
+ }
+
+ protected:
+ inline Exception(const char* classname_) : classname(classname_) { }
+ inline Exception(const char* classname_, const char* message_) : classname(classname_), message(message_) { }
+
+ inline Exception(const Exception& other) : classname(other.classname), message(other.message)
+ { logThrowMessage(); }
+
+ /**
+ * This method is called from the constructor of subclasses. It
+ * will set the message from varargs as well as call log message
+ */
+ inline void set(const char* fmt, va_list& argList)
+ {
+ // this is a hack and wont work if CStdString ever has
+ // state information
+ // ((CStdString*)(&message))->FormatV(fmt, argList);
+ CStdString tmps;
+ tmps.FormatV(fmt, argList);
+ message = tmps;
+
+ logThrowMessage();
+ }
+
+ /**
+ * This message can be called from the constructor of subclasses.
+ * It will set the message and log the throwing.
+ */
+ inline void setMessage(const char* fmt, ...)
+ {
+ // calls 'set'
+ YAUL_COPYVARARGS(fmt);
+ }
+
+ public:
+ typedef LogSetter<Exception> Settings;
+
+ inline const char* getMessage() const { return message.c_str(); }
+ };
+}
+
14 README
@@ -0,0 +1,14 @@
+Yaul - Readme,n
+ Copyright (C) 2011
+
+Yaul - Yet Another Utility Library
+
+Well, why another utility library? Why not. Actually, this one should fill in
+a few gaps and not really replace what's out there.
+
+Currently it contains a C++ based dependency injection framework. Please see
+di/di.h for complete details.
+
+Current Dependencies:
+ boost::shared_ptr
+ UnitTest++ - to build and run the tests
4,336 StdString.h
4,336 additions, 0 deletions not shown
231 di.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2011
+ */
+
+#include "di.h"
+
+#include "StdString.h"
+
+#include <map>
+
+namespace di
+{
+ namespace internal
+ {
+ const std::string RequirementBase::toString() const
+ {
+ CStdString msg;
+ if (specifiesIdp)
+ 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();
+ for(std::vector<TypeConverterBase*>::const_iterator it = providesTheseTypes.begin(); it != providesTheseTypes.end(); it++)
+ {
+ TypeConverterBase* typeConverter = (*it);
+
+ if (typeConverter->isTypeToConvertTo(typeToConvertTo))
+ {
+ void* ret = ((TypeConverterBase*)typeConverter)->doConvert((void*)obj);
+ if (ret == NULL)
+ throw DependencyInjectionException("Failed to convert a \"%s\" to a \"%s\" using a dynamic_cast for ", typeConverter->toString().c_str(), type.getTypeInfo().name());
+ return ret;
+ }
+ }
+
+ return NULL;
+ }
+
+ bool InstanceBase::canConvertTo(const TypeBase& typeToConvertTo) const
+ {
+ for(std::vector<TypeConverterBase*>::const_iterator it = providesTheseTypes.begin(); it != providesTheseTypes.end(); it++)
+ {
+ TypeConverterBase* typeConverter = (*it);
+
+ if (typeConverter->isTypeToConvertTo(typeToConvertTo))
+ return true;
+ }
+
+ return false;
+ }
+
+ }
+
+ internal::InstanceBase* Context::find(const std::type_info& typeInfo)
+ {
+ for(std::vector<internal::InstanceBase*>::iterator it = instances.begin(); it != instances.end(); it++)
+ {
+ internal::InstanceBase* instance = (*it);
+
+ if (instance->type.getTypeInfo() == typeInfo)
+ return instance;
+ }
+ return NULL;
+ }
+
+ internal::InstanceBase* Context::find(const char* id, const std::type_info& typeInfo)
+ {
+ for(std::vector<internal::InstanceBase*>::iterator it = instances.begin(); it != instances.end(); it++)
+ {
+ internal::InstanceBase* instance = (*it);
+
+ if (instance->type.getTypeInfo() == typeInfo && instance->id == id)
+ return instance;
+ }
+ return NULL;
+ }
+
+ void Context::resetInstances()
+ {
+ internal::InstanceBase* instance;
+ for(std::vector<internal::InstanceBase*>::iterator it = instances.begin(); it != instances.end(); it++)
+ {
+ // since this results in the instance destructor being called ... in case some moron
+ // throws from the destructor, we don't want to stop deleting.
+ try
+ {
+ instance = (*it);
+ instance->reset();
+ }
+ catch (...)
+ {
+ // this prints a message to the log as long as there is a logger set in the exception
+ DependencyInjectionException ex("Exception detected in the destructor of the instance for \"%s.\"",instance->toString().c_str());
+ }
+ }
+
+ curPhase = stopped;
+ }
+
+ void Context::stop() throw (DependencyInjectionException)
+ {
+ if (! isStopped())
+ {
+ internal::InstanceBase* instance;
+ // pre destroy step
+ for(std::vector<internal::InstanceBase*>::iterator it = instances.begin(); it != instances.end(); it++)
+ {
+ instance = (*it);
+ try
+ {
+ instance->doPreDestroy();
+ }
+ catch (...)
+ {
+ // hum .... what to do? c++ sucks here in that I cannot get a handle to the
+ // original exception. ... I can either log and rethrow or I can throw another
+ // known exception. I wish I could wrap and throw.
+ resetInstances();
+ throw DependencyInjectionException("Unknown exception intercepted while executing PreDestroy phase on \"%s.\"", instance->toString().c_str());
+ }
+ }
+
+ // clear out the instances.
+ resetInstances();
+ }
+ }
+
+ void Context::clear()
+ {
+ try { stop(); } catch (DependencyInjectionException& ex) {}
+ for(std::vector<internal::InstanceBase*>::iterator it = instances.begin(); it != instances.end(); it++)
+ delete (*it);
+ instances.clear();
+ curPhase = initial;
+ }
+
+ void Context::start() throw (DependencyInjectionException)
+ {
+ if (isStarted())
+ throw DependencyInjectionException("Called start for a second time on a di::Context.");
+
+ // First instantiate
+ internal::InstanceBase* instance;
+ try
+ {
+ for(std::vector<internal::InstanceBase*>::iterator it = instances.begin(); it != instances.end(); it++)
+ {
+ instance = (*it);
+ instance->instantiateInstance();
+ }
+ }
+ catch (...)
+ {
+ // hum .... what to do? c++ sucks here in that I cannot get a handle to the
+ // original exception. ... I can either log and rethrow or I can throw another
+ // known exception. I wish I could wrap and throw.
+ resetInstances();
+ throw DependencyInjectionException("Unknown exception intercepted while instantiating \"%s.\"", instance->toString().c_str());
+ }
+
+ // we need a map of Types to the Instances that provide those types
+ for(std::vector<internal::InstanceBase*>::iterator it = instances.begin(); it != instances.end(); it++)
+ {
+ instance = (*it);
+
+ // find out what it requires.
+ std::vector<internal::RequirementBase*>& requirements = instance->getRequirements();
+ for (std::vector<internal::RequirementBase*>::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<internal::InstanceBase*> satisfies;
+
+ for(std::vector<internal::InstanceBase*>::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);
+ }
+ }
+ }
+
+ // post construct step
+ for(std::vector<internal::InstanceBase*>::iterator it = instances.begin(); it != instances.end(); it++)
+ {
+ instance = (*it);
+ try
+ {
+ instance->doPostConstruct();
+ }
+ catch (...)
+ {
+ // hum .... what to do? c++ sucks here in that I cannot get a handle to the
+ // original exception. ... I can either log and rethrow or I can throw another
+ // known exception. I wish I could wrap and throw.
+ resetInstances();
+ throw DependencyInjectionException("Unknown exception intercepted while executing postConstruct phase on \"%s.\"", instance->toString().c_str());
+ }
+ }
+
+ curPhase = started;
+ }
+}
465 di.h
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2011
+ */
+
+#pragma once
+
+#define DI__DEPENDENCY_INJECTION__H
+//#define DI__DEPENDENCY_INJECTION_DEBUG
+
+#include "StdString.h"
+#include "Exception.h"
+
+#include <typeinfo>
+#include <vector>
+
+#ifdef DI__DEPENDENCY_INJECTION_DEBUG
+#include <iostream>
+#endif
+
+#include <boost/shared_ptr.hpp>
+
+/**
+ * These classes represent a simple "dependency injection" framework for c++
+ * (see http://en.wikipedia.org/wiki/Dependency_injection). It's loosely based
+ * on some of the concepts implemented in the core "spring framework" for java
+ * (see http://www.springsource.org/).
+ *
+ * This API supports "setter" injection only (primarily because it's easier
+ * to implement in C++ than constructor injection and setter injection, while
+ * some might argue is not as "pure" as constructor injection, is more flexible).
+ *
+ * Assuming you are familiar with dependency injection, the way this api works
+ * is that you create a "Context" and identify Instances of your classes
+ * while declaring the requirements of the class. For example, suppose I have
+ * a class Foo that requires an instance of class Bar. Id like to simply declare
+ * that my Context has a "Foo" that requires a "Bar." In pseudo code I might
+ * declare that as:
+ *
+ * context:
+ * has instance of Foo:
+ * that requires a Bar (and can be set with 'setBar').
+ * has an instance of Bar
+ *
+ * Using this api you would say:
+ *
+ * Context context;
+ * context.hasInstance(Type<Foo>()).requires(Type<Bar>(), &Foo::setBar);
+ * context.hasInstance(Type<Bar>);
+ * context.start();
+ *
+ * 'start' kicks off the instance lifecycles by instantiating the Type<T>'s
+ * that were specified using the class' default constructor. Then it resolves
+ * and satisfies all of the dependencies. Then the "Post Construct" lifecycle
+ * stage is executed (see the section on "Lifecycle stages"). An exception is
+ * thrown if all of the dependencies cannot be resolved or if there is ambiguity
+ * in resolving dependencies.
+ *
+ * Abstraction/Inheritence:
+ * Abstraction is not handled as cleanly as I'd hoped. The reason is because
+ * I could not figure out a means of run-time traversal of a class hierarchy
+ * as RTTI doesn't support it.
+ *
+ * What this means in a nutshell is that you must DECLARE abstractions. Expanding
+ * the above example: if a class Foo requires an interface, say IBar, then the
+ * Instance that implements IBar must declare that it satisfies that dependency.
+ *
+ * The following will NOT work:
+ *
+ * Context context;
+ * context.hasInstance(Type<Foo>()).requires(Type<IBar>(), &Foo::setIBar);
+ * context.hasInstance(Type<Bar>);
+ * context.start();
+ *
+ * Even if Bar extends/implements IBar, the book keeping done by the
+ * context wont understand that the instance of Bar satisfies that dependency.
+ * Instead you need to tell the context this. The line above that declares
+ * the instance of Bar should instead look like:
+ *
+ * context.hasInstance(Type<Bar>().provides(Type<IBar>()));
+ *
+ * Set injection:
+ *
+ * You can specify that all of the instances of a particular type be a requirement
+ * of another instance. For example, if class Foo has a method
+ * 'void setBars(const std::vector<IBar*>)' then you can have the instance
+ * of Foo injected with all of the instances of Bar in the context as follows:
+ *
+ * Context context;
+ * context.hasInstance(Type<Foo>()).requiresAll(Type<IBar>(),&Foo::setBars);
+ * context.hasInstance(Type<Bar>()).provides(Type<IBar>());
+ * context.hasInstance(Type<Bar>()).provides(Type<IBar>());
+ * context.hasInstance(Type<Bar>()).provides(Type<IBar>());
+ * context.start();
+ *
+ * Note that this example combines the abstraction with the set injection but it
+ * would (of course) also work with simple concrete types. Given
+ * 'void Foo::setBars(const std::vector<Bar*>)' the following is fine:
+ *
+ * Context context;
+ * context.hasInstance(Type<Foo>()).requiresAll(Type<Bar>(),&Foo::setBars);
+ * context.hasInstance(Type<Bar>());
+ * context.hasInstance(Type<Bar>());
+ * context.hasInstance(Type<Bar>());
+ * context.start();
+ *
+ * Chaining:
+ *
+ * Context::hasInstance as well as the Instance methods 'provides' and 'requires'
+ * returns the Instance& so that you can chain calls.
+ *
+ * If an instance requires more declaration/clarifications they can be chained:
+ *
+ * context.hasInstance(Type<Foo>()).
+ * requires(Type<Bar>(), &Foo::setBar).
+ * requires(Type<Other>(), &Foo::setOther).
+ * provides(Type<IFoo>());
+ *
+ * Note that, by default, all instances 'provide' their own type. The following is an
+ * error:
+ *
+ * context.hasInstance(Type<Foo>()).provides(Type<Foo>());
+ *
+ * Using instance ids:
+ *
+ * It's possible to name instances so that requirements and dependencies can be explicitly
+ * identified. For example:
+ *
+ * context.hasInstance(Type<Foo>()).requires("bar1",Type<IBar>(), &Foo::setIBar);
+ * context.hasInstance(Type<Foo>()).requires("bar2",Type<IBar>(), &Foo::setIBar);
+ * context.hasInstance("bar1",Type<Bar>());
+ * context.hasInstance("bar2",Type<Bar>());
+ *
+ * Lifecycle stages:
+ *
+ * Instances in the context go through several "lifecycles stages" (or "phases"). These
+ * are, in order:
+ *
+ * 1) Instantiation - instantiation of the defined instances.
+ * 2) Wiring - satisfying of the requirements via calling the identified setters
+ * 3) PostConstruction - which calls all of the postConstruct methods that
+ * were declared to the context.
+ * ...
+ * 4) PreDestruction - which calls all of the preDestroy methods that
+ * were declared to the context.
+ * 5) Deletion - Deletes all of the instances that were instantiated
+ * during the Instantiation lifecycle stage.
+ *
+ * Context::start executes the first three and Context::stop executes the last two.
+ *
+ * "PostConstruct" and "PreDestroy" are lifecycle stages where the instances are notified
+ * via callbacks that were previously identified to the context using the
+ * 'Instance<T>::postConstruct' and 'Instance<T>::preDestroy' register methods respectively.
+ * For example:
+ *
+ * context.hasInstance(Type<Foo>()).
+ * postConstruct(&Foo::postConstructMethod).
+ * preDestroy(&Foo::preDestroyMethod)...
+ *
+ * After the context has wired up all of the dependnecies, Foo::postConstructMethod
+ * will be called (along with any other registerd postConstruct method on any other
+ * instance).
+ *
+ * NOTE: Currently the Context expects only one postConstruct (and/or) preDestroy callback
+ * to be registered per instance.
+ *
+ */
+
+namespace di
+{
+ /**
+ * Generic exception thrown by various DI container operations.
+ */
+ YAUL_STANDARD_EXCEPTION(DependencyInjectionException);
+
+ inline std::ostream& operator<<(std::ostream& stream, const DependencyInjectionException& ex)
+ {
+ stream << "DependencyInjectionException:" << ex.getMessage();
+ return stream;
+ }
+
+ // Nothing to see here, move along ...
+ #include "diinternal.h"
+
+ /**
+ * This class represents the means of declaring type information
+ * to the context.
+ */
+ template <class T> class Type : public internal::TypeBase
+ {
+ public:
+ inline Type() : internal::TypeBase(typeid(T)) {}
+
+ virtual ~Type() {}
+
+ /**
+ * public methods defined in TypeBase include:
+ *
+ * const std::string Type<T>::toString() const;
+ *
+ * toString method returns the string representation of the Type<T> instance. This
+ * is simply the typeinfo name() result wrapped in a std::string.
+ *
+ * const std::type_info& Type<T>::getTypeInfo() const
+ *
+ * getTypeInfo returns the rtti typeinfo instance for the type T.
+ *
+ * operator== and operator!= are defined for Type<T> and delegate
+ * to the == and != on typeinfo.
+ */
+ };
+
+ /**
+ * This template allows the declaration of object instances in a context.
+ */
+ template<class T> class Instance : public internal::InstanceBase
+ {
+ friend class Context;
+
+ public:
+ typedef void (T::*PostConstructMethod)();
+ typedef void (T::*PreDestroyMethod)();
+
+ private:
+ boost::shared_ptr<T> ref;
+
+ PostConstructMethod postConstructMethod;
+ PreDestroyMethod preDestroyMethod;
+
+ virtual void doPostConstruct()
+ {
+ if (postConstructMethod != NULL)
+ ((*get()).*(postConstructMethod))();
+ }
+
+ virtual void doPreDestroy()
+ {
+ if (preDestroyMethod != NULL)
+ ((*get()).*(preDestroyMethod))();
+ }
+
+ inline Instance() : InstanceBase(Type<T>()), postConstructMethod(NULL),
+ preDestroyMethod(NULL) { provides(Type<T>()); }
+
+ inline explicit Instance(const char* name) :
+ InstanceBase(name,Type<T>()), postConstructMethod(NULL),
+ preDestroyMethod(NULL) { provides(Type<T>()); }
+
+ inline Instance(const Instance<T>& other) :
+ InstanceBase(other), ref(other.ref), postConstructMethod(NULL),
+ preDestroyMethod(NULL) { provides(Type<T>()); }
+
+ virtual ~Instance() {}
+
+ protected:
+ virtual inline const void* getConcrete() const { return ref.get(); }
+
+ virtual inline void instantiateInstance() { ref = boost::shared_ptr<T>(new T); }
+
+ virtual inline void reset() { ref.reset(); }
+ public:
+
+ /**
+ * It is possible to explicitly declare that an Instance satisfies a particular
+ * requirement. This is often necessary because relationships within class
+ * hierarchies are not understood by the DI API (if someone can figure out
+ * a way to do this then be my guest).
+ */
+ template<typename D> inline Instance<T>& provides(const Type<D>& typeInfo) throw (DependencyInjectionException)
+ {
+ providesTheseTypes.push_back(new internal::TypeConverter<D,T>);
+ return *this;
+ }
+
+ /**
+ * Use this method to declare that this instance requires a particular
+ * dependency.
+ */
+ template<typename D> inline Instance<T>& requires(const Type<D>& dependency, typename internal::Requirement<T,D>::Setter setter)
+ {
+ requirements.push_back(new internal::Requirement<T,D>(this,setter));
+ return *this;
+ }
+
+ /**
+ * Use this method to declare that this instance requires a particular
+ * dependency.
+ */
+ template<typename D> inline Instance<T>& requires(const char* id, const Type<D>& dependency, typename internal::Requirement<T,D>::Setter setter)
+ {
+ requirements.push_back(new internal::Requirement<T,D>(id,this,setter));
+ return *this;
+ }
+
+ /**
+ * Use this method to declare that this instance requires a particular
+ * dependency.
+ */
+ template<typename D> inline Instance<T>& requiresAll(const Type<D>& dependency, typename internal::Requirement<T,D>::SetterAll setter)
+ {
+ requirements.push_back(new internal::Requirement<T,D>(this,setter));
+ return *this;
+ }
+
+ /**
+ * Calling this method instructs the context to call the postConstructMethod
+ * on the Instance after everything is initialized and wired.
+ */
+ inline Instance<T>& postConstruct(PostConstructMethod postConstructMethod_) throw (DependencyInjectionException)
+ {
+ if (postConstructMethod != NULL)
+ throw DependencyInjectionException("Multiple postConstruct registrations detected for '%s'. \"There can be only one (per instance).\"",this->toString().c_str());
+
+ postConstructMethod = postConstructMethod_;
+ return *this;
+ }
+
+ /**
+ * Calling this method instructs the context to call the preDestroyMethod
+ * on the Instance before everything is deleted.
+ */
+ inline Instance<T>& preDestroy(PreDestroyMethod preDestroyMethod_) throw (DependencyInjectionException)
+ {
+ if (preDestroyMethod != NULL)
+ throw DependencyInjectionException("Multiple preDestroy registrations detected for '%s'. \"There can be only one (pre instance).\"",this->toString().c_str());
+
+ preDestroyMethod = preDestroyMethod_;
+ return *this;
+ }
+
+ /**
+ * Returns the underlying instance.
+ */
+ inline T* get() { return (T*)getConcrete(); }
+
+ };
+
+ /**
+ * A context defines the specific instance of a dependency injection container.
+ * There is typically one per application but this is not a requirement if there
+ * is a reason to have an application with multiple sub-eco-systems of
+ * interrelated implementations.
+ */
+ class Context
+ {
+ std::vector<internal::InstanceBase*> instances;
+
+ internal::InstanceBase* find(const std::type_info& typeInfo);
+ internal::InstanceBase* find(const char* id, const std::type_info& typeInfo);
+
+ void resetInstances();
+
+ enum Phase { initial = 0, started, stopped };
+ Phase curPhase;
+
+ public:
+
+ virtual ~Context() { clear(); }
+
+ inline Context() : curPhase(initial) {}
+
+ /**
+ * Use this method to declare that the context has an instance of a
+ * particular type. The instance will be created using the default
+ * constructor prior to the method returning. (It is possible this
+ * may change in a future implementation so please don't write code
+ * that either expects this to be the case, or expects this not to
+ * be the case).
+ */
+ template<typename T> inline Instance<T>& hasInstance(const Type<T>& bean)
+ {
+ Instance<T>* newInstance = new Instance<T>();
+ instances.push_back(newInstance);
+ return *newInstance;
+ }
+
+ /**
+ * Use this method to declare that the context has an instance of a
+ * particular type with a given id. The instance will be created using the default
+ * constructor prior to the method returning. (It is possible this
+ * may change in a future implementation so please don't write code
+ * that either expects this to be the case, or expects this not to
+ * be the case).
+ */
+ template<typename T> inline Instance<T>& hasInstance(const char* id, const Type<T>& bean)
+ {
+ Instance<T>* newInstance = new Instance<T>(id);
+ instances.push_back(newInstance);
+ return *newInstance;
+ }
+
+ /**
+ * This triggers the wiring and startup object lifecycle stages. Theses include,
+ * in order:
+ *
+ * 1) Instantiation - instantiation of the defined instances.
+ * 2) Wiring - satisfying of the requirements via calling the identified setters
+ * 3) Post Construction - which calls all of the postConstruct methods that
+ * were declared to the context.
+ *
+ * A failure to start (indicated by an exception) will automatically reset the
+ * instances. Therefore it is possible that the instances constructors and
+ * destructors may have executed
+ */
+ void start() throw (DependencyInjectionException);
+
+ /**
+ * progress through the stop/shutdown lifecycle stages. These include,
+ * in order:
+ *
+ * 1) Pre Destroy - which calls all of the preDestroy methods that
+ * were declared to the context.
+ * 2) Deletion - Deletes all of the instances that were instantiated
+ * during the Instantiation lifecycle stage.
+ */
+ void stop() throw (DependencyInjectionException);
+
+ /**
+ * clear() will reset the Context to it's initial state prior to any instances
+ * even being added. It clears all Instances from the context, first invoking
+ * stop(). Since Exceptions thrown from stop() are swallowed, it is recommended
+ * that you don't use this method. Call stop explicitly and allow the Context
+ * destructor to clean up the container.
+ */
+ 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<typename T> T* get(Type<T> typeToFind)
+ {
+ internal::InstanceBase* ret = find(typeToFind.getTypeInfo());
+
+ return ret != NULL ? ((Instance<T>*)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<typename T> T* get(const char* id, Type<T> typeToFind)
+ {
+ internal::InstanceBase* ret = find(id,typeToFind.getTypeInfo());
+
+ return ret != NULL ? ((Instance<T>*)ret)->get() : NULL;
+ }
+
+ /**
+ * Is the Context stopped. This will be true prior to start or after stop
+ * is called.
+ */
+ inline bool isStopped() { return curPhase == initial || curPhase == stopped; }
+
+ /**
+ * isStarted() will be true once the 'start()' call succeeds.
+ */
+ inline bool isStarted() { return curPhase == started; }
+
+ };
+
+}
+
+#undef DI__DEPENDENCY_INJECTION__H
236 diinternal.h
@@ -0,0 +1,236 @@
+/*
+ * 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 \"diinternal.h\" directly."
+#endif
+
+/**
+ * Things in the di::internal namespace are not meant as part of the public
+ * API. If you're interested in learning the API, see the documenation on the
+ * non-internal API classes in "di.h".
+ */
+class Context;
+template<class T> class Instance;
+
+namespace internal
+{
+ class RequirementBase;
+ class InstanceBase;
+
+ /**
+ * holds simple rtti type information. Defines equivalence and toString
+ */
+ class TypeBase
+ {
+ friend class InstanceBase;
+ friend class RequirementBase;
+
+ protected:
+ const std::type_info* type;
+
+ inline TypeBase(const std::type_info& type_) : 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 bool operator==(const TypeBase& other) const { return (*type) == (*(other.type)); }
+
+ inline bool operator!=(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); }
+ };
+
+ /**
+ * base class for template that defines a type conversion between two
+ * types in a class hierarchy.
+ */
+ class TypeConverterBase : public TypeBase
+ {
+ 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; }
+ };
+
+ /**
+ * Template to allow the instantiation of a particular type conversion
+ */
+ template<class T, class F> class TypeConverter : public TypeConverterBase
+ {
+ protected:
+
+ virtual inline void* doConvert(void * from) { return dynamic_cast<T*>((F*)from); }
+ inline T* convert(F* from) { return (T*)doConvert(from); }
+
+ public:
+ // this needs to be public for the "<class D> Instance<T>::provides" method
+ inline TypeConverter() : TypeConverterBase(typeid(T)) {}
+ };
+
+ /**
+ * Base class for an instance declaration in the DI context
+ */
+ class InstanceBase
+ {
+ friend class di::Context;
+ friend class RequirementBase;
+
+ protected:
+
+ TypeBase type;
+ std::string id;
+ bool hasId;
+
+ std::vector<TypeConverterBase*> providesTheseTypes;
+ std::vector<RequirementBase*> requirements;
+
+ virtual void doPostConstruct() = 0;
+ virtual void doPreDestroy() = 0;
+
+ inline explicit InstanceBase(const TypeBase& tb) : type(tb), hasId(false) {}
+
+ inline InstanceBase(const char* name, const TypeBase& tb) : type(tb), id(name), hasId(true) {}
+
+ inline InstanceBase(const InstanceBase& other) : type(other.type), id(other.id), hasId(other.hasId),
+ providesTheseTypes(other.providesTheseTypes),
+ requirements(other.requirements){ }
+
+ inline virtual ~InstanceBase() { }
+
+ inline std::vector<RequirementBase*>& getRequirements() { return requirements; }
+
+ inline const std::string toString() const { return hasId ? (id + ":" + type.toString()) : type.toString(); }
+
+ void* convertTo(const TypeBase& typeToConvertTo) const throw (DependencyInjectionException);
+
+ bool canConvertTo(const TypeBase& other) const;
+
+ virtual const void* getConcrete() const = 0;
+
+ virtual void instantiateInstance() = 0;
+
+ virtual void reset() = 0;
+ };
+
+ /**
+ * base class for the template that defines a requirement.
+ */
+ class RequirementBase
+ {
+ friend class di::Context;
+
+ protected:
+ TypeBase instance;
+ TypeBase parameter;
+
+ std::string requiredId;
+ bool specifiesIdp;
+
+ inline RequirementBase(const std::type_info& obj, const std::type_info& param) :
+ instance(obj), parameter(param), specifiesIdp(false) {}
+ inline RequirementBase(const char* id, const std::type_info& obj, const std::type_info& param) :
+ instance(obj), parameter(param), requiredId(id), specifiesIdp(id != NULL) {}
+
+ virtual ~RequirementBase() {}
+
+ inline bool specifiesId() const { return specifiesIdp; }
+
+ inline bool isSatisfiedBy(const InstanceBase* dep)
+ {
+ return specifiesIdp ? (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<InstanceBase*>& dep) throw (DependencyInjectionException) = 0;
+
+ // hacks for child to call other stuff
+ inline void* instanceBaseConvertTo(InstanceBase* dep, const TypeBase& typeToConvertTo) const
+ throw (DependencyInjectionException) { return dep->convertTo(typeToConvertTo); }
+
+ inline const std::string toString(const InstanceBase& dep) const { return dep.toString(); }
+
+ inline const void* getConcrete(InstanceBase* instance) const { return instance->getConcrete(); }
+ };
+
+ /**
+ * This template is used to declare a requirement of an object that
+ * needs to be fulfilled in the container.
+ */
+ template<class T, class D> class Requirement : public internal::RequirementBase
+ {
+ friend class di::Instance<T>;
+ public:
+ typedef void (T::*Setter)(D*);
+ typedef void (T::*SetterAll)(const std::vector<D*>);
+
+ private:
+ Setter setter;
+ SetterAll setterAll;
+ InstanceBase* requirementOf;
+
+ inline Requirement(InstanceBase* thingWithSetter, Setter func) :
+ RequirementBase(typeid(T), typeid(D)), setter(func),
+ setterAll(NULL), requirementOf(thingWithSetter) {}
+ inline Requirement(InstanceBase* thingWithSetter, SetterAll func) :
+ RequirementBase(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*)getConcrete(requirementOf); }
+
+ inline void injectRequirement(D* objectToInject) { ((*getRequirementOf()).*(setter))(objectToInject); }
+ inline void injectRequirementSet(const std::vector<D*>& objectToInject) { ((*getRequirementOf()).*(setterAll))(objectToInject); }
+
+ inline virtual void satisfyWith(InstanceBase* dep) throw (DependencyInjectionException)
+ {
+#ifdef DI__DEPENDENCY_INJECTION_DEBUG
+ std::cout << "requirement:" << toString() << " is satisfied by " << toString(*dep) << std::endl;
+#endif
+ D* actualDep = (D*)instanceBaseConvertTo(dep,parameter);
+ if (actualDep == NULL)
+ throw DependencyInjectionException("Can't satisfy a requirement for '%s' with '%s.'",toString().c_str(),toString(*dep).c_str());
+ injectRequirement(actualDep);
+ }
+
+ inline virtual bool satisfiedWithSet() { return setterAll != NULL; }
+
+ inline virtual void satisfyWithSet(const std::vector<InstanceBase*>& dep) throw (DependencyInjectionException)
+ {
+#ifdef DI__DEPENDENCY_INJECTION_DEBUG
+ std::cout << "requirement:" << toString() << " is satisfied by: " << std::endl;
+ for (std::vector<InstanceBase*>::const_iterator depIt = dep.begin(); depIt != dep.end(); depIt++)
+ std::cout << " " << toString(*(*depIt)) << std::endl;
+#endif
+ std::vector<D*> param;
+ for (std::vector<InstanceBase*>::const_iterator depIt = dep.begin(); depIt != dep.end(); depIt++)
+ param.push_back((D*)instanceBaseConvertTo((*depIt),parameter));
+ injectRequirementSet(param);
+ }
+ };
+
+}
+
+
22 ilog.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2011
+ */
+
+#include "ilog.h"
+#include "StdString.h"
+
+namespace YaulCommons
+{
+ void ILogger::Log(int loglevel, const char *format, ... )
+ {
+ CStdString strData;
+
+ strData.reserve(16384);
+ va_list va;
+ va_start(va, format);
+ strData.FormatV(format,va);
+ va_end(va);
+
+ log(loglevel, strData);
+ }
+}
64 ilog.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+#define LOG_LEVEL_NONE -1 // nothing at all is logged
+#define LOG_LEVEL_NORMAL 0 // shows notice, error, severe and fatal
+#define LOG_LEVEL_DEBUG 1 // shows all
+#define LOG_LEVEL_DEBUG_FREEMEM 2 // shows all + shows freemem on screen
+#define LOG_LEVEL_DEBUG_SAMBA 3 // shows all + freemem on screen + samba debugging
+#define LOG_LEVEL_MAX LOG_LEVEL_DEBUG_SAMBA
+
+// ones we use in the code
+#define LOGDEBUG 0
+#define LOGINFO 1
+#define LOGNOTICE 2
+#define LOGWARNING 3
+#define LOGERROR 4
+#define LOGSEVERE 5
+#define LOGFATAL 6
+#define LOGNONE 7
+
+#ifdef __GNUC__
+#define ATTRIB_LOG_FORMAT __attribute__((format(printf,3,4)))
+#else
+#define ATTRIB_LOG_FORMAT
+#endif
+
+namespace YaulCommons
+{
+ class ILogger
+ {
+ public:
+ void Log(int loglevel, const char *format, ... ) ATTRIB_LOG_FORMAT;
+
+ virtual void log(int loglevel, const char* message) = 0;
+ };
+
+ /**
+ * Because the logger is usually set as a static member of the class it's used
+ * in, this template will help define a Setter class that will set the value of
+ * the logger for any particular class static member called 'logger.'
+ *
+ * An instance of this class can be used in the DI container functionality.
+ *
+ * To see an example of where this is used, see the generic Exception class
+ * that's part of this library.
+ */
+ template <class C> class LogSetter
+ {
+ public:
+ inline ~LogSetter() { C::logger = NULL; }
+
+ inline void setLogger(YaulCommons::ILogger* logger)
+ {
+ C::logger = logger;
+ }
+ };
+}
+
+#undef ATTRIB_LOG_FORMAT
534 test/TestDi.cpp
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2011
+ */
+
+#include "../di.h"
+
+#include <unittest++/UnitTest++.h>
+#include <iostream>
+
+using namespace di;
+
+namespace rudamentaryTests
+{
+ class IMyBean
+ {
+ public:
+ virtual ~IMyBean() {}
+
+ virtual void func() = 0;
+ };
+
+ static bool funcCalled = false;
+ static bool destCalled = false;
+
+ class MyBean : public IMyBean
+ {
+ public:
+ virtual void func() { funcCalled = true; }
+ virtual ~MyBean() { destCalled = true; }
+ };
+
+ class Bean
+ {
+ IMyBean* test;
+ public:
+
+ inline void setMyBean(IMyBean* test_) { test = test_; }
+ inline void call() { test->func(); }
+ };
+
+ TEST(TestDi)
+ {
+ Context context;
+ context.hasInstance(Type<MyBean>()).provides(Type<IMyBean>());
+ context.hasInstance(Type<Bean>()).requires(Type<IMyBean>(),&Bean::setMyBean);
+ context.start();
+ context.stop();
+ CHECK(destCalled);
+ }
+
+ TEST(TestDiMissingRequirement)
+ {
+ bool failure = false;
+ Context context;
+ context.hasInstance(Type<Bean>()).requires(Type<IMyBean>(),&Bean::setMyBean);
+ try
+ {
+ context.start();
+ }
+ catch (di::DependencyInjectionException& ex)
+ {
+ failure = true;
+ }
+
+ CHECK(failure);
+ context.stop();
+ }
+
+ TEST(TestDiAmbiguousRequirement)
+ {
+ destCalled = false;
+ bool failure = false;
+ Context context;
+ context.hasInstance(Type<MyBean>()).provides(Type<IMyBean>());
+ context.hasInstance(Type<MyBean>()).provides(Type<IMyBean>());
+ context.hasInstance(Type<Bean>()).requires(Type<IMyBean>(),&Bean::setMyBean);
+ try
+ {
+ context.start();
+ }
+ catch (di::DependencyInjectionException& ex)
+ {
+ failure = true;
+ }
+
+ CHECK(failure);
+
+ context.stop();
+ CHECK(destCalled);
+ }
+}
+
+namespace simpleexample
+{
+ class Bar
+ {
+ };
+
+ class Foo
+ {
+ public:
+ bool calledPostConstruct;
+ Bar* bar;
+
+ inline Foo() : calledPostConstruct(false) {}
+ void setBar(Bar* bar_) { bar = bar_; }
+
+ void postConstruct() { calledPostConstruct = true; }
+ };
+
+ TEST(TestSimpleExample)
+ {
+ Context context;
+ context.hasInstance(Type<Foo>()).
+ requires(Type<Bar>(), &Foo::setBar).
+ postConstruct(&Foo::postConstruct);
+
+ // notice without a Bar in the context we will have a failure.
+ bool failure = false;
+ try
+ {
+ context.start();
+ }
+ catch (di::DependencyInjectionException& ex)
+ {
+ failure = true;
+ }
+ CHECK(failure);
+ CHECK(context.isStopped());
+
+ // but if we add the bar ...
+ context.hasInstance(Type<Bar>());
+
+ context.start();
+
+ CHECK(context.isStarted());
+ CHECK(!context.isStopped());
+
+ Foo* foo = context.get(Type<Foo>());
+ CHECK(foo != NULL);
+ CHECK(foo->calledPostConstruct);
+ }
+
+ TEST(TestSimpleExampleWithIds)
+ {
+ Context context;
+ context.hasInstance("foo",Type<Foo>()).requires("bar",Type<Bar>(), &Foo::setBar);
+
+ // notice without a Bar in the context we will have a failure.
+ bool failure = false;
+ try
+ {
+ context.start();
+ }
+ catch (di::DependencyInjectionException& ex)
+ {
+ failure = true;
+ }
+ CHECK(failure);
+
+ // but if we add the bar without an id ...
+ context.hasInstance(Type<Bar>());
+
+ // ... it should still fail
+ failure = false;
+ try
+ {
+ context.start();
+ }
+ catch (di::DependencyInjectionException& ex)
+ {
+ failure = true;
+ }
+ CHECK(failure);
+
+ // but if we add a Bar with a name then it should work
+ context.hasInstance("bar",Type<Bar>());
+ context.start();
+ CHECK(context.isStarted());
+ context.stop();
+ CHECK(context.isStopped());
+ }
+
+ TEST(TestSimpleExampleWithDups)
+ {
+ Context context;
+ context.hasInstance(Type<Foo>()).requires("bar1",Type<Bar>(), &Foo::setBar);
+ context.hasInstance(Type<Foo>()).requires("bar2",Type<Bar>(), &Foo::setBar);
+ context.hasInstance("bar1",Type<Bar>());
+ context.hasInstance("bar2",Type<Bar>());
+ context.start();
+
+ Foo* foo = context.get(Type<Foo>());
+ Bar* bar1 = context.get("bar1",Type<Bar>());
+ Bar* bar2 = context.get("bar2",Type<Bar>());
+
+ CHECK(foo != NULL);
+ CHECK(foo->bar == bar1 || foo->bar == bar2);
+ }
+}
+
+namespace abstractExample
+{
+ class IBar
+ {
+ public:
+ virtual void func() = 0;
+ };
+
+ class Bar : public IBar
+ {
+ public:
+ bool called;
+
+ Bar() : called(false) {}
+
+ virtual ~Bar() {}
+ virtual void func() { called = true; }
+ };
+
+ class Foo
+ {
+ public:
+ IBar* bar;
+ void setIBar(IBar* bar_) { bar = bar_; }
+
+ void call() { bar->func(); }
+ };
+
+ TEST(TestSimpleExample)
+ {
+ Context context;
+ context.hasInstance(Type<Foo>()).requires(Type<IBar>(), &Foo::setIBar);
+ context.hasInstance(Type<Bar>()).provides(Type<IBar>());
+
+ context.start();
+ CHECK(context.isStarted());
+
+ Foo* foo = context.get(Type<Foo>());
+ CHECK(foo);
+
+ Bar* bar = context.get(Type<Bar>());
+ CHECK(bar);
+
+ foo->call();
+ CHECK(bar->called);
+ }
+
+ TEST(TestSimpleExampleWithId)
+ {
+ Context context;
+ context.hasInstance(Type<Foo>()).requires("bar",Type<IBar>(), &Foo::setIBar);
+ context.hasInstance("bar",Type<Bar>()).provides(Type<IBar>());
+
+ context.start();
+ CHECK(context.isStarted());
+
+ Foo* foo = context.get(Type<Foo>());
+ CHECK(foo);
+
+ Bar* bar = context.get(Type<Bar>());
+ CHECK(bar);
+
+ foo->call();
+ CHECK(bar->called);
+ }
+}
+
+namespace postConstructTest
+{
+
+ class PBar
+ {
+ long long something;
+ };
+
+ class SBar
+ {
+ long long something;
+ };
+
+ bool calledIBarPreDestroy;
+ class IBar
+ {
+ public:
+ bool calledPostConstruct;
+
+ IBar() : calledPostConstruct(false) {}
+
+ virtual void func() = 0;
+ void postConstruct() { calledPostConstruct = true; }
+ void preDestroy() { calledIBarPreDestroy = true; }
+ };
+
+ class Bar : public PBar, public IBar, public SBar
+ {
+ public:
+ bool called;
+
+ Bar() : called(false) {}
+
+ virtual ~Bar() {}
+ virtual void func() { called = true; }
+ };
+
+ bool calledFooPreDestroy;
+ class Foo
+ {
+ public:
+ bool calledPostConstruct;
+
+ IBar* bar;
+
+ inline Foo() : calledPostConstruct(false) {}
+ void setBar(IBar* bar_) { bar = bar_; }
+
+ void postConstruct() { calledPostConstruct = true; }
+ void preDestroy() { calledFooPreDestroy = true; }
+ };
+
+ TEST(TestSimplePostConstruct)
+ {
+ Context context;
+ context.hasInstance(Type<Foo>()).postConstruct(&Foo::postConstruct);
+ context.start();
+
+ Foo* foo = context.get(Type<Foo>());
+
+ CHECK(foo != NULL);
+ CHECK(foo->calledPostConstruct);
+ }
+
+ TEST(TestParentPostConstruct)
+ {
+ Context context;
+ context.hasInstance(Type<Bar>()).postConstruct(&IBar::postConstruct);
+ context.start();
+
+ Bar* bar = context.get(Type<Bar>());
+ CHECK(bar != NULL);
+ CHECK(bar->calledPostConstruct);
+ }
+
+ TEST(TestSimplePreDestroy)
+ {
+ calledFooPreDestroy = false;
+ calledIBarPreDestroy = false;
+
+ Context context;
+ context.hasInstance(Type<Foo>()).preDestroy(&Foo::preDestroy);
+ context.start();
+
+ context.stop();
+
+ CHECK(calledFooPreDestroy);
+ }
+
+ TEST(TestParentPreDestroy)
+ {
+ calledFooPreDestroy = false;
+ calledIBarPreDestroy = false;
+
+ Context context;
+ context.hasInstance(Type<Bar>()).preDestroy(&IBar::preDestroy);
+ context.start();
+ context.stop();
+
+ CHECK(calledIBarPreDestroy);
+ }
+
+ TEST(TestSimplePostConstructAndPreDestroy)
+ {
+ calledFooPreDestroy = false;
+ calledIBarPreDestroy = false;
+
+ Context context;
+ context.hasInstance(Type<Foo>()).postConstruct(&Foo::postConstruct).preDestroy(&Foo::preDestroy);
+ context.start();
+
+ Foo* foo = context.get(Type<Foo>());
+
+ CHECK(foo != NULL);
+ CHECK(foo->calledPostConstruct);
+ CHECK(!calledFooPreDestroy);
+
+ context.stop();
+
+ CHECK(calledFooPreDestroy);
+ }
+
+ TEST(TestParentPostConstructAndPreDestroy)
+ {
+ calledFooPreDestroy = false;
+ calledIBarPreDestroy = false;
+
+ Context context;
+ context.hasInstance(Type<Bar>()).postConstruct(&IBar::postConstruct).preDestroy(&IBar::preDestroy);
+ context.start();
+
+ Bar* bar = context.get(Type<Bar>());
+ CHECK(bar != NULL);
+ CHECK(bar->calledPostConstruct);
+
+ CHECK(!calledIBarPreDestroy);
+
+ context.stop();
+
+ CHECK(calledIBarPreDestroy);
+ }
+}
+
+namespace vectorTest
+{
+ class IBar
+ {
+ public:
+ virtual void func() = 0;
+ };
+
+ class Bar : public IBar
+ {
+ public:
+ bool called;
+
+ Bar() : called(false) {}
+
+ virtual ~Bar() {}
+ virtual void func() { called = true; }
+ };
+
+ class Foo
+ {
+ public:
+ std::vector<IBar*> bars;
+
+ inline Foo() {}
+ void setBars(const std::vector<IBar*> bar_)
+ {
+ bars = bar_;
+ }
+ };
+
+ TEST(TestSimple)
+ {
+ Context context;
+ context.hasInstance(Type<Foo>()).requiresAll(Type<IBar>(),&Foo::setBars);
+ context.hasInstance(Type<Bar>()).provides(Type<IBar>());
+ context.hasInstance(Type<Bar>()).provides(Type<IBar>());
+ context.hasInstance(Type<Bar>()).provides(Type<IBar>());
+ context.start();
+
+ Foo* foo = context.get(Type<Foo>());
+
+ CHECK(foo != NULL);
+ CHECK(foo->bars.size() == 3);
+ }
+
+}
+
+namespace otherTests
+{
+ class IBar
+ {
+ public:
+ virtual void func() = 0;
+ };
+
+ static bool barDestructorCalled = false;
+
+ class Bar : public IBar
+ {
+ public:
+ bool called;
+
+ Bar() : called(false) {}
+
+ virtual ~Bar() { barDestructorCalled = true; }
+ virtual void func() { called = true; }
+ };
+
+ static bool fooDestructorCalled = false;
+ static bool fooConstructorCalled = false;
+
+ class Foo
+ {
+ public:
+ inline ~Foo() { fooDestructorCalled = true; }
+ inline Foo() { fooConstructorCalled = true; }
+ IBar* bar;
+ void setIBar(IBar* bar_) { bar = bar_; }
+ };
+
+ TEST(TestStartStopStart)
+ {
+ {
+ Context context;
+ context.hasInstance(Type<Foo>()).requires(Type<IBar>(),&Foo::setIBar);
+ context.hasInstance(Type<Bar>()).provides(Type<IBar>());
+
+ CHECK(!fooDestructorCalled);
+ CHECK(!fooConstructorCalled);
+ CHECK(!context.isStarted());
+
+ context.start();
+
+ CHECK(!fooDestructorCalled);
+ CHECK(fooConstructorCalled);
+ CHECK(context.isStarted());
+
+ fooConstructorCalled = false;
+
+ context.stop();
+ CHECK(fooDestructorCalled);
+ CHECK(!fooConstructorCalled);
+ CHECK(context.isStopped());
+
+ fooDestructorCalled = false;
+
+ context.start();
+
+ CHECK(!fooDestructorCalled);
+ CHECK(fooConstructorCalled);
+ CHECK(context.isStarted());
+
+ fooConstructorCalled = false;
+ barDestructorCalled = false;
+ } // context destructor
+
+ CHECK(fooDestructorCalled);
+ CHECK(barDestructorCalled);
+ CHECK(!fooConstructorCalled);
+ }
+}
+
26 test/TestMain.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2011
+ */
+
+#include "../Exception.h"
+
+#include <unittest++/UnitTest++.h>
+
+#include <iostream>
+
+class DumbLogger : public YaulCommons::ILogger
+{
+public:
+ virtual void log(int loglevel, const char* message)
+ {
+ std::cout << loglevel << ":" << message << std::endl;
+ }
+};
+
+int main()
+{
+ YaulCommons::Exception::Settings s1;
+ s1.setLogger(new DumbLogger);
+ return UnitTest::RunAllTests();
+}
+
Please sign in to comment.
Something went wrong with that request. Please try again.