]> git.cworth.org Git - vogl/blob - src/extlib/loki/include/loki/CachedFactory.h
Initial vogl checkin
[vogl] / src / extlib / loki / include / loki / CachedFactory.h
1 ////////////////////////////////////////////////////////////////////////////////
2 // The Loki Library
3 // Copyright (c) 2006 by Guillaume Chatelet
4 //
5 // Code covered by the MIT License
6 //
7 // Permission to use, copy, modify, distribute and sell this software for any
8 // purpose is hereby granted without fee, provided that the above copyright
9 // notice appear in all copies and that both that copyright notice and this
10 // permission notice appear in supporting documentation.
11 //
12 // The authors make no representations about the suitability of this software
13 // for any purpose. It is provided "as is" without express or implied warranty.
14 //
15 // This code DOES NOT accompany the book:
16 // Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design
17 //     Patterns Applied". Copyright (c) 2001. Addison-Wesley.
18 //
19 ////////////////////////////////////////////////////////////////////////////////
20 #ifndef LOKI_CACHEDFACTORY_INC_
21 #define LOKI_CACHEDFACTORY_INC_
22
23 // $Id: CachedFactory.h 950 2009-01-26 19:45:54Z syntheticpp $
24
25 #include <functional>
26 #include <algorithm>
27 #include <iostream>
28 #include <vector>
29 #include <iterator>
30 #include <map>
31 #include <cassert>
32 #include <loki/Key.h>
33
34 #ifdef DO_EXTRA_LOKI_TESTS
35 #define D( x ) x
36 #else
37 #define D( x ) ;
38 #endif
39
40 #if defined(_MSC_VER)  || defined(__CYGWIN__)
41 #include <time.h>
42 #endif
43
44 /**
45  * \defgroup    FactoriesGroup Factories
46  * \defgroup    CachedFactoryGroup Cached Factory
47  * \ingroup             FactoriesGroup
48  * \brief               CachedFactory provides an extension of a Factory with caching
49  * support.
50  *
51  * Once used objects are returned to the CachedFactory that manages its
52  * destruction.
53  * If your code uses lots of "long to construct/destruct objects" using the
54  * CachedFactory will surely speedup the execution.
55  */
56 namespace Loki
57 {
58 /**
59  * \defgroup    EncapsulationPolicyCachedFactoryGroup   Encapsulation policies
60  * \ingroup     CachedFactoryGroup
61  * \brief       Defines how the object is returned to the client
62  */
63 /**
64  * \class       SimplePointer
65  * \ingroup     EncapsulationPolicyCachedFactoryGroup
66  * \brief       No encaspulation : returns the pointer
67  *
68  * This implementation does not make any encapsulation.
69  * It simply returns the object's pointer.
70  */
71 template<class AbstractProduct>
72 class SimplePointer
73 {
74 protected:
75         typedef AbstractProduct *ProductReturn;
76         ProductReturn encapsulate(AbstractProduct *pProduct)
77         {
78                 return pProduct;
79         }
80
81         AbstractProduct *release(ProductReturn &pProduct)
82         {
83                 AbstractProduct *pPointer(pProduct);
84                 pProduct=NULL;
85                 return pPointer;
86         }
87         const char *name()
88         {
89                 return "pointer";
90         }
91 };
92
93 /**
94  * \defgroup    CreationPolicyCachedFactoryGroup                Creation policies
95  * \ingroup             CachedFactoryGroup
96  * \brief               Defines a way to limit the creation operation.
97  *
98  * For instance one may want to be alerted (Exception) when
99  * - Cache has created a more than X object within the last x seconds
100  * - Cache creation rate has increased dramatically
101  * .
102  * which may result from bad caching strategy, or critical overload
103  */
104 /**
105  * \class       NeverCreate
106  * \ingroup     CreationPolicyCachedFactoryGroup
107  * \brief       Never allows creation. Testing purposes only.
108  *
109  * Using this policy will throw an exception.
110  */
111 class NeverCreate
112 {
113 protected:
114         struct Exception : public std::exception
115         {
116                 const char *what() const throw()
117                 {
118                         return "NeverFetch Policy : No Fetching allowed";
119                 }
120         };
121
122         bool canCreate()
123         {
124                 throw Exception();
125         }
126
127         void onCreate() {}
128         void onDestroy() {}
129         const char *name()
130         {
131                 return "never";
132         }
133 };
134
135 /**
136  * \class               AlwaysCreate
137  * \ingroup     CreationPolicyCachedFactoryGroup
138  * \brief               Always allows creation.
139  *
140  * Doesn't limit the creation in any way
141  */
142 class AlwaysCreate
143 {
144 protected:
145         bool canCreate()
146         {
147                 return true;
148         }
149
150         void onCreate() {}
151         void onDestroy() {}
152         const char *name()
153         {
154                 return "always";
155         }
156 };
157
158
159 /**
160  * \class       RateLimitedCreation
161  * \ingroup     CreationPolicyCachedFactoryGroup
162  * \brief       Limit in rate.
163  *
164  * This implementation will prevent from Creating more than maxCreation objects
165  * within byTime ms by throwing an exception.
166  * Could be usefull to detect prevent loads (http connection for instance).
167  * Use the setRate method to set the rate parameters.
168  * default is 10 objects in a second.
169  */
170 // !! CAUTION !!
171 // The std::clock() function is not quite precise
172 // under linux this policy might not work.
173 // TODO : get a better implementation (platform dependant)
174 class RateLimitedCreation
175 {
176 private:
177         typedef std::vector< clock_t > Vector;
178         Vector m_vTimes;
179         unsigned maxCreation;
180         clock_t timeValidity;
181         clock_t lastUpdate;
182
183         void cleanVector()
184         {
185                 using namespace std;
186                 clock_t currentTime = clock();
187                 D( cout << "currentTime = " << currentTime<< endl; )
188                 D( cout << "currentTime - lastUpdate = " << currentTime - lastUpdate<< endl; )
189                 if(currentTime - lastUpdate > timeValidity)
190                 {
191                         m_vTimes.clear();
192                         D( cout << " is less than time validity " << timeValidity; )
193                         D( cout << " so clearing vector" << endl; )
194                 }
195                 else
196                 {
197                         D( cout << "Cleaning time less than " << currentTime - timeValidity << endl; )
198                         D( displayVector(); )
199                         Vector::iterator newEnd = remove_if(m_vTimes.begin(), m_vTimes.end(), bind2nd(less<clock_t>(), currentTime - timeValidity));
200                         // this rearrangement might be costly, consider optimization
201                         // by calling cleanVector in less used onCreate function
202                         // ... although it may not be correct
203                         m_vTimes.erase(newEnd, m_vTimes.end());
204                         D( displayVector(); )
205                 }
206                 lastUpdate = currentTime;
207         }
208 #ifdef DO_EXTRA_LOKI_TESTS
209         void displayVector()
210         {
211                 std::cout << "Vector : ";
212                 copy(m_vTimes.begin(), m_vTimes.end(), std::ostream_iterator<clock_t>(std::cout, " "));
213                 std::cout << std::endl;
214         }
215 #endif
216 protected:
217         RateLimitedCreation() : maxCreation(10), timeValidity(CLOCKS_PER_SEC), lastUpdate(clock())
218         {}
219
220         struct Exception : public std::exception
221         {
222                 const char *what() const throw()
223                 {
224                         return "RateLimitedCreation Policy : Exceeded the authorized creation rate";
225                 }
226         };
227
228         bool canCreate()
229         {
230                 cleanVector();
231                 if(m_vTimes.size()>maxCreation)
232                         throw Exception();
233                 else
234                         return true;
235         }
236
237         void onCreate()
238         {
239                 m_vTimes.push_back(clock());
240         }
241
242         void onDestroy()
243         {
244         }
245         const char *name()
246         {
247                 return "rate limited";
248         }
249 public:
250         // set the creation rate
251         // No more than maxCreation within byTime milliseconds
252         void setRate(unsigned maxCreation, unsigned byTime)
253         {
254                 assert(byTime>0);
255                 this->maxCreation = maxCreation;
256                 this->timeValidity = static_cast<clock_t>(byTime * CLOCKS_PER_SEC / 1000);
257                 D( std::cout << "Setting no more than "<< maxCreation <<" creation within " << this->timeValidity <<" ms"<< std::endl; )
258         }
259 };
260
261 /**
262  * \class       AmountLimitedCreation
263  * \ingroup     CreationPolicyCachedFactoryGroup
264  * \brief       Limit by number of objects
265  *
266  * This implementation will prevent from Creating more than maxCreation objects
267  * within byTime ms by calling eviction policy.
268  * Use the setRate method to set the rate parameters.
269  * default is 10 objects.
270  */
271 class AmountLimitedCreation
272 {
273 private:
274         unsigned maxCreation;
275         unsigned created;
276
277 protected:
278         AmountLimitedCreation() : maxCreation(10), created(0)
279         {}
280
281         bool canCreate()
282         {
283                 return !(created>=maxCreation);
284         }
285
286         void onCreate()
287         {
288                 ++created;
289         }
290
291         void onDestroy()
292         {
293                 --created;
294         }
295         const char *name()
296         {
297                 return "amount limited";
298         }
299 public:
300         // set the creation max amount
301         void setMaxCreation(unsigned maxCreation)
302         {
303                 assert(maxCreation>0);
304                 this->maxCreation = maxCreation;
305                 D( std::cout << "Setting no more than " << maxCreation <<" creation" << std::endl; )
306         }
307 };
308
309 /**
310  * \defgroup    EvictionPolicyCachedFactoryGroup                Eviction policies
311  * \ingroup     CachedFactoryGroup
312  * \brief       Gathers informations about the stored objects and choose a
313  * candidate for eviction.
314  */
315
316 class EvictionException : public std::exception
317 {
318 public:
319         const char *what() const throw()
320         {
321                 return "Eviction Policy : trying to make room but no objects are available";
322         }
323 };
324
325 // The following class is intented to provide helpers to sort
326 // the container that will hold an eviction score
327 template
328 <
329 typename ST, // Score type
330          typename DT // Data type
331          >
332 class EvictionHelper
333 {
334 protected:
335         typedef typename std::map< DT, ST >                     HitMap;
336         typedef typename HitMap::iterator                       HitMapItr;
337 private:
338         typedef std::pair< ST, DT >                                     SwappedPair;
339         typedef std::multimap< ST, DT >                         SwappedHitMap;
340         typedef typename SwappedHitMap::iterator        SwappedHitMapItr;
341 protected:
342         HitMap                                                                          m_mHitCount;
343
344         // This function sorts the map according to the score
345         // and returns the lower bound of the sorted container
346         DT      &getLowerBound()
347         {
348                 assert(!m_mHitCount.empty());
349                 // inserting the swapped pair into a multimap
350                 SwappedHitMap copyMap;
351                 for(HitMapItr itr = m_mHitCount.begin(); itr != m_mHitCount.end(); ++itr)
352                         copyMap.insert(SwappedPair((*itr).second, (*itr).first));
353                 if((*copyMap.rbegin()).first == 0) // the higher score is 0 ...
354                         throw EvictionException(); // there is no key evict
355                 return (*copyMap.begin()).second;
356         }
357 };
358
359 /**
360  * \class       EvictLRU
361  * \ingroup     EvictionPolicyCachedFactoryGroup
362  * \brief       Evicts least accessed objects first.
363  *
364  * Implementation of the Least recent used algorithm as
365  * described in http://en.wikipedia.org/wiki/Page_replacement_algorithms .
366  *
367  * WARNING : If an object is heavily fetched
368  * (more than ULONG_MAX = UINT_MAX = 4294967295U)
369  * it could unfortunately be removed from the cache.
370  */
371 template
372 <
373 typename DT, // Data Type (AbstractProduct*)
374          typename ST = unsigned // default data type to use as Score Type
375          >
376 class EvictLRU : public EvictionHelper< ST , DT >
377 {
378 private:
379         typedef EvictionHelper< ST , DT >       EH;
380 protected:
381
382         virtual ~EvictLRU() {}
383
384         // OnStore initialize the counter for the new key
385         // If the key already exists, the counter is reseted
386         void onCreate(const DT &key)
387         {
388                 EH::m_mHitCount[key] = 0;
389         }
390
391         void onFetch(const DT &)
392         {
393         }
394
395         // onRelease increments the hit counter associated with the object
396         void onRelease(const DT &key)
397         {
398                 ++(EH::m_mHitCount[key]);
399         }
400
401         void onDestroy(const DT &key)
402         {
403                 EH::m_mHitCount.erase(key);
404         }
405
406         // this function is implemented in Cache and redirected
407         // to the Storage Policy
408         virtual void remove(DT const key)=0;
409
410         // LRU Eviction policy
411         void evict()
412         {
413                 remove(EH::getLowerBound());
414         }
415         const char *name()
416         {
417                 return "LRU";
418         }
419 };
420
421 /**
422  * \class       EvictAging
423  * \ingroup     EvictionPolicyCachedFactoryGroup
424  * \brief       LRU aware of the time span of use
425  *
426  * Implementation of the Aging algorithm as
427  * described in http://en.wikipedia.org/wiki/Page_replacement_algorithms .
428  *
429  * This method is much more costly than evict LRU so
430  * if you need extreme performance consider switching to EvictLRU
431  */
432 template
433 <
434 typename DT, // Data Type (AbstractProduct*)
435          typename ST = unsigned // default data type to use as Score Type
436          >
437 class EvictAging : public EvictionHelper< ST, DT >
438 {
439 private:
440         EvictAging(const EvictAging &);
441         EvictAging &operator=(const EvictAging &);
442         typedef EvictionHelper< ST, DT >                                EH;
443         typedef typename EH::HitMap                                             HitMap;
444         typedef typename EH::HitMapItr                                  HitMapItr;
445
446         // update the counter
447         template<class T> struct updateCounter : public std::unary_function<T, void>
448         {
449                 updateCounter(const DT &key): key_(key) {}
450                 void operator()(T x)
451                 {
452                         x.second = (x.first == key_ ? (x.second >> 1) | ( 1 << ((sizeof(ST)-1)*8) ) : x.second >> 1);
453                         D( std::cout <<  x.second << std::endl; )
454                 }
455                 const DT &key_;
456                 updateCounter(const updateCounter &rhs) : key_(rhs.key_) {}
457         private:
458                 updateCounter &operator=(const updateCounter &rhs);
459         };
460 protected:
461         EvictAging() {}
462         virtual ~EvictAging() {}
463
464         // OnStore initialize the counter for the new key
465         // If the key already exists, the counter is reseted
466         void onCreate(const DT &key)
467         {
468                 EH::m_mHitCount[key] = 0;
469         }
470
471         void onFetch(const DT &) {}
472
473         // onRelease increments the hit counter associated with the object
474         // Updating every counters by iterating over the map
475         // If the key is the key of the fetched object :
476         //  the counter is shifted to the right and it's MSB is set to 1
477         // else
478         //  the counter is shifted to the left
479         void onRelease(const DT &key)
480         {
481                 std::for_each(EH::m_mHitCount.begin(), EH::m_mHitCount.end(), updateCounter< typename HitMap::value_type >(key));
482         }
483
484         void onDestroy(const DT &key)
485         {
486                 EH::m_mHitCount.erase(key);
487         }
488
489         // this function is implemented in Cache and redirected
490         // to the Storage Policy
491         virtual void remove(DT const key)=0;
492
493         // LRU with Aging Eviction policy
494         void evict()
495         {
496                 remove(EH::getLowerBound());
497         }
498         const char *name()
499         {
500                 return "LRU with aging";
501         }
502 };
503
504 /**
505  * \class       EvictRandom
506  * \ingroup     EvictionPolicyCachedFactoryGroup
507  * \brief       Evicts a random object
508  *
509  * Implementation of the Random algorithm as
510  * described in http://en.wikipedia.org/wiki/Page_replacement_algorithms .
511  */
512 template
513 <
514 typename DT, // Data Type (AbstractProduct*)
515          typename ST = void // Score Type not used by this policy
516          >
517 class EvictRandom
518 {
519 private:
520         std::vector< DT >       m_vKeys;
521         typedef typename std::vector< DT >::size_type   size_type;
522         typedef typename std::vector< DT >::iterator            iterator;
523
524 protected:
525
526         virtual ~EvictRandom() {}
527
528         void onCreate(const DT &)
529         {
530         }
531
532         void onFetch(const DT & )
533         {
534         }
535
536         void onRelease(const DT &key)
537         {
538                 m_vKeys.push_back(key);
539         }
540
541         void onDestroy(const DT &key)
542         {
543                 using namespace std;
544                 m_vKeys.erase(remove_if(m_vKeys.begin(), m_vKeys.end(), bind2nd(equal_to< DT >(), key)), m_vKeys.end());
545         }
546
547         // Implemented in Cache and redirected to the Storage Policy
548         virtual void remove(DT const key)=0;
549
550         // Random Eviction policy
551         void evict()
552         {
553                 if(m_vKeys.empty())
554                         throw EvictionException();
555                 size_type random = static_cast<size_type>((m_vKeys.size()*rand())/(static_cast<size_type>(RAND_MAX) + 1));
556                 remove(*(m_vKeys.begin()+random));
557         }
558         const char *name()
559         {
560                 return "random";
561         }
562 };
563
564 /**
565  * \defgroup    StatisticPolicyCachedFactoryGroup               Statistic policies
566  * \ingroup     CachedFactoryGroup
567  * \brief       Gathers information about the cache.
568  *
569  * For debugging purpose this policy proposes to gather informations
570  * about the cache. This could be useful to determine whether the cache is
571  * mandatory or if the policies are well suited to the application.
572  */
573 /**
574  * \class       NoStatisticPolicy
575  * \ingroup     StatisticPolicyCachedFactoryGroup
576  * \brief       Do nothing
577  *
578  * Should be used in release code for better performances
579  */
580 class NoStatisticPolicy
581 {
582 protected:
583         void onDebug() {}
584         void onFetch() {}
585         void onRelease() {}
586         void onCreate() {}
587         void onDestroy() {}
588         const char *name()
589         {
590                 return "no";
591         }
592 };
593
594 /**
595  * \class       SimpleStatisticPolicy
596  * \ingroup     StatisticPolicyCachedFactoryGroup
597  * \brief       Simple statistics
598  *
599  * Provides the following informations about the cache :
600  *              - Created objects
601  *              - Fetched objects
602  *              - Destroyed objects
603  *              - Cache hit
604  *              - Cache miss
605  *              - Currently allocated
606  *              - Currently out
607  *              - Cache overall efficiency
608  */
609 class SimpleStatisticPolicy
610 {
611 private:
612         unsigned allocated, created, hit, out, fetched;
613 protected:
614         SimpleStatisticPolicy() : allocated(0), created(0), hit(0), out(0), fetched(0)
615         {
616         }
617
618         void onDebug()
619         {
620                 using namespace std;
621                 cout << "############################" << endl;
622                 cout << "## About this cache " << this << endl;
623                 cout << "## + Created objects     : " << created << endl;
624                 cout << "## + Fetched objects     : " << fetched << endl;
625                 cout << "## + Destroyed objects   : " << created - allocated << endl;
626                 cout << "## + Cache hit           : " << hit << endl;
627                 cout << "## + Cache miss          : " << fetched - hit << endl;
628                 cout << "## + Currently allocated : " << allocated << endl;
629                 cout << "## + Currently out       : " << out << endl;
630                 cout << "############################" << endl;
631                 if(fetched!=0)
632                 {
633                         cout << "## Overall efficiency " << 100*double(hit)/fetched <<"%"<< endl;
634                         cout << "############################" << endl;
635                 }
636                 cout << endl;
637         }
638
639         void onFetch()
640         {
641                 ++fetched;
642                 ++out;
643                 ++hit;
644         }
645         void onRelease()
646         {
647                 --out;
648         }
649         void onCreate()
650         {
651                 ++created;
652                 ++allocated;
653                 --hit;
654         }
655         void onDestroy()
656         {
657                 --allocated;
658         }
659
660         const char *name()
661         {
662                 return "simple";
663         }
664 public:
665         unsigned getCreated()
666         {
667                 return created;
668         }
669         unsigned getFetched()
670         {
671                 return fetched;
672         }
673         unsigned getHit()
674         {
675                 return hit;
676         }
677         unsigned getMissed()
678         {
679                 return fetched - hit;
680         }
681         unsigned getAllocated()
682         {
683                 return allocated;
684         }
685         unsigned getOut()
686         {
687                 return out;
688         }
689         unsigned getDestroyed()
690         {
691                 return created-allocated;
692         }
693 };
694
695 ///////////////////////////////////////////////////////////////////////////
696 // Cache Factory definition
697 ///////////////////////////////////////////////////////////////////////////
698 class CacheException : public std::exception
699 {
700 public:
701         const char *what() const throw()
702         {
703                 return "Internal Cache Error";
704         }
705 };
706
707 /**
708  * \class               CachedFactory
709  * \ingroup             CachedFactoryGroup
710  * \brief               Factory with caching support
711  *
712  * This class acts as a Factory (it creates objects)
713  * but also keeps the already created objects to prevent
714  * long constructions time.
715  *
716  * Note this implementation do not retain ownership.
717  */
718 template
719 <
720 class AbstractProduct,
721       typename IdentifierType,
722       typename CreatorParmTList = NullType,
723       template<class> class EncapsulationPolicy = SimplePointer,
724       class CreationPolicy = AlwaysCreate,
725       template <typename , typename> class EvictionPolicy = EvictRandom,
726       class StatisticPolicy = NoStatisticPolicy,
727       template<typename, class> class FactoryErrorPolicy = DefaultFactoryError,
728       class ObjVector = std::vector<AbstractProduct *>
729       >
730 class CachedFactory :
731         protected EncapsulationPolicy<AbstractProduct>,
732         public CreationPolicy, public StatisticPolicy, EvictionPolicy< AbstractProduct * , unsigned >
733 {
734 private:
735         typedef Factory< AbstractProduct, IdentifierType, CreatorParmTList, FactoryErrorPolicy> MyFactory;
736         typedef FactoryImpl< AbstractProduct, IdentifierType, CreatorParmTList > Impl;
737         typedef Functor< AbstractProduct * , CreatorParmTList > ProductCreator;
738         typedef EncapsulationPolicy<AbstractProduct> NP;
739         typedef CreationPolicy  CP;
740         typedef StatisticPolicy SP;
741         typedef EvictionPolicy< AbstractProduct * , unsigned > EP;
742
743         typedef typename Impl::Parm1 Parm1;
744         typedef typename Impl::Parm2 Parm2;
745         typedef typename Impl::Parm3 Parm3;
746         typedef typename Impl::Parm4 Parm4;
747         typedef typename Impl::Parm5 Parm5;
748         typedef typename Impl::Parm6 Parm6;
749         typedef typename Impl::Parm7 Parm7;
750         typedef typename Impl::Parm8 Parm8;
751         typedef typename Impl::Parm9 Parm9;
752         typedef typename Impl::Parm10 Parm10;
753         typedef typename Impl::Parm11 Parm11;
754         typedef typename Impl::Parm12 Parm12;
755         typedef typename Impl::Parm13 Parm13;
756         typedef typename Impl::Parm14 Parm14;
757         typedef typename Impl::Parm15 Parm15;
758
759 public:
760         typedef typename NP::ProductReturn ProductReturn;
761 private:
762         typedef Key< Impl, IdentifierType > MyKey;
763         typedef std::map< MyKey, ObjVector >  KeyToObjVectorMap;
764         typedef std::map< AbstractProduct *, MyKey >  FetchedObjToKeyMap;
765
766         MyFactory                       factory;
767         KeyToObjVectorMap   fromKeyToObjVector;
768         FetchedObjToKeyMap  providedObjects;
769         unsigned            outObjects;
770
771         ObjVector &getContainerFromKey(MyKey key)
772         {
773                 return fromKeyToObjVector[key];
774         }
775
776         AbstractProduct *const getPointerToObjectInContainer(ObjVector &entry)
777         {
778                 if(entry.empty()) // No object available
779                 {
780                         // the object will be created in the calling function.
781                         // It has to be created in the calling function because of
782                         // the variable number of parameters for CreateObject(...) method
783                         return NULL;
784                 }
785                 else
786                 {
787                         // returning the found object
788                         AbstractProduct *pObject(entry.back());
789                         assert(pObject!=NULL);
790                         entry.pop_back();
791                         return pObject;
792                 }
793         }
794
795         bool shouldCreateObject(AbstractProduct *const pProduct)
796         {
797                 if(pProduct!=NULL) // object already exists
798                         return false;
799                 if(CP::canCreate()==false) // Are we allowed to Create ?
800                         EP::evict(); // calling Eviction Policy to clean up
801                 return true;
802         }
803
804         void ReleaseObjectFromContainer(ObjVector &entry, AbstractProduct *const object)
805         {
806                 entry.push_back(object);
807         }
808
809         void onFetch(AbstractProduct *const pProduct)
810         {
811                 SP::onFetch();
812                 EP::onFetch(pProduct);
813                 ++outObjects;
814         }
815
816         void onRelease(AbstractProduct *const pProduct)
817         {
818                 SP::onRelease();
819                 EP::onRelease(pProduct);
820                 --outObjects;
821         }
822
823         void onCreate(AbstractProduct *const pProduct)
824         {
825                 CP::onCreate();
826                 SP::onCreate();
827                 EP::onCreate(pProduct);
828         }
829
830         void onDestroy(AbstractProduct *const pProduct)
831         {
832                 CP::onDestroy();
833                 SP::onDestroy();
834                 EP::onDestroy(pProduct);
835         }
836
837         // delete the object
838         template<class T> struct deleteObject : public std::unary_function<T, void>
839         {
840                 void operator()(T x)
841                 {
842                         delete x;
843                 }
844         };
845
846         // delete the objects in the vector
847         template<class T> struct deleteVectorObjects : public std::unary_function<T, void>
848         {
849                 void operator()(T x)
850                 {
851                         ObjVector &vec(x.second);
852                         std::for_each(vec.begin(), vec.end(), deleteObject< typename ObjVector::value_type>());
853                 }
854         };
855
856         // delete the keys of the map
857         template<class T> struct deleteMapKeys : public std::unary_function<T, void>
858         {
859                 void operator()(T x)
860                 {
861                         delete x.first;
862                 }
863         };
864
865 protected:
866         virtual void remove(AbstractProduct *const pProduct)
867         {
868                 typename FetchedObjToKeyMap::iterator fetchedItr = providedObjects.find(pProduct);
869                 if(fetchedItr!=providedObjects.end()) // object is unreleased.
870                         throw CacheException();
871                 bool productRemoved = false;
872                 typename KeyToObjVectorMap::iterator objVectorItr;
873                 typename ObjVector::iterator objItr;
874                 for(objVectorItr=fromKeyToObjVector.begin(); objVectorItr!=fromKeyToObjVector.end(); ++objVectorItr)
875                 {
876                         ObjVector &v((*objVectorItr).second);
877                         objItr = remove_if(v.begin(), v.end(), std::bind2nd(std::equal_to<AbstractProduct *>(), pProduct));
878                         if(objItr != v.end()) // we found the vector containing pProduct and removed it
879                         {
880                                 onDestroy(pProduct); // warning policies we are about to destroy an object
881                                 v.erase(objItr, v.end()); // real removing
882                                 productRemoved = true;
883                                 break;
884                         }
885                 }
886                 if(productRemoved==false)
887                         throw CacheException(); // the product is not in the cache ?!
888                 delete pProduct; // deleting it
889         }
890
891 public:
892         CachedFactory() : factory(), fromKeyToObjVector(), providedObjects(), outObjects(0)
893         {
894         }
895
896         ~CachedFactory()
897         {
898                 using namespace std;
899                 // debug information
900                 SP::onDebug();
901                 // cleaning the Cache
902                 for_each(fromKeyToObjVector.begin(), fromKeyToObjVector.end(),
903                          deleteVectorObjects< typename KeyToObjVectorMap::value_type >()
904                         );
905                 if(!providedObjects.empty())
906                 {
907                         // The factory is responsible for the creation and destruction of objects.
908                         // If objects are out during the destruction of the Factory : deleting anyway.
909                         // This might not be a good idea. But throwing an exception in a destructor is
910                         // considered as a bad pratice and asserting might be too much.
911                         // What to do ? Leaking memory or corrupting in use pointers ? hmm...
912                         D( cout << "====>>  Cache destructor : deleting "<< providedObjects.size()<<" in use objects  <<====" << endl << endl; )
913                         for_each(providedObjects.begin(), providedObjects.end(),
914                                  deleteMapKeys< typename FetchedObjToKeyMap::value_type >()
915                                 );
916                 }
917         }
918
919         ///////////////////////////////////
920         // Acts as the proxy pattern and //
921         // forwards factory methods      //
922         ///////////////////////////////////
923
924         bool Register(const IdentifierType &id, ProductCreator creator)
925         {
926                 return factory.Register(id, creator);
927         }
928
929         template <class PtrObj, typename CreaFn>
930         bool Register(const IdentifierType &id, const PtrObj &p, CreaFn fn)
931         {
932                 return factory.Register(id, p, fn);
933         }
934
935         bool Unregister(const IdentifierType &id)
936         {
937                 return factory.Unregister(id);
938         }
939
940         /// Return the registered ID in this Factory
941         std::vector<IdentifierType>& RegisteredIds()
942         {
943                 return factory.RegisteredIds();
944         }
945
946         ProductReturn CreateObject(const IdentifierType &id)
947         {
948                 MyKey key(id);
949                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
950                 if(shouldCreateObject(pProduct))
951                 {
952                         pProduct = factory.CreateObject(key.id);
953                         onCreate(pProduct);
954                 }
955                 onFetch(pProduct);
956                 providedObjects[pProduct] = key;
957                 return NP::encapsulate(pProduct);
958         }
959
960         ProductReturn CreateObject(const IdentifierType &id,
961                                    Parm1 p1)
962         {
963                 MyKey key(id,p1);
964                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
965                 if(shouldCreateObject(pProduct))
966                 {
967                         pProduct = factory.CreateObject(key.id,key.p1);
968                         onCreate(pProduct);
969                 }
970                 onFetch(pProduct);
971                 providedObjects[pProduct] = key;
972                 return NP::encapsulate(pProduct);
973         }
974
975         ProductReturn CreateObject(const IdentifierType &id,
976                                    Parm1 p1, Parm2 p2)
977         {
978                 MyKey key(id,p1,p2);
979                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
980                 if(shouldCreateObject(pProduct))
981                 {
982                         pProduct = factory.CreateObject(key.id,key.p1,key.p2);
983                         onCreate(pProduct);
984                 }
985                 onFetch(pProduct);
986                 providedObjects[pProduct] = key;
987                 return NP::encapsulate(pProduct);
988         }
989
990         ProductReturn CreateObject(const IdentifierType &id,
991                                    Parm1 p1, Parm2 p2, Parm3 p3)
992         {
993                 MyKey key(id,p1,p2,p3);
994                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
995                 if(shouldCreateObject(pProduct))
996                 {
997                         pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3);
998                         onCreate(pProduct);
999                 }
1000                 onFetch(pProduct);
1001                 providedObjects[pProduct] = key;
1002                 return NP::encapsulate(pProduct);
1003         }
1004
1005         ProductReturn CreateObject(const IdentifierType &id,
1006                                    Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4)
1007         {
1008                 MyKey key(id,p1,p2,p3,p4);
1009                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
1010                 if(shouldCreateObject(pProduct))
1011                 {
1012                         pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
1013                                                         ,key.p4);
1014                         onCreate(pProduct);
1015                 }
1016                 onFetch(pProduct);
1017                 providedObjects[pProduct] = key;
1018                 return NP::encapsulate(pProduct);
1019         }
1020
1021         ProductReturn CreateObject(const IdentifierType &id,
1022                                    Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5)
1023         {
1024                 MyKey key(id,p1,p2,p3,p4,p5);
1025                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
1026                 if(shouldCreateObject(pProduct))
1027                 {
1028                         pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
1029                                                         ,key.p4,key.p5);
1030                         onCreate(pProduct);
1031                 }
1032                 onFetch(pProduct);
1033                 providedObjects[pProduct] = key;
1034                 return NP::encapsulate(pProduct);
1035         }
1036
1037         ProductReturn CreateObject(const IdentifierType &id,
1038                                    Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5,
1039                                    Parm6 p6)
1040         {
1041                 MyKey key(id,p1,p2,p3,p4,p5,p6);
1042                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
1043                 if(shouldCreateObject(pProduct))
1044                 {
1045                         pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
1046                                                         ,key.p4,key.p5,key.p6);
1047                         onCreate(pProduct);
1048                 }
1049                 onFetch(pProduct);
1050                 providedObjects[pProduct] = key;
1051                 return NP::encapsulate(pProduct);
1052         }
1053
1054         ProductReturn CreateObject(const IdentifierType &id,
1055                                    Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5,
1056                                    Parm6 p6, Parm7 p7 )
1057         {
1058                 MyKey key(id,p1,p2,p3,p4,p5,p6,p7);
1059                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
1060                 if(shouldCreateObject(pProduct))
1061                 {
1062                         pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
1063                                                         ,key.p4,key.p5,key.p6,key.p7);
1064                         onCreate(pProduct);
1065                 }
1066                 onFetch(pProduct);
1067                 providedObjects[pProduct] = key;
1068                 return NP::encapsulate(pProduct);
1069         }
1070
1071         ProductReturn CreateObject(const IdentifierType &id,
1072                                    Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5,
1073                                    Parm6 p6, Parm7 p7, Parm8 p8)
1074         {
1075                 MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8);
1076                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
1077                 if(shouldCreateObject(pProduct))
1078                 {
1079                         pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
1080                                                         ,key.p4,key.p5,key.p6,key.p7,key.p8);
1081                         onCreate(pProduct);
1082                 }
1083                 onFetch(pProduct);
1084                 providedObjects[pProduct] = key;
1085                 return NP::encapsulate(pProduct);
1086         }
1087
1088         ProductReturn CreateObject(const IdentifierType &id,
1089                                    Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5,
1090                                    Parm6 p6, Parm7 p7, Parm8 p8, Parm9 p9)
1091         {
1092                 MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8,p9);
1093                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
1094                 if(shouldCreateObject(pProduct))
1095                 {
1096                         pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
1097                                                         ,key.p4,key.p5,key.p6,key.p7,key.p8,key.p9);
1098                         onCreate(pProduct);
1099                 }
1100                 onFetch(pProduct);
1101                 providedObjects[pProduct] = key;
1102                 return NP::encapsulate(pProduct);
1103         }
1104
1105         ProductReturn CreateObject(const IdentifierType &id,
1106                                    Parm1 p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5 p5,
1107                                    Parm6 p6, Parm7 p7, Parm8 p8, Parm9 p9,Parm10 p10)
1108         {
1109                 MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10);
1110                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
1111                 if(shouldCreateObject(pProduct))
1112                 {
1113                         pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
1114                                                         ,key.p4,key.p5,key.p6,key.p7,key.p8,key.p9,key.p10);
1115                         onCreate(pProduct);
1116                 }
1117                 onFetch(pProduct);
1118                 providedObjects[pProduct] = key;
1119                 return NP::encapsulate(pProduct);
1120         }
1121
1122         ProductReturn CreateObject(const IdentifierType &id,
1123                                    Parm1  p1, Parm2 p2, Parm3 p3, Parm4 p4, Parm5  p5,
1124                                    Parm6  p6, Parm7 p7, Parm8 p8, Parm9 p9, Parm10 p10,
1125                                    Parm11 p11)
1126         {
1127                 MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11);
1128                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
1129                 if(shouldCreateObject(pProduct))
1130                 {
1131                         pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
1132                                                         ,key.p4,key.p5,key.p6,key.p7,key.p8,key.p9,key.p10,key.p11);
1133                         onCreate(pProduct);
1134                 }
1135                 onFetch(pProduct);
1136                 providedObjects[pProduct] = key;
1137                 return NP::encapsulate(pProduct);
1138         }
1139
1140         ProductReturn CreateObject(const IdentifierType &id,
1141                                    Parm1  p1,  Parm2  p2, Parm3 p3, Parm4 p4, Parm5  p5,
1142                                    Parm6  p6,  Parm7  p7, Parm8 p8, Parm9 p9, Parm10 p10,
1143                                    Parm11 p11, Parm12 p12)
1144         {
1145                 MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12);
1146                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
1147                 if(shouldCreateObject(pProduct))
1148                 {
1149                         pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
1150                                                         ,key.p4,key.p5,key.p6,key.p7,key.p8,key.p9,key.p10,key.p11,key.p12);
1151                         onCreate(pProduct);
1152                 }
1153                 onFetch(pProduct);
1154                 providedObjects[pProduct] = key;
1155                 return NP::encapsulate(pProduct);
1156         }
1157
1158         ProductReturn CreateObject(const IdentifierType &id,
1159                                    Parm1  p1,  Parm2  p2,  Parm3  p3, Parm4 p4, Parm5  p5,
1160                                    Parm6  p6,  Parm7  p7,  Parm8  p8, Parm9 p9, Parm10 p10,
1161                                    Parm11 p11, Parm12 p12, Parm13 p13)
1162         {
1163                 MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13);
1164                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
1165                 if(shouldCreateObject(pProduct))
1166                 {
1167                         pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
1168                                                         ,key.p4,key.p5,key.p6,key.p7,key.p8,key.p9,key.p10,key.p11,key.p12
1169                                                         ,key.p13);
1170                         onCreate(pProduct);
1171                 }
1172                 onFetch(pProduct);
1173                 providedObjects[pProduct] = key;
1174                 return NP::encapsulate(pProduct);
1175         }
1176
1177         ProductReturn CreateObject(const IdentifierType &id,
1178                                    Parm1  p1,  Parm2  p2,  Parm3  p3,  Parm4  p4, Parm5  p5,
1179                                    Parm6  p6,  Parm7  p7,  Parm8  p8,  Parm9  p9, Parm10 p10,
1180                                    Parm11 p11, Parm12 p12, Parm13 p13, Parm14 p14)
1181         {
1182                 MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14);
1183                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
1184                 if(shouldCreateObject(pProduct))
1185                 {
1186                         pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
1187                                                         ,key.p4,key.p5,key.p6,key.p7,key.p8,key.p9,key.p10,key.p11,key.p12
1188                                                         ,key.p13,key.p14);
1189                         onCreate(pProduct);
1190                 }
1191                 onFetch(pProduct);
1192                 providedObjects[pProduct] = key;
1193                 return NP::encapsulate(pProduct);
1194         }
1195
1196         ProductReturn CreateObject(const IdentifierType &id,
1197                                    Parm1  p1,  Parm2  p2,  Parm3  p3,  Parm4  p4,  Parm5  p5,
1198                                    Parm6  p6,  Parm7  p7,  Parm8  p8,  Parm9  p9,  Parm10 p10,
1199                                    Parm11 p11, Parm12 p12, Parm13 p13, Parm14 p14, Parm15 p15)
1200         {
1201                 MyKey key(id,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15);
1202                 AbstractProduct *pProduct(getPointerToObjectInContainer(getContainerFromKey(key)));
1203                 if(shouldCreateObject(pProduct))
1204                 {
1205                         pProduct = factory.CreateObject(key.id,key.p1,key.p2,key.p3
1206                                                         ,key.p4,key.p5,key.p6,key.p7,key.p8,key.p9,key.p10,key.p11,key.p12
1207                                                         ,key.p13,key.p14,key.p15);
1208                         onCreate(pProduct);
1209                 }
1210                 onFetch(pProduct);
1211                 providedObjects[pProduct] = key;
1212                 return NP::encapsulate(pProduct);
1213         }
1214
1215         /// Use this function to release the object
1216         /**
1217          * if execution brakes in this function then you tried
1218          * to release an object that wasn't provided by this Cache
1219          * ... which is bad :-)
1220          */
1221         void ReleaseObject(ProductReturn &object)
1222         {
1223                 AbstractProduct *pProduct(NP::release(object));
1224                 typename FetchedObjToKeyMap::iterator itr = providedObjects.find(pProduct);
1225                 if(itr == providedObjects.end())
1226                         throw CacheException();
1227                 onRelease(pProduct);
1228                 ReleaseObjectFromContainer(getContainerFromKey((*itr).second), pProduct);
1229                 providedObjects.erase(itr);
1230         }
1231
1232         /// display the cache configuration
1233         void displayCacheType()
1234         {
1235                 using namespace std;
1236                 cout << "############################" << endl;
1237                 cout << "## Cache configuration" << endl;
1238                 cout << "## + Encapsulation " << NP::name() << endl;
1239                 cout << "## + Creating      " << CP::name() << endl;
1240                 cout << "## + Eviction      " << EP::name() << endl;
1241                 cout << "## + Statistics    " << SP::name() << endl;
1242                 cout << "############################" << endl;
1243         }
1244 };
1245 } // namespace Loki
1246
1247 #endif // end file guardian
1248