00001 #ifndef DVUTIL_DEBUG_H 00002 #define DVUTIL_DEBUG_H 00003 // $Id: debug.h,v 1.9 2008/03/14 21:44:10 dvermeir Exp $ 00004 00005 #include <dvutil/trace.h> 00006 #include <dvutil/basetype.h> 00007 #include <dvutil/observer.h> 00008 00009 namespace Dv { 00010 /** The following functions support writing to a std::ostream* 00011 * If the pointer is 0 (null), nothing is written. Otherwise, @a operator<< 00012 * behaves as on std::ostream. 00013 * 00014 * This is useful e.g. when one has a 00015 * @code 00016 * std::ostream* log; 00017 * @endcode 00018 * 00019 * variable which may be 0 (if logging is turned off). 00020 * One may then write 00021 * @code 00022 * ostream_ptr(log) << "blabla" << std::endl; 00023 * @endcode 00024 * instead of cluttering with 00025 * @code 00026 * if (log) 00027 * *log << "blabla" << std::endl; 00028 * @endcode 00029 * 00030 * Note that in order to support 00031 * @code 00032 * operator<<(ostream_ptr& osp, const T& t) { 00033 * @endcode 00034 * we need to package std::ostream* into its own class since 00035 * overloaded operators need at least one user defined operand type. 00036 * 00037 * @sa Dv::Debugable 00038 * @sa Dv::DebuSlave 00039 * @sa Dv::Debug 00040 */ 00041 class ostream_ptr { 00042 public: 00043 /** Constructor. 00044 * @param ostrm underlying std::ostream pointer or 0 00045 */ 00046 ostream_ptr(std::ostream* ostrm = 0): os(ostrm) { 00047 } 00048 00049 /** Destructor. */ 00050 ~ostream_ptr() { 00051 } 00052 00053 /** @return true if this is a null Dv::ostream_ptr, i.e. 00054 * the underlying std::ostream pointer is 0 00055 */ 00056 bool null() const { return os; } 00057 00058 /** Update the underlying std::ostream pointer. 00059 * @param ostrm new underlying std::ostream pointer or 0 00060 * @return old underlying std::ostream pointer 00061 */ 00062 std::ostream* 00063 set_os(std::ostream* ostrm) { 00064 std::ostream* old_os = os; 00065 os = ostrm; 00066 return old_os; 00067 } 00068 00069 /* @return the @a std::ostream* underlying this Dv::ostream_ptr */ 00070 std::ostream* 00071 ostrm() const { 00072 return os; 00073 } 00074 00075 /** Update the underlying std::ostream pointer using 00076 * assignment. 00077 * @param ostrm new underlying std::ostream pointer or 0 00078 * @return *this 00079 * @sa Dv::ostream_ptr::set_os 00080 */ 00081 ostream_ptr& operator=(std::ostream* ostrm) { 00082 set_os(ostrm); 00083 return *this; 00084 } 00085 00086 /** Generic writing to an ostream_ptr. If the 00087 * underlying stream is 0, nothing will be written, obviously. 00088 * @param osp ostream_ptr with underlying stream (or 0) to write to 00089 * @param t object to write 00090 * @return osp, which may have been zeroed if the write operation 00091 * failed 00092 */ 00093 template<typename T> 00094 friend ostream_ptr& 00095 operator<<(ostream_ptr& osp, const T& t) { 00096 if (osp.os) 00097 if (*osp.os << t) 00098 return osp; 00099 osp.os = 0; 00100 return osp; 00101 } 00102 00103 /** Specialization for writing a pointer to an ostream_ptr. If the 00104 * underlying stream is 0, nothing will be written, obviously. 00105 * @param osp ostream_ptr with underlying stream (or 0) to write to 00106 * @param t object to write 00107 * @return osp, which may have been zeroed if the write operation 00108 * failed 00109 */ 00110 template<typename T> 00111 friend ostream_ptr& 00112 operator<<(ostream_ptr& osp, const T* t) { 00113 if (osp.os) 00114 if (*osp.os << t) 00115 return osp; 00116 osp.os = 0; 00117 return osp; 00118 } 00119 00120 /** This function supports writing some io manipulators such 00121 * as std::endl or std::setw to an ostream_ptr. 00122 * @param osp stream to write to 00123 * @param pf io manipulator to write 00124 * @return osp, which may have been zeroed if the write operation 00125 * failed 00126 */ 00127 friend ostream_ptr& 00128 operator<<(ostream_ptr& osp, std::ostream& (*pf)(std::ostream&)) { 00129 if (osp.os) 00130 if (*osp.os << pf) 00131 return osp; 00132 osp.os = 0; 00133 return osp; 00134 } 00135 00136 private: 00137 std::ostream* os; 00138 }; 00139 00140 /** An abstract class that provides debugging services. It is not 00141 * really abstract, the interface has a default implementation which 00142 * will be used by slave debug objects that have lost their master. 00143 * 00144 * Other Dv::DebugSlave objects may use a Dv::Debugable as a master, i.e. 00145 * such objects use their master's debug and log facilities, possibly 00146 * modified by an extra threshold so that the slave will obtain 00147 * a non-null Dv::ostream_ptr from Debugable::log(0) only if the 00148 * master's debug level is not less than this threashold. 00149 * The intended usage of this feature is that, e.g. a higher level 00150 * object acts as a master while more lower level objects are 00151 * its slaves. Oftentimes, one wants debug output from the lower level 00152 * objects only in extreme cases, i.e. if the debug level of the 00153 * master is already quite high. 00154 * 00155 * E.g. 00156 * @code 00157 * Collection<Elements> master; 00158 * master.debug(1); 00159 * .. 00160 * class Element: public DebugSlave { 00161 * Element(Collection& c): DebugSlave(3) { 00162 * set_master(&c); 00163 * log() << "blabla"; // only if master.debug() >= 3 00164 * } 00165 * } 00166 * @endcode 00167 * The default implementation provided by Debugable always returns 00168 * a null Dv::ostream_ptr. 00169 */ 00170 class Debugable { 00171 public: 00172 /** Constructor. Sets log_0, which is a null ostream_ptr. */ 00173 Debugable() {} 00174 00175 virtual ~Debugable() {} 00176 00177 /** @return debug level. */ 00178 virtual unsigned int debug() const { return 0; } 00179 00180 /** Return an Dv::ostream_ptr to write debug or log info on. 00181 * The default implementation always returns a null Dv::ostream_ptr 00182 * @param condition a non-null Dv::ostream_ptr will be returned 00183 * only if this condition is true 00184 * (however, the default implementation here always returns a null Dv::ostream_ptr). 00185 * @return a null Dv::ostream_ptr 00186 */ 00187 virtual ostream_ptr& logif(bool condition) const { return const_cast<Debugable*>(this)->log_0; } 00188 /** Return an Dv::ostream_ptr to write debug or log info on. 00189 * The default implementation always returns a null Dv::ostream_ptr 00190 * @param i minimal debug level for getting a non-null Dv::ostream_ptr 00191 * (however, the default implementation here always returns a null Dv::ostream_ptr). 00192 * @return a null Dv::ostream_ptr 00193 * @sa Debugable::logif 00194 */ 00195 virtual ostream_ptr& log(unsigned int i = 0) const { return logif(debug() >= i); } 00196 protected: 00197 ostream_ptr log_0; 00198 }; 00199 00200 /** A DebugSlave uses a link to another master Debugable to 00201 * provide its log and bug services. If there is no master, the slave 00202 * always return a null Dv::ostream_ptr and a debug level of 0. 00203 * 00204 * Note that the liberal use of const_cast. The idea is that adding 00205 * debug manipulation to existing code should not generate const-correctness errors. 00206 * @sa Dv::Debugable 00207 */ 00208 class DebugSlave: public Debugable { 00209 public: 00210 /** Constructor. 00211 * @param minimal_debug_level only if the master's debug level is at least 00212 * this number will the master's Dv::ostream_ptr be returned by 00213 * the Dv::DebugSlave::logif and Dv::DebugSlave::log member functions. 00214 * @param master to which the object will be connected. This can 00215 * also be done later using Dv::DebugSlave::set_debug_master 00216 */ 00217 DebugSlave(unsigned int minimal_debug_level = 0, Debugable* master = 0): 00218 threshold_(minimal_debug_level), master_(master) {} 00219 00220 /** @return the master debug level. */ 00221 virtual unsigned int debug() const { 00222 return (debug_master() ? debug_master()->debug() : Debugable::debug() ); 00223 } 00224 00225 /** Return an Dv::ostream_ptr to write debug or log info on. 00226 * @param condition a non-null Dv::ostream_ptr will be returned 00227 * only if this condition is true and the master debug level is at least 00228 * the treshold specified in the constructor. 00229 * @return the master Dv::ostream_ptr 00230 * if the condition is true and the master debug level is at least 00231 * the treshold specified in the constructor. 00232 * @return a null Dv::ostream_ptr 00233 * if the condition is false or the master debug level is less than 00234 * the treshold specified in the constructor. 00235 */ 00236 virtual ostream_ptr& logif(bool condition) const { 00237 return ( 00238 debug_master() 00239 ? 00240 debug_master()->logif(condition && (debug() >= threshold_)) 00241 : 00242 Debugable::logif(condition && (debug() >= threshold_)) 00243 ); 00244 } 00245 00246 /** Set the debug master of this slave. 00247 * @param master new debug master of this slave. 00248 * @param min_debug_level to use with the new master. If 0, the old threshold remains 00249 * in effect. 00250 */ 00251 virtual void set_debug_master(Debugable* master, unsigned int min_debug_level = 0) const { 00252 const_cast<DebugSlave*>(this)->master_ = master; 00253 if (min_debug_level) 00254 const_cast<DebugSlave*>(this)->threshold_ = min_debug_level; 00255 } 00256 00257 /** @return the minimal debug level of the master for this slave to activate debugging */ 00258 unsigned int debug_threshold() const { return threshold_; } 00259 /** @return the debug master if this slave (or 0 if none). */ 00260 virtual Debugable* debug_master() const { return master_; } 00261 00262 protected: 00263 00264 unsigned int threshold_; 00265 Debugable* master_; 00266 }; 00267 00268 /** An ObservableDebugSlave keeps an eye on its collection of slaves. 00269 * That is, if the master disappears, the slaves's master will become 0. 00270 */ 00271 class ObservableDebugSlave: public DebugSlave, public Observable { 00272 }; 00273 00274 /** A Debug object is typically a master. It has an actual 00275 * debug level and Dv::ostream_ptr for debug and log output. 00276 * 00277 * However, it can also be made to act as a slave. 00278 * The latter is accomplished by using DebugSlave::set_debug_master. 00279 */ 00280 class Debug: public ObservableDebugSlave { 00281 public: 00282 /** Constructor. 00283 * @param log initial (pointer to) std::ostream that will be used as log stream 00284 * @param dbg initial debug level 00285 */ 00286 Debug(std::ostream* log = 0, int dbg = 0): debug_(dbg), log_1(log) { 00287 } 00288 00289 /** Set the debug level. 00290 * @param dbg the new debug level. 00291 * @return the old debug level 00292 */ 00293 unsigned int set_debug(unsigned int dbg) { 00294 unsigned int old_debug = debug_; 00295 debug_ = dbg; 00296 return old_debug; 00297 } 00298 00299 /** Set some bits in the current debug flag. 00300 * @param dbg bit pattern to add 00301 * @return the old debug level 00302 */ 00303 unsigned int add_debug(unsigned int dbg) { 00304 return set_debug(debug() | dbg); 00305 } 00306 00307 /** Clear bits in the current debug flag. 00308 * @param dbg bit pattern to add 00309 * @return the old debug level 00310 */ 00311 unsigned int del_debug(unsigned int dbg) { 00312 return set_debug(debug() & ~dbg); 00313 } 00314 00315 /** @return the current debug level. */ 00316 unsigned int debug() const { return debug_; } 00317 00318 /** Set a log stream. 00319 * @param log the new log stream (or 0) 00320 * @return the old log stream 00321 */ 00322 std::ostream* set_log(std::ostream* log) { return log_1.set_os(log); } 00323 00324 /** Retrieve log file, depending on a condition. 00325 * This allows one to write e.g. 00326 * @code 00327 * x.log(debug() & THIS_MODULE_DEBUG) << "blabla" 00328 * @endcode 00329 * which will actually write only if there is a non-zero log stream and 00330 * the current debug level has the appropriate THIS_MODULE_DEBUG bits set. 00331 * @param condition only return valid log stream if @a condition is true 00332 * @return the log stream as returned by log() but only 00333 * if the condition is true. Otherwise, a zero ostream_ptr is returned. 00334 */ 00335 ostream_ptr& logif(bool condition) const { 00336 return ( 00337 debug_master() 00338 ? 00339 debug_master()->logif(condition && (debug_master()->debug() >= threshold_)) 00340 : 00341 (condition ? const_cast<Debug*>(this)->log_1 : const_cast<Debug*>(this)->log_0) 00342 ); 00343 } 00344 00345 private: 00346 unsigned int debug_; 00347 Dv::ostream_ptr log_1; 00348 }; 00349 00350 00351 /** A Dv::DebugObserver is a Dv::DebugSlave that implements the link 00352 * to its master through the observer-observable link. 00353 * In addition, a Dv::DebugObserver is safe w.r.t. events such 00354 * as the destruction of the master etc. 00355 * The master is provided through a (restricted) observer pattern. 00356 * Note that this makes a Dv::DebugSlave an Dv::Observable (because Dv::Debugable) 00357 * and an Dv::Observer<Debugable>. 00358 */ 00359 class DebugObserver: public ObservableDebugSlave, public Observer<ObservableDebugSlave> { 00360 public: 00361 /** This is used for slaves that directly link to 00362 * a Debug object. 00363 * @param dbg Debug object providing services for this slave 00364 */ 00365 DebugObserver(Debug& dbg): Dv::Observer<ObservableDebugSlave>(&dbg) {} 00366 /** This looks like a copy constructor but it is not. 00367 * It initializes a slave to use another slave as the 00368 * source for its services. 00369 * @param dbg dbg slave to use as master for this object 00370 */ 00371 DebugObserver(DebugObserver& dbg): Dv::Observer<ObservableDebugSlave>(&dbg) {} 00372 ~DebugObserver() {} 00373 protected: 00374 /** Reuse the Observer master as debug_master. */ 00375 Debugable* debug_master() const { return master(); } 00376 }; 00377 00378 } 00379 #endif
dvutil-1.0.10 | [ 5 December, 2009] |