summaryrefslogtreecommitdiff
path: root/shared/loki/CachedFactory.h
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:01:29 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:01:29 +0200
commit9a2a524f1e311853d08050be2dcdddc09ac7759a (patch)
treed8e4a24169fce88c2d89931d58514889a0bcb0ea /shared/loki/CachedFactory.h
parent2.3 (diff)
downloadFreeFileSync-9a2a524f1e311853d08050be2dcdddc09ac7759a.tar.gz
FreeFileSync-9a2a524f1e311853d08050be2dcdddc09ac7759a.tar.bz2
FreeFileSync-9a2a524f1e311853d08050be2dcdddc09ac7759a.zip
3.0
Diffstat (limited to 'shared/loki/CachedFactory.h')
-rw-r--r--shared/loki/CachedFactory.h1167
1 files changed, 1167 insertions, 0 deletions
diff --git a/shared/loki/CachedFactory.h b/shared/loki/CachedFactory.h
new file mode 100644
index 00000000..15c9a801
--- /dev/null
+++ b/shared/loki/CachedFactory.h
@@ -0,0 +1,1167 @@
+////////////////////////////////////////////////////////////////////////////////
+// The Loki Library
+// Copyright (c) 2006 by Guillaume Chatelet
+//
+// Code covered by the MIT License
+//
+// Permission to use, copy, modify, distribute and sell this software for any
+// purpose is hereby granted without fee, provided that the above copyright
+// notice appear in all copies and that both that copyright notice and this
+// permission notice appear in supporting documentation.
+//
+// The authors make no representations about the suitability of this software
+// for any purpose. It is provided "as is" without express or implied warranty.
+//
+// This code DOES NOT accompany the book:
+// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design
+// Patterns Applied". Copyright (c) 2001. Addison-Wesley.
+//
+////////////////////////////////////////////////////////////////////////////////
+#ifndef LOKI_CACHEDFACTORY_INC_
+#define LOKI_CACHEDFACTORY_INC_
+
+// $Id: CachedFactory.h 950 2009-01-26 19:45:54Z syntheticpp $
+
+#include <functional>
+#include <algorithm>
+#include <iostream>
+#include <vector>
+#include <iterator>
+#include <map>
+#include <cassert>
+#include <loki/Key.h>
+
+#ifdef DO_EXTRA_LOKI_TESTS
+ #define D( x ) x
+#else
+ #define D( x ) ;
+#endif
+
+#if defined(_MSC_VER) || defined(__CYGWIN__)
+#include <time.h>
+#endif
+
+/**
+ * \defgroup FactoriesGroup Factories
+ * \defgroup CachedFactoryGroup Cached Factory
+ * \ingroup FactoriesGroup
+ * \brief CachedFactory provides an extension of a Factory with caching
+ * support.
+ *
+ * Once used objects are returned to the CachedFactory that manages its
+ * destruction.
+ * If your code uses lots of "long to construct/destruct objects" using the
+ * CachedFactory will surely speedup the execution.
+ */
+namespace Loki
+{
+/**
+ * \defgroup EncapsulationPolicyCachedFactoryGroup Encapsulation policies
+ * \ingroup CachedFactoryGroup
+ * \brief Defines how the object is returned to the client
+ */
+ /**
+ * \class SimplePointer
+ * \ingroup EncapsulationPolicyCachedFactoryGroup
+ * \brief No encaspulation : returns the pointer
+ *
+ * This implementation does not make any encapsulation.
+ * It simply returns the object's pointer.
+ */
+ template<class AbstractProduct>
+ class SimplePointer
+ {
+ protected:
+ typedef AbstractProduct* ProductReturn;
+ ProductReturn encapsulate(AbstractProduct* pProduct)
+ {
+ return pProduct;
+ }
+
+ AbstractProduct* release(ProductReturn &pProduct)
+ {
+ AbstractProduct* pPointer(pProduct);
+ pProduct=NULL;
+ return pPointer;
+ }
+ const char* name(){return "pointer";}
+ };
+
+/**
+ * \defgroup CreationPolicyCachedFactoryGroup Creation policies
+ * \ingroup CachedFactoryGroup
+ * \brief Defines a way to limit the creation operation.
+ *
+ * For instance one may want to be alerted (Exception) when
+ * - Cache has created a more than X object within the last x seconds
+ * - Cache creation rate has increased dramatically
+ * .
+ * which may result from bad caching strategy, or critical overload
+ */
+ /**
+ * \class NeverCreate
+ * \ingroup CreationPolicyCachedFactoryGroup
+ * \brief Never allows creation. Testing purposes only.
+ *
+ * Using this policy will throw an exception.
+ */
+ class NeverCreate
+ {
+ protected:
+ struct Exception : public std::exception
+ {
+ const char* what() const throw() { return "NeverFetch Policy : No Fetching allowed"; }
+ };
+
+ bool canCreate()
+ {
+ throw Exception();
+ }
+
+ void onCreate(){}
+ void onDestroy(){}
+ const char* name(){return "never";}
+ };
+
+ /**
+ * \class AlwaysCreate
+ * \ingroup CreationPolicyCachedFactoryGroup
+ * \brief Always allows creation.
+ *
+ * Doesn't limit the creation in any way
+ */
+ class AlwaysCreate
+ {
+ protected:
+ bool canCreate()
+ {
+ return true;
+ }
+
+ void onCreate(){}
+ void onDestroy(){}
+ const char* name(){return "always";}
+ };
+
+
+ /**
+ * \class RateLimitedCreation
+ * \ingroup CreationPolicyCachedFactoryGroup
+ * \brief Limit in rate.
+ *
+ * This implementation will prevent from Creating more than maxCreation objects
+ * within byTime ms by throwing an exception.
+ * Could be usefull to detect prevent loads (http connection for instance).
+ * Use the setRate method to set the rate parameters.
+ * default is 10 objects in a second.
+ */
+ // !! CAUTION !!
+ // The std::clock() function is not quite precise
+ // under linux this policy might not work.
+ // TODO : get a better implementation (platform dependant)
+ class RateLimitedCreation
+ {
+ private:
+ typedef std::vector< clock_t > Vector;
+ Vector m_vTimes;
+ unsigned maxCreation;
+ clock_t timeValidity;
+ clock_t lastUpdate;
+
+ void cleanVector()
+ {
+ using namespace std;
+ clock_t currentTime = clock();
+ D( cout << "currentTime = " << currentTime<< endl; )
+ D( cout << "currentTime - lastUpdate = " << currentTime - lastUpdate<< endl; )
+ if(currentTime - lastUpdate > timeValidity)
+ {
+ m_vTimes.clear();
+ D( cout << " is less than time validity " << timeValidity; )
+ D( cout << " so clearing vector" << endl; )
+ }
+ else
+ {
+ D( cout << "Cleaning time less than " << currentTime - timeValidity << endl; )
+ D( displayVector(); )
+ Vector::iterator newEnd = remove_if(m_vTimes.begin(), m_vTimes.end(), bind2nd(less<clock_t>(), currentTime - timeValidity));
+ // this rearrangement might be costly, consider optimization
+ // by calling cleanVector in less used onCreate function
+ // ... although it may not be correct
+ m_vTimes.erase(newEnd, m_vTimes.end());
+ D( displayVector(); )
+ }
+ lastUpdate = currentTime;
+ }
+#ifdef DO_EXTRA_LOKI_TESTS
+ void displayVector()
+ {
+ std::cout << "Vector : ";
+ copy(m_vTimes.begin(), m_vTimes.end(), std::ostream_iterator<clock_t>(std::cout, " "));
+ std::cout << std::endl;
+ }
+#endif
+ protected:
+ RateLimitedCreation() : maxCreation(10), timeValidity(CLOCKS_PER_SEC), lastUpdate(clock())
+ {}
+
+ struct Exception : public std::exception
+ {
+ const char* what() const throw() { return "RateLimitedCreation Policy : Exceeded the authorized creation rate"; }
+ };
+
+ bool canCreate()
+ {
+ cleanVector();
+ if(m_vTimes.size()>maxCreation)
+ throw Exception();
+ else
+ return true;
+ }
+
+ void onCreate()
+ {
+ m_vTimes.push_back(clock());
+ }
+
+ void onDestroy()
+ {
+ }
+ const char* name(){return "rate limited";}
+ public:
+ // set the creation rate
+ // No more than maxCreation within byTime milliseconds
+ void setRate(unsigned maxCreation, unsigned byTime)
+ {
+ assert(byTime>0);
+ this->maxCreation = maxCreation;
+ this->timeValidity = static_cast<clock_t>(byTime * CLOCKS_PER_SEC / 1000);
+ D( std::cout << "Setting no more than "<< maxCreation <<" creation within " << this->timeValidity <<" ms"<< std::endl; )
+ }
+ };
+
+ /**
+ * \class AmountLimitedCreation
+ * \ingroup CreationPolicyCachedFactoryGroup
+ * \brief Limit by number of objects
+ *
+ * This implementation will prevent from Creating more than maxCreation objects
+ * within byTime ms by calling eviction policy.
+ * Use the setRate method to set the rate parameters.
+ * default is 10 objects.
+ */
+ class AmountLimitedCreation
+ {
+ private:
+ unsigned maxCreation;
+ unsigned created;
+
+ protected:
+ AmountLimitedCreation() : maxCreation(10), created(0)
+ {}
+
+ bool canCreate()
+ {
+ return !(created>=maxCreation);
+ }
+
+ void onCreate()
+ {
+ ++created;
+ }
+
+ void onDestroy()
+ {
+ --created;
+ }
+ const char* name(){return "amount limited";}
+ public:
+ // set the creation max amount
+ void setMaxCreation(unsigned maxCreation)
+ {
+ assert(maxCreation>0);
+ this->maxCreation = maxCreation;
+ D( std::cout << "Setting no more than " << maxCreation <<" creation" << std::endl; )
+ }
+ };
+
+/**
+ * \defgroup EvictionPolicyCachedFactoryGroup Eviction policies
+ * \ingroup CachedFactoryGroup
+ * \brief Gathers informations about the stored objects and choose a
+ * candidate for eviction.
+ */
+
+ class EvictionException : public std::exception
+ {
+ public:
+ const char* what() const throw() { return "Eviction Policy : trying to make room but no objects are available"; }
+ };
+
+ // The following class is intented to provide helpers to sort
+ // the container that will hold an eviction score
+ template
+ <
+ typename ST, // Score type
+ typename DT // Data type
+ >
+ class EvictionHelper
+ {
+ protected:
+ typedef typename std::map< DT, ST > HitMap;
+ typedef typename HitMap::iterator HitMapItr;
+ private:
+ typedef std::pair< ST, DT > SwappedPair;
+ typedef std::multimap< ST, DT > SwappedHitMap;
+ typedef typename SwappedHitMap::iterator SwappedHitMapItr;
+ protected:
+ HitMap m_mHitCount;
+
+ // This function sorts the map according to the score
+ // and returns the lower bound of the sorted container
+ DT& getLowerBound(){
+ assert(!m_mHitCount.empty());
+ // inserting the swapped pair into a multimap
+ SwappedHitMap copyMap;
+ for(HitMapItr itr = m_mHitCount.begin(); itr != m_mHitCount.end(); ++itr)
+ copyMap.insert(SwappedPair((*itr).second, (*itr).first));
+ if((*copyMap.rbegin()).first == 0) // the higher score is 0 ...
+ throw EvictionException(); // there is no key evict
+ return (*copyMap.begin()).second;
+ }
+ };
+
+ /**
+ * \class EvictLRU
+ * \ingroup EvictionPolicyCachedFactoryGroup
+ * \brief Evicts least accessed objects first.
+ *
+ * Implementation of the Least recent used algorithm as
+ * described in http://en.wikipedia.org/wiki/Page_replacement_algorithms .
+ *
+ * WARNING : If an object is heavily fetched
+ * (more than ULONG_MAX = UINT_MAX = 4294967295U)
+ * it could unfortunately be removed from the cache.
+ */
+ template
+ <
+ typename DT, // Data Type (AbstractProduct*)
+ typename ST = unsigned // default data type to use as Score Type
+ >
+ class EvictLRU : public EvictionHelper< ST , DT >
+ {
+ private:
+ typedef EvictionHelper< ST , DT > EH;
+ protected:
+
+ virtual ~EvictLRU(){}
+
+ // OnStore initialize the counter for the new key
+ // If the key already exists, the counter is reseted
+ void onCreate(const DT& key)
+ {
+ EH::m_mHitCount[key] = 0;
+ }
+
+ void onFetch(const DT&)
+ {
+ }
+
+ // onRelease increments the hit counter associated with the object
+ void onRelease(const DT& key)
+ {
+ ++(EH::m_mHitCount[key]);
+ }
+
+ void onDestroy(const DT& key)
+ {
+ EH::m_mHitCount.erase(key);
+ }
+
+ // this function is implemented in Cache and redirected
+ // to the Storage Policy
+ virtual void remove(DT const key)=0;
+
+ // LRU Eviction policy
+ void evict()
+ {
+ remove(EH::getLowerBound());
+ }
+ const char* name(){return "LRU";}
+ };
+
+ /**
+ * \class EvictAging
+ * \ingroup EvictionPolicyCachedFactoryGroup
+ * \brief LRU aware of the time span of use
+ *
+ * Implementation of the Aging algorithm as
+ * described in http://en.wikipedia.org/wiki/Page_replacement_algorithms .
+ *
+ * This method is much more costly than evict LRU so
+ * if you need extreme performance consider switching to EvictLRU
+ */
+ template
+ <
+ typename DT, // Data Type (AbstractProduct*)
+ typename ST = unsigned // default data type to use as Score Type
+ >
+ class EvictAging : public EvictionHelper< ST, DT >
+ {
+ private:
+ EvictAging(const EvictAging&);
+ EvictAging& operator=(const EvictAging&);
+ typedef EvictionHelper< ST, DT > EH;
+ typedef typename EH::HitMap HitMap;
+ typedef typename EH::HitMapItr HitMapItr;
+
+ // update the counter
+ template<class T> struct updateCounter : public std::unary_function<T, void>
+ {
+ updateCounter(const DT& key): key_(key){}
+ void operator()(T x)
+ {
+ x.second = (x.first == key_ ? (x.second >> 1) | ( 1 << ((sizeof(ST)-1)*8) ) : x.second >> 1);
+ D( std::cout << x.second << std::endl; )
+ }
+ const DT &key_;
+ updateCounter(const updateCounter& rhs) : key_(rhs.key_){}
+ private:
+ updateCounter& operator=(const updateCounter& rhs);
+ };
+ protected:
+ EvictAging(){}
+ virtual ~EvictAging(){}
+
+ // OnStore initialize the counter for the new key
+ // If the key already exists, the counter is reseted
+ void onCreate(const DT& key){
+ EH::m_mHitCount[key] = 0;
+ }
+
+ void onFetch(const DT&){}
+
+ // onRelease increments the hit counter associated with the object
+ // Updating every counters by iterating over the map
+ // If the key is the key of the fetched object :
+ // the counter is shifted to the right and it's MSB is set to 1
+ // else
+ // the counter is shifted to the left
+ void onRelease(const DT& key)
+ {
+ std::for_each(EH::m_mHitCount.begin(), EH::m_mHitCount.end(), updateCounter< typename HitMap::value_type >(key));
+ }
+
+ void onDestroy(const DT& key)
+ {
+ EH::m_mHitCount.erase(key);
+ }
+
+ // this function is implemented in Cache and redirected
+ // to the Storage Policy
+ virtual void remove(DT const key)=0;
+
+ // LRU with Aging Eviction policy
+ void evict()
+ {
+ remove(EH::getLowerBound());
+ }
+ const char* name(){return "LRU with aging";}
+ };
+
+ /**
+ * \class EvictRandom
+ * \ingroup EvictionPolicyCachedFactoryGroup
+ * \brief Evicts a random object
+ *
+ * Implementation of the Random algorithm as
+ * described in http://en.wikipedia.org/wiki/Page_replacement_algorithms .
+ */
+ template
+ <
+ typename DT, // Data Type (AbstractProduct*)
+ typename ST = void // Score Type not used by this policy
+ >
+ class EvictRandom
+ {
+ private:
+ std::vector< DT > m_vKeys;
+ typedef typename std::vector< DT >::size_type size_type;
+ typedef typename std::vector< DT >::iterator iterator;
+
+ protected:
+
+ virtual ~EvictRandom(){}
+
+ void onCreate(const DT&){
+ }
+
+ void onFetch(const DT& ){
+ }
+
+ void onRelease(const DT& key){
+ m_vKeys.push_back(key);
+ }
+
+ void onDestroy(const DT& key){
+ using namespace std;
+ m_vKeys.erase(remove_if(m_vKeys.begin(), m_vKeys.end(), bind2nd(equal_to< DT >(), key)), m_vKeys.end());
+ }
+
+ // Implemented in Cache and redirected to the Storage Policy
+ virtual void remove(DT const key)=0;
+
+ // Random Eviction policy
+ void evict()
+ {
+ if(m_vKeys.empty())
+ throw EvictionException();
+ size_type random = static_cast<size_type>((m_vKeys.size()*rand())/(static_cast<size_type>(RAND_MAX) + 1));
+ remove(*(m_vKeys.begin()+random));
+ }
+ const char* name(){return "random";}
+ };
+
+/**
+ * \defgroup StatisticPolicyCachedFactoryGroup Statistic policies
+ * \ingroup CachedFactoryGroup
+ * \brief Gathers information about the cache.
+ *
+ * For debugging purpose this policy proposes to gather informations
+ * about the cache. This could be useful to determine whether the cache is
+ * mandatory or if the policies are well suited to the application.
+ */
+ /**
+ * \class NoStatisticPolicy
+ * \ingroup StatisticPolicyCachedFactoryGroup
+ * \brief Do nothing
+ *
+ * Should be used in release code for better performances
+ */
+ class NoStatisticPolicy
+ {
+ protected:
+ void onDebug(){}
+ void onFetch(){}
+ void onRelease(){}
+ void onCreate(){}
+ void onDestroy(){}
+ const char* name(){return "no";}
+ };
+
+ /**
+ * \class SimpleStatisticPolicy
+ * \ingroup StatisticPolicyCachedFactoryGroup
+ * \brief Simple statistics
+ *
+ * Provides the following informations about the cache :
+ * - Created objects
+ * - Fetched objects
+ * - Destroyed objects
+ * - Cache hit
+ * - Cache miss
+ * - Currently allocated
+ * - Currently out
+ * - Cache overall efficiency
+ */
+ class SimpleStatisticPolicy
+ {
+ private:
+ unsigned allocated, created, hit, out, fetched;
+ protected:
+ SimpleStatisticPolicy() : allocated(0), created(0), hit(0), out(0), fetched(0)
+ {
+ }
+
+ void onDebug()
+ {
+ using namespace std;
+ cout << "############################" << endl;
+ cout << "## About this cache " << this << endl;
+ cout << "## + Created objects : " << created << endl;
+ cout << "## + Fetched objects : " << fetched << endl;
+ cout << "## + Destroyed objects : " << created - allocated << endl;
+ cout << "## + Cache hit : " << hit << endl;
+ cout << "## + Cache miss : " << fetched - hit << endl;
+ cout << "## + Currently allocated : " << allocated << endl;
+ cout << "## + Currently out : " << out << endl;
+ cout << "############################" << endl;
+ if(fetched!=0){
+ cout << "## Overall efficiency " << 100*double(hit)/fetched <<"%"<< endl;
+ cout << "############################" << endl;
+ }
+ cout << endl;
+ }
+
+ void onFetch()
+ {
+ ++fetched;
+ ++out;
+ ++hit;
+ }
+ void onRelease()
+ {
+ --out;
+ }
+ void onCreate()
+ {
+ ++created;
+ ++allocated;
+ --hit;
+ }
+ void onDestroy()
+ {
+ --allocated;
+ }
+
+ const char* name(){return "simple";}
+ public:
+ unsigned getCreated(){return created;}
+ unsigned getFetched(){return fetched;}
+ unsigned getHit(){return hit;}
+ unsigned getMissed(){return fetched - hit;}
+ unsigned getAllocated(){return allocated;}
+ unsigned getOut(){return out;}
+ unsigned getDestroyed(){return created-allocated;}
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Cache Factory definition
+ ///////////////////////////////////////////////////////////////////////////
+ class CacheException : public std::exception
+ {
+ public:
+ const char* what() const throw() { return "Internal Cache Error"; }
+ };
+
+ /**
+ * \class CachedFactory
+ * \ingroup CachedFactoryGroup
+ * \brief Factory with caching support
+ *
+ * This class acts as a Factory (it creates objects)
+ * but also keeps the already created objects to prevent
+ * long constructions time.
+ *
+ * Note this implementation do not retain ownership.
+ */
+ template
+ <
+ class AbstractProduct,
+ typename IdentifierType,
+ typename CreatorParmTList = NullType,
+ template<class> class EncapsulationPolicy = SimplePointer,
+ class CreationPolicy = AlwaysCreate,
+ template <typename , typename> class EvictionPolicy = EvictRandom,
+ class StatisticPolicy = NoStatisticPolicy,
+ template<typename, class> class FactoryErrorPolicy = DefaultFactoryError,
+ class ObjVector = std::vector<AbstractProduct*>
+ >
+ class CachedFactory :
+ protected EncapsulationPolicy<AbstractProduct>,
+ public CreationPolicy, public StatisticPolicy, EvictionPolicy< AbstractProduct * , unsigned >
+ {
+ private:
+ typedef Factory< AbstractProduct, IdentifierType, CreatorParmTList, FactoryErrorPolicy> MyFactory;
+ typedef FactoryImpl< AbstractProduct, IdentifierType, CreatorParmTList > Impl;
+ typedef Functor< AbstractProduct* , CreatorParmTList > ProductCreator;
+ typedef EncapsulationPolicy<AbstractProduct> NP;
+ typedef CreationPolicy CP;
+ typedef StatisticPolicy SP;
+ typedef EvictionPolicy< AbstractProduct* , unsigned > EP;
+
+ typedef typename Impl::Parm1 Parm1;
+ typedef typename Impl::Parm2 Parm2;
+ typedef typename Impl::Parm3 Parm3;
+ typedef typename Impl::Parm4 Parm4;
+ typedef typename Impl::Parm5 Parm5;
+ typedef typename Impl::Parm6 Parm6;
+ typedef typename Impl::Parm7 Parm7;
+ typedef typename Impl::Parm8 Parm8;
+ typedef typename Impl::Parm9 Parm9;
+ typedef typename Impl::Parm10 Parm10;
+ typedef typename Impl::Parm11 Parm11;
+ typedef typename Impl::Parm12 Parm12;
+ typedef typename Impl::Parm13 Parm13;
+ typedef typename Impl::Parm14 Parm14;
+ typedef typename Impl::Parm15 Parm15;
+
+ public:
+ typedef typename NP::ProductReturn ProductReturn;
+ private:
+ typedef Key< Impl, IdentifierType > MyKey;
+ typedef std::map< MyKey, ObjVector > KeyToObjVectorMap;
+ typedef std::map< AbstractProduct*, MyKey > FetchedObjToKeyMap;
+
+ MyFactory factory;
+ KeyToObjVectorMap fromKeyToObjVector;
+ FetchedObjToKeyMap providedObjects;
+ unsigned outObjects;
+
+ ObjVector& getContainerFromKey(MyKey key){
+ return fromKeyToObjVector[key];
+ }
+
+ AbstractProduct* const getPointerToObjectInContainer(ObjVector &entry)
+ {
+ if(entry.empty()) // No object available
+ { // the object will be created in the calling function.
+ // It has to be created in the calling function because of
+ // the variable number of parameters for CreateObject(...) method
+ return NULL;
+ }
+ else
+ { // returning the found object
+ AbstractProduct* pObject(entry.back());
+ assert(pObject!=NULL);
+ entry.pop_back();
+ return pObject;
+ }
+ }
+
+ bool shouldCreateObject(AbstractProduct * const pProduct){
+ if(pProduct!=NULL) // object already exists
+ return false;
+ if(CP::canCreate()==false) // Are we allowed to Create ?
+ EP::evict(); // calling Eviction Policy to clean up
+ return true;
+ }
+
+ void ReleaseObjectFromContainer(ObjVector &entry, AbstractProduct * const object)
+ {
+ entry.push_back(object);
+ }
+
+ void onFetch(AbstractProduct * const pProduct)
+ {
+ SP::onFetch();
+ EP::onFetch(pProduct);
+ ++outObjects;
+ }
+
+ void onRelease(AbstractProduct * const pProduct)
+ {
+ SP::onRelease();
+ EP::onRelease(pProduct);
+ --outObjects;
+ }
+
+ void onCreate(AbstractProduct * const pProduct)
+ {
+ CP::onCreate();
+ SP::onCreate();
+ EP::onCreate(pProduct);
+ }
+
+ void onDestroy(AbstractProduct * const pProduct)
+ {
+ CP::onDestroy();
+ SP::onDestroy();
+ EP::onDestroy(pProduct);
+ }
+
+ // delete the object
+ template<class T> struct deleteObject : public std::unary_function<T, void>
+ {
+ void operator()(T x){ delete x; }
+ };
+
+ // delete the objects in the vector
+ template<class T> struct deleteVectorObjects : public std::unary_function<T, void>
+ {
+ void operator()(T x){
+ ObjVector &vec(x.second);
+ std::for_each(vec.begin(), vec.end(), deleteObject< typename ObjVector::value_type>());
+ }
+ };
+
+ // delete the keys of the map
+ template<class T> struct deleteMapKeys : public std::unary_function<T, void>
+ {
+ void operator()(T x){ delete x.first; }
+ };
+
+ protected:
+ virtual void remove(AbstractProduct * const pProduct)
+ {
+ typename FetchedObjToKeyMap::iterator fetchedItr = providedObjects.find(pProduct);
+ if(fetchedItr!=providedObjects.end()) // object is unreleased.
+ throw CacheException();
+ bool productRemoved = false;
+ typename KeyToObjVectorMap::iterator objVectorItr;
+ typename ObjVector::iterator objItr;
+ for(objVectorItr=fromKeyToObjVector.begin();objVectorItr!=fromKeyToObjVector.end();++objVectorItr)
+ {
+ ObjVector &v((*objVectorItr).second);
+ objItr = remove_if(v.begin(), v.end(), std::bind2nd(std::equal_to<AbstractProduct*>(), pProduct));
+ if(objItr != v.end()) // we found the vector containing pProduct and removed it
+ {
+ onDestroy(pProduct); // warning policies we are about to destroy an object
+ v.erase(objItr, v.end()); // real removing
+ productRemoved = true;
+ break;
+ }
+ }
+ if(productRemoved==false)
+ throw CacheException(); // the product is not in the cache ?!
+ delete pProduct; // deleting it
+ }
+
+ public:
+ CachedFactory() : factory(), fromKeyToObjVector(), providedObjects(), outObjects(0)
+ {
+ }
+
+ ~CachedFactory()
+ {
+ using namespace std;
+ // debug information
+ SP::onDebug();
+ // cleaning the Cache
+ for_each(fromKeyToObjVector.begin(), fromKeyToObjVector.end(),
+ deleteVectorObjects< typename KeyToObjVectorMap::value_type >()
+ );
+ if(!providedObjects.empty())
+ {
+ // The factory is responsible for the creation and destruction of objects.
+ // If objects are out during the destruction of the Factory : deleting anyway.
+ // This might not be a good idea. But throwing an exception in a destructor is
+ // considered as a bad pratice and asserting might be too much.
+ // What to do ? Leaking memory or corrupting in use pointers ? hmm...
+ D( cout << "====>> Cache destructor : deleting "<< providedObjects.size()<<" in use objects <<====" << endl << endl; )
+ for_each(providedObjects.begin(), providedObjects.end(),
+ deleteMapKeys< typename FetchedObjToKeyMap::value_type >()
+ );
+ }
+ }
+
+ ///////////////////////////////////
+ // Acts as the proxy pattern and //
+ // forwards factory methods //
+ ///////////////////////////////////
+
+ bool Register(const IdentifierType& id, ProductCreator creator)
+ {
+ return factory.Register(id, creator);
+ }
+
+ template <class PtrObj, typename CreaFn>
+ bool Register(const IdentifierType& id, const PtrObj& p, CreaFn fn)
+ {
+ return factory.Register(id, p, fn);
+ }
+
+ bool Unregister(const IdentifierType& id)
+ {
+ return factory.Unregister(id);
+ }
+
+ /// Return the registered ID in this Factory
+ std::vector<IdentifierType>& RegisteredIds()
+ {
+ return factory.RegisteredIds();
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id)
+ {
+ MyKey key(id);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id,
+ Parm1 p1)
+ {
+ MyKey key(id,p1);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id,key.p1);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id,
+ Parm1 p1, Parm2 p2)
+ {
+ MyKey key(id,p1,p2);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id,key.p1,key.p2);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id,
+ Parm1 p1, Parm2 p2, Parm3 p3)
+ {
+ MyKey key(id,p1,p2,p3);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id,
+ Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4)
+ {
+ MyKey key(id,p1,p2,p3,p4);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
+ ,key.p4);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id,
+ Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5)
+ {
+ MyKey key(id,p1,p2,p3,p4,p5);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
+ ,key.p4,key.p5);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id,
+ Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5,
+ Parm6 p6)
+ {
+ MyKey key(id,p1,p2,p3,p4,p5,p6);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
+ ,key.p4,key.p5,key.p6);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id,
+ Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5,
+ Parm6 p6, Parm7 p7 )
+ {
+ MyKey key(id,p1,p2,p3,p4,p5,p6,p7);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
+ ,key.p4,key.p5,key.p6,key.p7);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id,
+ Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5,
+ Parm6 p6, Parm7 p7, Parm8 p8)
+ {
+ MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
+ ,key.p4,key.p5,key.p6,key.p7,key.p8);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id,
+ Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5,
+ Parm6 p6, Parm7 p7, Parm8 p8, Parm9 p9)
+ {
+ MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8,p9);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
+ ,key.p4,key.p5,key.p6,key.p7,key.p8,key.p9);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id,
+ Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5,
+ Parm6 p6, Parm7 p7, Parm8 p8, Parm9 p9,Parm10 p10)
+ {
+ MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
+ ,key.p4,key.p5,key.p6,key.p7,key.p8,key.p9,key.p10);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id,
+ Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5,
+ Parm6 p6, Parm7 p7, Parm8 p8, Parm9 p9, Parm10 p10,
+ Parm11 p11)
+ {
+ MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
+ ,key.p4,key.p5,key.p6,key.p7,key.p8,key.p9,key.p10,key.p11);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id,
+ Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5,
+ Parm6 p6, Parm7 p7, Parm8 p8, Parm9 p9, Parm10 p10,
+ Parm11 p11, Parm12 p12)
+ {
+ MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
+ ,key.p4,key.p5,key.p6,key.p7,key.p8,key.p9,key.p10,key.p11,key.p12);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id,
+ Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5,
+ Parm6 p6, Parm7 p7, Parm8 p8, Parm9 p9, Parm10 p10,
+ Parm11 p11, Parm12 p12, Parm13 p13)
+ {
+ MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
+ ,key.p4,key.p5,key.p6,key.p7,key.p8,key.p9,key.p10,key.p11,key.p12
+ ,key.p13);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id,
+ Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5,
+ Parm6 p6, Parm7 p7, Parm8 p8, Parm9 p9, Parm10 p10,
+ Parm11 p11, Parm12 p12, Parm13 p13, Parm14 p14)
+ {
+ MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
+ ,key.p4,key.p5,key.p6,key.p7,key.p8,key.p9,key.p10,key.p11,key.p12
+ ,key.p13,key.p14);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ ProductReturn CreateObject(const IdentifierType& id,
+ Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5,
+ Parm6 p6, Parm7 p7, Parm8 p8, Parm9 p9, Parm10 p10,
+ Parm11 p11, Parm12 p12, Parm13 p13, Parm14 p14, Parm15 p15)
+ {
+ MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15);
+ AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
+ if(shouldCreateObject(pProduct))
+ {
+ pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
+ ,key.p4,key.p5,key.p6,key.p7,key.p8,key.p9,key.p10,key.p11,key.p12
+ ,key.p13,key.p14,key.p15);
+ onCreate(pProduct);
+ }
+ onFetch(pProduct);
+ providedObjects[pProduct] = key;
+ return NP::encapsulate(pProduct);
+ }
+
+ /// Use this function to release the object
+ /**
+ * if execution brakes in this function then you tried
+ * to release an object that wasn't provided by this Cache
+ * ... which is bad :-)
+ */
+ void ReleaseObject(ProductReturn &object)
+ {
+ AbstractProduct* pProduct(NP::release(object));
+ typename FetchedObjToKeyMap::iterator itr = providedObjects.find(pProduct);
+ if(itr == providedObjects.end())
+ throw CacheException();
+ onRelease(pProduct);
+ ReleaseObjectFromContainer(getContainerFromKey((*itr).second), pProduct);
+ providedObjects.erase(itr);
+ }
+
+ /// display the cache configuration
+ void displayCacheType()
+ {
+ using namespace std;
+ cout << "############################" << endl;
+ cout << "## Cache configuration" << endl;
+ cout << "## + Encapsulation " << NP::name() << endl;
+ cout << "## + Creating " << CP::name() << endl;
+ cout << "## + Eviction " << EP::name() << endl;
+ cout << "## + Statistics " << SP::name() << endl;
+ cout << "############################" << endl;
+ }
+ };
+} // namespace Loki
+
+#endif // end file guardian
+
bgstack15