00001 /** @file rutz/factory.h template class for object factories, which 00002 create objects belonging to some inheritance hierarchy given just 00003 a type name as a string */ 00004 00005 /////////////////////////////////////////////////////////////////////// 00006 // 00007 // Copyright (c) 1999-2004 California Institute of Technology 00008 // Copyright (c) 2004-2007 University of Southern California 00009 // Rob Peters <rjpeters at usc dot edu> 00010 // 00011 // created: Sat Jun 26 23:40:55 1999 00012 // commit: $Id: factory.h 8274 2007-04-19 17:44:48Z rjpeters $ 00013 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/rutz/factory.h $ 00014 // 00015 // -------------------------------------------------------------------- 00016 // 00017 // This file is part of GroovX. 00018 // [http://ilab.usc.edu/rjpeters/groovx/] 00019 // 00020 // GroovX is free software; you can redistribute it and/or modify it 00021 // under the terms of the GNU General Public License as published by 00022 // the Free Software Foundation; either version 2 of the License, or 00023 // (at your option) any later version. 00024 // 00025 // GroovX is distributed in the hope that it will be useful, but 00026 // WITHOUT ANY WARRANTY; without even the implied warranty of 00027 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00028 // General Public License for more details. 00029 // 00030 // You should have received a copy of the GNU General Public License 00031 // along with GroovX; if not, write to the Free Software Foundation, 00032 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 00033 // 00034 /////////////////////////////////////////////////////////////////////// 00035 00036 #ifndef GROOVX_RUTZ_FACTORY_H_UTC20050626084020_DEFINED 00037 #define GROOVX_RUTZ_FACTORY_H_UTC20050626084020_DEFINED 00038 00039 #include "rutz/assocarray.h" 00040 #include "rutz/demangle.h" 00041 #include "rutz/fileposition.h" 00042 #include "rutz/mutex.h" 00043 #include "rutz/shared_ptr.h" 00044 #include "rutz/traits.h" 00045 00046 #include <typeinfo> 00047 00048 namespace rutz 00049 { 00050 class fstring; 00051 00052 /// Abstract interface for creating objects of a particular type. 00053 /** rutz::creator_base is a template class that defines a single 00054 abstract function, create(), that returns an object of type 00055 creator_base::base_t. */ 00056 template <class T> 00057 class creator_base 00058 { 00059 public: 00060 typedef T base_t; 00061 00062 /// Virtual destructor. 00063 virtual ~creator_base() {} 00064 00065 /// Return a clone of this Creator 00066 virtual creator_base* clone() const = 0; 00067 00068 /// Return a new pointer (or smart pointer) to type \c Base. 00069 virtual base_t create() = 0; 00070 }; 00071 00072 00073 /// Implements rutz::creator_base by calling a function pointer. 00074 /** rutz::creator_from_func implements the rutz::creator_base 00075 interface by storing a pointer to function that returns an 00076 object of the appropriate type. */ 00077 template <class base_t, class derived_t> 00078 class creator_from_func : public rutz::creator_base<base_t> 00079 { 00080 public: 00081 /// Creator function type. 00082 typedef derived_t (*creator_func_t) (); 00083 00084 /// Construct with a creator function. 00085 creator_from_func(creator_func_t func) : 00086 rutz::creator_base<base_t>(), m_creator_func(func) {} 00087 00088 /// Clone operator. 00089 virtual rutz::creator_base<base_t>* clone() const 00090 { return new creator_from_func<base_t, derived_t>(*this); } 00091 00092 /// Create an object using the creator function. 00093 virtual base_t create() { return base_t(m_creator_func()); } 00094 00095 private: 00096 creator_func_t m_creator_func; 00097 }; 00098 00099 00100 /// Abstract class for a fallback strategy when factory lookup fails. 00101 class factory_fallback 00102 { 00103 public: 00104 /// Default constructor. 00105 factory_fallback() throw(); 00106 00107 /// Virtual no-throw destructor for proper inheritance. 00108 virtual ~factory_fallback() throw(); 00109 00110 /// This will be called with the key whose lookup failed in the factory. 00111 virtual void try_fallback(const rutz::fstring& key) const = 0; 00112 }; 00113 00114 /// Non-template helper class for rutz::factory. 00115 class factory_base 00116 { 00117 public: 00118 typedef void (fallback_t)(const rutz::fstring&); 00119 00120 /// Default constructor sets the fallback to null. 00121 factory_base() throw(); 00122 00123 /// Virtual no-throw destructor for proper inheritance. 00124 virtual ~factory_base() throw(); 00125 00126 /// Change the fallback object. 00127 void set_fallback(rutz::shared_ptr<factory_fallback> f); 00128 00129 /// Change the fallback function. 00130 void set_fallback(fallback_t* fptr); 00131 00132 /// Try running the fallback function for the given key. 00133 void try_fallback(const rutz::fstring& key) const; 00134 00135 private: 00136 rutz::shared_ptr<factory_fallback> m_fallback; 00137 }; 00138 00139 00140 /// Create objects base on 'key' strings. 00141 /** rutz::factory can create objects of different types associated 00142 with different 'key' strings. rutz::factory maintains a mapping 00143 from key strings to rutz::creator_base's, and so given a key can 00144 call the create() function of the associated creator_base. All 00145 of the product types of a factory must be derived from 00146 factory::base_t. The recommended usage is for factories for 00147 specific types to be implemented as singleton classes derived 00148 from rutz::factory. 00149 00150 rutz::factory uses an internal mutex so that all of its member 00151 functions can safely be called in a multithreaded program 00152 without external locking. 00153 */ 00154 template <class T> 00155 class factory 00156 { 00157 public: 00158 // typedefs 00159 00160 typedef factory_base::fallback_t fallback_t; 00161 typedef T base_t; 00162 00163 private: 00164 rutz::factory_base m_base; 00165 rutz::assoc_array<rutz::creator_base<base_t> > m_map; 00166 mutable pthread_mutex_t m_mutex; 00167 00168 protected: 00169 /// Find a creator for the given key; otherwise return null. 00170 rutz::creator_base<base_t>* 00171 find_creator(const rutz::fstring& key) const 00172 { 00173 rutz::creator_base<base_t>* creator = 0; 00174 00175 { 00176 GVX_MUTEX_LOCK(&m_mutex); 00177 creator = m_map.get_ptr_for_key(key); 00178 } 00179 00180 if (creator == 0) 00181 { 00182 // note that we must call try_fallback() with our mutex 00183 // UN-locked because the intention is that the fallback 00184 // function will try do something to cause a new entry to be 00185 // inserted into this very factory object; that insertion 00186 // will require locking the mutex so if we have the mutex 00187 // locked here we will end up with a deadlock: 00188 m_base.try_fallback(key); 00189 00190 { 00191 GVX_MUTEX_LOCK(&m_mutex); 00192 creator = m_map.get_ptr_for_key(key); 00193 } 00194 } 00195 00196 return creator; 00197 } 00198 00199 public: 00200 /// Default constructor. 00201 /** @param key_descr a human-readable description of what this 00202 factory's keys represent; this is used in error messages, 00203 e.g. if descr is "frobnicator", then error messages would 00204 include "unknown frobnicator" 00205 00206 @param nocase true if the factory should use case-insensitive 00207 string comparisons (default is false, giving normal 00208 case-sensitive string comparisons) 00209 */ 00210 factory(const char* keydescr = "object type", bool nocase = false) 00211 : m_base(), m_map(keydescr, nocase), m_mutex() 00212 { 00213 pthread_mutex_init(&m_mutex, (pthread_mutexattr_t*) 0); 00214 } 00215 00216 /// Virtual no-throw destructor. 00217 virtual ~factory() throw() 00218 { 00219 pthread_mutex_destroy(&m_mutex); 00220 } 00221 00222 /// Registers a creation object with the factory. 00223 /** The function returns the actual key that was paired with the 00224 creation function.*/ 00225 const char* register_creator(rutz::creator_base<base_t>* creator, 00226 const char* name) 00227 { 00228 GVX_MUTEX_LOCK(&m_mutex); 00229 00230 m_map.set_ptr_for_key(name, creator); 00231 00232 return name; 00233 } 00234 00235 /// Registers a creation function with the factory. 00236 /** The default key associated with the creation function will be 00237 given by the type's actual C++ name. The function returns the 00238 actual key that was paired with the creation function.*/ 00239 template <class derived_t> 00240 const char* register_creator(derived_t (*func) (), 00241 const char* key = 0) 00242 { 00243 GVX_MUTEX_LOCK(&m_mutex); 00244 00245 if (key == 0) 00246 key = rutz::demangled_name 00247 (typeid(typename rutz::type_traits<derived_t>::pointee_t)); 00248 00249 m_map.set_ptr_for_key 00250 (key, new rutz::creator_from_func<base_t, derived_t>(func)); 00251 00252 return key; 00253 } 00254 00255 /// Introduces an alternate key for an existing key. 00256 /** There must already have been a creation function registered 00257 for the original key. */ 00258 void register_alias(const char* orig_key, const char* alias_key) 00259 { 00260 GVX_MUTEX_LOCK(&m_mutex); 00261 00262 rutz::creator_base<base_t>* creator = 00263 m_map.get_ptr_for_key(orig_key); 00264 00265 if (creator != 0) 00266 { 00267 m_map.set_ptr_for_key(alias_key, creator->clone()); 00268 } 00269 } 00270 00271 /// Query whether a given key is a valid, known key in the factory. 00272 bool is_valid_key(const char* key) const 00273 { 00274 return (this->find_creator(key) != 0); 00275 } 00276 00277 /// Get a list of known keys, separated by sep. 00278 rutz::fstring get_known_keys(const char* sep) const 00279 { 00280 GVX_MUTEX_LOCK(&m_mutex); 00281 00282 return m_map.get_known_keys(sep); 00283 } 00284 00285 /// Returns a new object of a given type. 00286 /** If the given type has not been registered with the factory, a 00287 null pointer is returned. */ 00288 base_t new_object(const rutz::fstring& type) const 00289 { 00290 rutz::creator_base<base_t>* creator = this->find_creator(type); 00291 00292 if (creator == 0) return base_t(); 00293 00294 return creator->create(); 00295 } 00296 00297 /// Returns a new object of a given type. 00298 /** If the given type has not been registered with the factory, an 00299 exception is thrown. */ 00300 base_t new_checked_object(const rutz::fstring& type) const 00301 { 00302 rutz::creator_base<base_t>* creator = this->find_creator(type); 00303 00304 if (creator == 0) 00305 { 00306 GVX_MUTEX_LOCK(&m_mutex); 00307 00308 m_map.throw_for_key(type, SRC_POS); 00309 } 00310 00311 return creator->create(); 00312 } 00313 00314 /// Change the fallback object. 00315 void set_fallback(rutz::shared_ptr<factory_fallback> f) 00316 { 00317 GVX_MUTEX_LOCK(&m_mutex); 00318 00319 m_base.set_fallback(f); 00320 } 00321 00322 /// Change the fallback function. 00323 void set_fallback(fallback_t* fptr) 00324 { 00325 GVX_MUTEX_LOCK(&m_mutex); 00326 00327 m_base.set_fallback(fptr); 00328 } 00329 }; 00330 00331 } // end namespace rutz 00332 00333 00334 static const char __attribute__((used)) vcid_groovx_rutz_factory_h_utc20050626084020[] = "$Id: factory.h 8274 2007-04-19 17:44:48Z rjpeters $ $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/rutz/factory.h $"; 00335 #endif // !GROOVX_RUTZ_FACTORY_H_UTC20050626084020_DEFINED