Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

InstanceTracker.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_InstanceTracker_h_
00003 #define INCLUDED_InstanceTracker_h_
00004 
00005 #include "plistCollections.h"
00006 #include "FamilyFactory.h"
00007 #include <string>
00008 
00009 //! Attempts to provide references to all currently instantiated objects of a class (and its subclasses)
00010 /*! The classes being tracked need to register themselves via registerInstance() -- this isn't magic.
00011  *  The easiest way to do this is simply to have the base FamilyT's constructor do the registration,
00012  *  and then all of the subclasses will get it automatically.  This only works however, on the assumption
00013  *  that nothing is going to try to immediately access the newly registered entry while subclass
00014  *  construction is still in progress. */
00015 template<class FamilyT, typename ClassNameT=std::string, class FactoryBaseT=Factory1Arg<FamilyT,std::string>, template<class U> class FactoryT=FactoryBaseT::template Factory>
00016 class InstanceTracker : public plist::Dictionary, protected FamilyFactory<FamilyT,ClassNameT,FactoryBaseT,FactoryT> {
00017 public:
00018   //! shorthand for iterators to be returned
00019   typedef plist::Dictionary::iterator iterator;
00020   //! shorthand for iterators to be returned
00021   typedef plist::Dictionary::const_iterator const_iterator;
00022   
00023   //! allows indirect access to the product family type
00024   typedef FamilyT FamilyType;
00025   //! allows indirect access to the product class name type
00026   typedef ClassNameT ClassNameType;
00027   //! allows indirect access to the factory base class
00028   typedef FactoryBaseT FactoryBaseType;
00029   //! allows indirect access to create factory classes
00030   template<class T> struct FactoryType : FactoryT<T> {};
00031   
00032   //! constructor
00033   InstanceTracker() : plist::Dictionary(), FamilyFactory<FamilyT,ClassNameT,FactoryBaseT,FactoryT>() { setLoadSavePolicy(FIXED,SYNC); }
00034   
00035   using FamilyFactory<FamilyT,ClassNameT,FactoryBaseT,FactoryT>::getTypeNames;
00036   using FamilyFactory<FamilyT,ClassNameT,FactoryBaseT,FactoryT>::getNumTypes;
00037   using FamilyFactory<FamilyT,ClassNameT,FactoryBaseT,FactoryT>::registerType;
00038   using FamilyFactory<FamilyT,ClassNameT,FactoryBaseT,FactoryT>::registerFactory;
00039   
00040   //! Register an existing instance via reference, does @em not assume responsibility for memory deallocation, returns false if @a name is already in use.
00041   template<class T> bool registerInstance(const std::string& name, T& inst);
00042   //! Register an existing instance via pointer, @em does assume responsibility for memory deallocation, returns false if @a name is already in use.
00043   template<class T> bool registerInstance(const std::string& name, T* inst);
00044   //! Register an existing instance via reference, does @em not assume responsibility for memory deallocation, returns false if @a name is already in use.
00045   /*! If @a type is not an empty string, and @a inst is a plist::Dictionary, a '.type' entry will be added to store
00046     *  the type.  This allows polymorphic loading.   Non-dictionaries will be wrapped in a new plist::Dictionary with a '.type'. */
00047   template<class T> bool registerInstance(const ClassNameT& type, const std::string& name, T& inst);
00048   //! Register an existing instance via pointer, @em does assume responsibility for memory deallocation, returns false if @a name is already in use.
00049   /*! If @a type is not an empty string, and @a inst is a plist::Dictionary, a '.type' entry will be added to store
00050    *  the type.  This allows polymorphic loading.   Non-dictionaries will be wrapped in a new plist::Dictionary with a '.type'. */
00051   template<class T> bool registerInstance(const ClassNameT& type, const std::string& name, T* inst);
00052   
00053   //! create and register an instance in one call
00054   FamilyT* create(const ClassNameType& type, const std::string& name);
00055   //! looks up a previously registered instance, returning NULL if not found
00056   FamilyT* getInstance(const std::string& name );
00057   //! Removes an instance from the tracker.
00058   /*! If the tracker was managing the memory allocation (via create() or the registerInstance() functions
00059     *  which take a pointer instead of a reference), then this will cause the instance to be deallocated. */
00060   bool destroy(const std::string& name);
00061   
00062   void loadXML(xmlNode* node);
00063 
00064 protected:
00065   //! Provides a wrapper for non-plist::Dictionary entries.
00066   /*! This allows InstanceTracker to use plist::Dictionary as a superclass to manage the tracked instances,
00067     *  as well as polymorphically load and save the entire set of instances.  Which is why we use a dictionary
00068     *  here instead of a simple plist::ObjectBase so we can handle a '.type' entry to store the instance's class type. */
00069   class InstanceEntry : public plist::Dictionary {
00070   public:
00071     //! constructor, assigns @a inst to both #instance and #alloc (thus marking the instance for deallocation on InstanceEntry destruction)
00072     explicit InstanceEntry(FamilyT* inst) : plist::Dictionary(), alloc(inst), instance(*inst) {}
00073     //! constructor, assigns @a inst to #instance only, leaving #alloc NULL (thus leaving deallocation of instance to the caller)
00074     explicit InstanceEntry(FamilyT& inst) : plist::Dictionary(), alloc(NULL), instance(inst) {}
00075     //! destructor, deallocates #alloc (which might be NULL, and thus a no-op)
00076     ~InstanceEntry() { delete alloc; }
00077     
00078     FamilyT* const alloc; //!< if the original instance was passed as a pointer, we will store that here for easy deallocation on destruction
00079     FamilyT& instance; //!< reference to the actual instance
00080     
00081   private:
00082     InstanceEntry(const InstanceEntry&); //!< copy constructor, don't call
00083     InstanceEntry& operator=(const InstanceEntry&); //!< assignment, don't call
00084   };
00085   
00086 private:
00087   InstanceTracker(const InstanceTracker&); //!< copy constructor, don't call
00088   InstanceTracker& operator=(const InstanceTracker&); //!< assignment, don't call
00089 };
00090 
00091 template<class FamilyT, typename ClassNameT, class FactoryBaseT, template<class U> class FactoryT>
00092 template<class T>
00093 bool InstanceTracker<FamilyT,ClassNameT,FactoryBaseT,FactoryT>::registerInstance(const std::string& name, T& inst) {
00094   if(findEntry(name)!=end())
00095     return false; // already have instance by that name
00096   plist::Dictionary* dinst=dynamic_cast<plist::Dictionary*>(&inst);
00097   if(dinst!=NULL) {
00098     addEntry(name,*dinst); // note adding as reference so we don't try to delete the reference we were given
00099   } else {
00100     dinst=new InstanceEntry(inst); // create a wrapper for the non-Dictionary type
00101     addEntry(name,dinst); // adding as pointer since we want to delete our wrapper
00102   }
00103   return true;
00104 }
00105 template<class FamilyT, typename ClassNameT, class FactoryBaseT, template<class U> class FactoryT>
00106 template<class T>
00107 bool InstanceTracker<FamilyT,ClassNameT,FactoryBaseT,FactoryT>::registerInstance(const std::string& name, T* inst) {
00108   if(findEntry(name)!=end())
00109     return false; // already have instance by that name
00110   plist::Dictionary* dinst=dynamic_cast<plist::Dictionary*>(inst);
00111   if(dinst==NULL)
00112     dinst=new InstanceEntry(inst); // create a wrapper for the non-Dictionary type
00113   addEntry(name,dinst);
00114   return true;
00115 }
00116 template<class FamilyT, typename ClassNameT, class FactoryBaseT, template<class U> class FactoryT>
00117 template<class T>
00118 bool InstanceTracker<FamilyT,ClassNameT,FactoryBaseT,FactoryT>::registerInstance(const ClassNameT& type, const std::string& name, T& inst) {
00119   if(findEntry(name)!=end())
00120     return false; // already have instance by that name
00121   plist::Dictionary* dinst=dynamic_cast<plist::Dictionary*>(&inst);
00122   if(dinst!=NULL) {
00123     if(type.size()>0)
00124       dinst->addEntry(".type",new plist::Primitive<ClassNameT>(type),"Stores the typename of the class so it can be re-instantiated on load.\n** Do not edit ** ");
00125     addEntry(name,*dinst); // note adding as reference so we don't try to delete the reference we were given
00126   } else {
00127     dinst=new InstanceEntry(inst); // create a wrapper for the non-Dictionary type
00128     if(type.size()>0)
00129       dinst->addEntry(".type",new plist::Primitive<ClassNameT>(type),"Stores the typename of the class so it can be re-instantiated on load.\n** Do not edit ** ");
00130     addEntry(name,dinst); // adding as pointer since we want to delete our wrapper
00131   }
00132   return true;
00133 }
00134 template<class FamilyT, typename ClassNameT, class FactoryBaseT, template<class U> class FactoryT>
00135 template<class T>
00136 bool InstanceTracker<FamilyT,ClassNameT,FactoryBaseT,FactoryT>::registerInstance(const ClassNameT& type, const std::string& name, T* inst) {
00137   if(findEntry(name)!=end())
00138     return false; // already have instance by that name
00139   plist::Dictionary* dinst=dynamic_cast<plist::Dictionary*>(inst);
00140   if(dinst==NULL)
00141     dinst=new InstanceEntry(inst); // create a wrapper for the non-Dictionary type
00142   if(type.size()>0)
00143     dinst->addEntry(".type",new plist::Primitive<ClassNameT>(type),"Stores the typename of the class so it can be re-instantiated on load.\n** Do not edit ** ");
00144   addEntry(name,dinst);
00145   return true;
00146 }
00147 
00148 template<class FamilyT, typename ClassNameT, class FactoryBaseT, template<class U> class FactoryT>
00149 FamilyT* InstanceTracker<FamilyT,ClassNameT,FactoryBaseT,FactoryT>::create(const ClassNameT& type, const std::string& name) {
00150   if(findEntry(name)!=end())
00151     return NULL; // already have instance by that name
00152   FamilyT* inst=FamilyFactory<FamilyT,ClassNameT,FactoryBaseT,FactoryT>::create(type,name);
00153   if(inst==NULL)
00154     return NULL; // apparently type isn't valid
00155   registerInstance(type,name,inst); // if it fails, means instance registered itself from constructor, that's fine
00156   return inst;
00157 }
00158 
00159 template<class FamilyT, typename ClassNameT, class FactoryBaseT, template<class U> class FactoryT>
00160 FamilyT* InstanceTracker<FamilyT,ClassNameT,FactoryBaseT,FactoryT>::getInstance(const std::string& name) {
00161   const_iterator it=findEntry(name);
00162   if(it==end())
00163     return NULL; // doesn't exit
00164   if(FamilyT* inst = dynamic_cast<FamilyT*>(it->second))
00165     return inst; // usually this is our instance right here
00166   // otherwise, was a wrapper because FamilyT isn't a Dictionary, extract actual instance
00167   if(InstanceEntry* inst = dynamic_cast<InstanceEntry*>(it->second))
00168     return &inst->instance;
00169   if(it->second==NULL)
00170     std::cerr << "InstanceTracker had NULL entry for " << name << std::endl;
00171   else
00172     std::cerr << "InstanceTracker had entry of unknown type for " << name << std::endl;
00173   return NULL;
00174 }
00175 
00176 template<class FamilyT, typename ClassNameT, class FactoryBaseT, template<class U> class FactoryT>
00177 bool InstanceTracker<FamilyT,ClassNameT,FactoryBaseT,FactoryT>::destroy(const std::string& name) {
00178   const_iterator it=findEntry(name);
00179   if(it==end())
00180     return false;
00181   removeEntry(name);
00182   return true;
00183 }   
00184 
00185 //! Pre-loads dictionaries for InstanceTracker::loadXML to extract ".type" entries so matching instances can be created for loading
00186 struct InstanceTypeLoader : public virtual plist::Dictionary {
00187   //! constructor
00188   InstanceTypeLoader() : plist::Dictionary(), type() {
00189     addEntry(".type",type);
00190     setLoadSavePolicy(FIXED,SYNC);
00191     setUnusedWarning(false);
00192   }
00193   //! type field indicates name of class to be created
00194   plist::Primitive<std::string> type;
00195 };
00196 
00197 template<class FamilyT, typename ClassNameT, class FactoryBaseT, template<class U> class FactoryT>
00198 void InstanceTracker<FamilyT,ClassNameT,FactoryBaseT,FactoryT>::loadXML(xmlNode* node) {
00199   // first load the dictionary as a set of generic sub-dictionaries so we can pull out the type fields
00200   plist::DictionaryOf<InstanceTypeLoader> d;
00201   d.loadXML(node);
00202   for(plist::DictionaryOf<InstanceTypeLoader>::const_iterator it=d.begin(); it!=d.end(); ++it) {
00203     InstanceTypeLoader* dd = it->second;
00204     if(dd->type.size()==0) {
00205       std::cerr << "ERROR: could not instantiate driver named '" << it->first << "'; no '.type' field found!" << std::endl;
00206     } else {
00207       plist::Dictionary::const_iterator drobit = findEntry(it->first);
00208       plist::ObjectBase * drob = (drobit==end()) ? NULL : drobit->second;
00209       if(plist::Dictionary* dr = dynamic_cast<plist::Dictionary*>(drob)) {
00210         plist::Dictionary::const_iterator tyobit = dr->findEntry(".type");
00211         if(tyobit==dr->end())
00212           std::cerr << "WARNING: Driver dictionary lacking .type field for '" << it->first << "'" << std::endl;
00213         else if(tyobit->second->toString()==dd->type)
00214           continue; // name and type already match, don't need to recreate
00215         destroy(it->first); // otherwise clear the old instance and fall through to allocate a new instance
00216         dr=NULL;
00217       }
00218       if(create(dd->type,it->first)==NULL)
00219         std::cerr << "ERROR: failed to instantiate driver named '" << it->first << "' with type '" << dd->type << "'" << std::endl;
00220     }
00221   }
00222   // now that we've created basic instances, do the "real" loading to give them settings
00223   plist::Dictionary::loadXML(node);
00224 }
00225 
00226 /*! @file
00227  * @brief 
00228  * @author Ethan Tira-Thompson (ejt) (Creator)
00229  */
00230 
00231 #endif

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:42 2016 by Doxygen 1.6.3