Fawkes API
Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * exception.cpp - basic exception 00004 * 00005 * Generated: Thu Feb 09 13:04:45 2006 (from FireVision) 00006 * Copyright 2005-2006 Tim Niemueller [www.niemueller.de] 00007 * 00008 ****************************************************************************/ 00009 00010 /* This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. A runtime exception applies to 00014 * this software (see LICENSE.GPL_WRE file mentioned below for details). 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU Library General Public License for more details. 00020 * 00021 * Read the full text in the LICENSE.GPL_WRE file in the doc directory. 00022 */ 00023 00024 #include <core/exception.h> 00025 #include <core/threading/mutex.h> 00026 00027 #ifndef _GNU_SOURCE 00028 #define _GNU_SOURCE 00029 #endif 00030 00031 #include <cstring> 00032 #include <cstdlib> 00033 #include <cstdio> 00034 #ifdef HAVE_EXECINFO 00035 # include <execinfo.h> 00036 #endif 00037 00038 namespace fawkes { 00039 00040 /** @class Exception core/exception.h 00041 * Base class for exceptions in Fawkes. 00042 * Exceptions are a good way to handle errors. If you choose to use 00043 * exceptions use derivates of this class so that there is a unified way of 00044 * handling errors in Fawkes. Do <i>not</i> throw an arbitrary class such as 00045 * a string or integer as this is hard to handle. 00046 * 00047 * For your exceptions in general you only need to override the constructor 00048 * and call the Exception constructor with the appropriate message. In 00049 * cases where more information is needed about the error add appropriate 00050 * handlers. 00051 * 00052 * In most cases it is bad to just throw an Exception like this: 00053 * 00054 * @code 00055 * if ( error_condition ) { 00056 * throw Exception("Out of memory"); 00057 * } 00058 * @endcode 00059 * 00060 * Rather you should explicitly subclass Exception appropriately. For the 00061 * above example you could have something like this as exception class: 00062 * 00063 * @code 00064 * class OutOfMemException : public Exception 00065 * { 00066 * public: 00067 * OutOfMemoryException() : Exception("Out of memory") {} 00068 * } 00069 * @endcode 00070 * 00071 * And in your handling code you throw a OutOfMemoryException. This is 00072 * especially useful if it is possible to throw several different exceptions. 00073 * If the message was different you would have to parse the string for 00074 * the exact error. This can be avoided if you just catch different 00075 * exceptions. This is also useful if the Exception is not catched explicitly 00076 * as this will printout the name of the exception class thrown just before 00077 * exiting the program. And reading something like 00078 * "terminate called after throwing an instance of 'OutOfMemoryException'" 00079 * makes it a lot easier to spot the problem. 00080 * 00081 * Exceptions should be catched by reference like this: 00082 * @code 00083 * try { 00084 * some_operation(); 00085 * } catch (OutOfMemoryException &e) { 00086 * std::cout << e.c_str() << std::endl; 00087 * error_handling(); 00088 * } 00089 * @endcode 00090 * 00091 * Messages are stored as list. The first message however is called the 00092 * primary message and it should contain as much information as available. 00093 * This message is printed on the screen if the application crashes with an 00094 * unhandled exception. So having meaningful content here means that the 00095 * error can be traced more easily. 00096 * 00097 * You can utilize the list feature by adding appropriate information 00098 * through appropriate try/catch statements. This way you can 00099 * build information path ways that will help to debug your software. Use 00100 * block like this to append information: 00101 * @code 00102 * try { 00103 * potentially_failing(); 00104 * } catch {MyException &e) { 00105 * e.append("info where exception happened"); 00106 * throw; // re-throw exception 00107 * } 00108 * @endcode 00109 * This is especially useful if the exception may occur at several different 00110 * places and it cannot be fixed where it happens. 00111 * 00112 * 00113 * @see example_exception.cpp 00114 * @ingroup FCL 00115 * @ingroup Exceptions 00116 * 00117 * @author Tim Niemueller 00118 */ 00119 /** @var Exception::messages 00120 * List of messages. Should not be NULL. Messages are append with append(). 00121 * Using a custom list to avoid including STL stuff in this core file. 00122 * @see append() 00123 */ 00124 /** @var Exception::messages_iterator 00125 * Iterator to iterate over messages 00126 */ 00127 /** @var Exception::messages_end 00128 * Pointer that points to the very last message. Used for fast appending. 00129 */ 00130 /** @var Exception::messages_mutex 00131 * Mutex to protect operations on messages list. 00132 */ 00133 /** @var Exception::_errno 00134 * Error number, should be used if the error was caused by a method that supplies 00135 * errno. 00136 */ 00137 00138 00139 /** Constructor. 00140 * Constructs a new exception with the given message. 00141 * @param format The format of the primary message. Supports the same 00142 * arguments as append(). The message is copied and not just referenced. 00143 * Thus the memory has to be freed if it is a dynamic string on the heap. 00144 */ 00145 Exception::Exception(const char *format, ...) throw() 00146 { 00147 messages_mutex = new Mutex(); 00148 00149 _errno = 0; 00150 __type_id = "unknown"; 00151 00152 messages = NULL; 00153 messages_end = NULL; 00154 messages_iterator = NULL; 00155 00156 if ( format != NULL ) { 00157 va_list arg; 00158 va_start(arg, format); 00159 append_nolock_va(format, arg); 00160 va_end(arg); 00161 } else { 00162 append_nolock("Unnkown Exception"); 00163 } 00164 } 00165 00166 00167 /** Constructor. 00168 * Constructs a new exception with the given message and errno value. This 00169 * is particularly handy when throwing the exception after a function failed 00170 * that returns an error code in errno. 00171 * @param errno error number 00172 * @param format The format of the primary message. Supports the same 00173 * arguments as append(). The message is copied and not just referenced. 00174 * Thus the memory has to be freed if it is a dynamic string on the heap. 00175 */ 00176 Exception::Exception(int errno, const char *format, ...) throw() 00177 { 00178 messages_mutex = new Mutex(); 00179 00180 _errno = errno; 00181 __type_id = "unknown"; 00182 00183 messages = NULL; 00184 messages_end = NULL; 00185 messages_iterator = NULL; 00186 00187 if ( format != NULL ) { 00188 va_list arg; 00189 va_start(arg, format); 00190 char *ext_format; 00191 if ( asprintf(&ext_format, "%s (errno: %i, %s)", format, errno, strerror(errno)) == -1 ) { 00192 append_nolock_va(format, arg); 00193 } else { 00194 append_nolock_va(ext_format, arg); 00195 free(ext_format); 00196 } 00197 va_end(arg); 00198 } else { 00199 append_nolock("Exception with errno=%i (%s)", errno, strerror(errno)); 00200 } 00201 } 00202 00203 00204 /** Copy constructor. 00205 * The copy constructor is worth some extra discussion. If you do an exception 00206 * by value (which you shouldn't in the first place since this will generate a 00207 * copy, only do this if you can't avoid it for some reason. Not if you only 00208 * THINK that you can't avoid it) the copy constructor is called. If your catch 00209 * statements reads like 00210 * @code 00211 * try { 00212 * ... 00213 * } catch (Exception e) { 00214 * ... 00215 * } 00216 * @endcode 00217 * then a copy will be created for the catch block. You throw the exception with 00218 * something like 00219 * @code 00220 * throw Exception("Boom"); 00221 * @endcode 00222 * This will create an Exception which is valid in the block where you throw the 00223 * exception. Now for the catch block a copy is created. Since the exception 00224 * holds a pointer on the heap the implicit copy constructor would just copy 00225 * the pointer, not the data. So both exceptions point to the same data (to the 00226 * message for the base exception). If now both destructors for the exception 00227 * are called they both try to free the very same memory. Of course the second 00228 * destructor will cause a disaster. If you are lucky your glibc detectes the 00229 * problem an kills the application. If you are not that fortunate you will 00230 * cause very strange behaviour of your application. 00231 * 00232 * In general you should not have to worry about this. But if you choose to have 00233 * own storage on the heap using either new, malloc or a method that returns 00234 * memory on the heap (like strdup()) you have to write your own copy contructor 00235 * and copy the memory area or take care that only one exception frees the memory. 00236 * @param exc Exception to copy 00237 */ 00238 Exception::Exception(const Exception &exc) throw() 00239 { 00240 messages_mutex = new Mutex(); 00241 00242 messages = NULL; 00243 messages_end = NULL; 00244 messages_iterator = NULL; 00245 00246 _errno = exc._errno; 00247 __type_id = exc.__type_id; 00248 copy_messages(exc); 00249 } 00250 00251 00252 /** Constructor for subclasses. 00253 * This constructor can be used in subclasses is some processing code is 00254 * needed (like sprintf) to assign the message. At least assign the empty 00255 * string to the message. 00256 */ 00257 Exception::Exception() throw() 00258 { 00259 messages_mutex = new Mutex(); 00260 _errno = 0; 00261 __type_id = "unknown"; 00262 messages = NULL; 00263 messages_end = NULL; 00264 messages_iterator = NULL; 00265 } 00266 00267 00268 /** Destructor. */ 00269 Exception::~Exception() throw() 00270 { 00271 message_list_t *msg_this; 00272 messages_iterator = messages; 00273 while ( messages_iterator ) { 00274 free(messages_iterator->msg); 00275 msg_this = messages_iterator; 00276 messages_iterator = messages_iterator->next; 00277 free(msg_this); 00278 } 00279 messages = NULL; 00280 messages_end = NULL; 00281 delete messages_mutex; 00282 } 00283 00284 00285 /** Set exception type ID. 00286 * Set the type ID of this exception. 00287 * @param id new type ID, note that this must be a static string which is 00288 * guaranteed to exist for the whole lifetime of the exception. 00289 * @see Exception::type_id() 00290 */ 00291 void 00292 Exception::set_type_id(const char *id) 00293 { 00294 __type_id = id; 00295 } 00296 00297 00298 /** Get type ID. 00299 * Exceptions can have a type ID. This can be used to avoid having to declare 00300 * numerous specific exception sub-classes to different errors, if it is 00301 * essential to be able to differentiate them in the exception handling code. 00302 * The type ID is a free-form string. It should NOT contain any message, rather 00303 * it should be a one-word internal identifier that is never leaked to the user 00304 * of the software, i.e. it is not printed anywhere. Note that the ID must be 00305 * a static string, which exists for the whole life time of the exception, is 00306 * generally not in a dynamically allocated memory (this very exception could 00307 * indicate memory shortage). This also makes it thread-safe. 00308 * @return type ID 00309 */ 00310 const char * 00311 Exception::type_id() const 00312 { 00313 return __type_id; 00314 } 00315 00316 00317 /** Prepend messages to the message list. 00318 * @param format format of the message to prepend, see printf(3) for details about formatting 00319 * options. 00320 */ 00321 void 00322 Exception::prepend(const char *format, ...) throw() 00323 { 00324 // do not append empty messages 00325 if (format == NULL) return; 00326 00327 va_list arg; 00328 va_start(arg, format); 00329 messages_mutex->lock(); 00330 prepend_nolock_va(format, arg); 00331 messages_mutex->unlock(); 00332 va_end(arg); 00333 } 00334 00335 00336 /** Append messages to the message list. 00337 * @param format format of the message to append, see printf(3) for details about formatting 00338 * options. 00339 */ 00340 void 00341 Exception::append(const char *format, ...) throw() 00342 { 00343 // do not append empty messages 00344 if (format == NULL) return; 00345 00346 va_list arg; 00347 va_start(arg, format); 00348 messages_mutex->lock(); 00349 append_nolock_va(format, arg); 00350 messages_mutex->unlock(); 00351 va_end(arg); 00352 } 00353 00354 00355 /** Append messages to the message list. 00356 * @param format format of the message to append, see printf(3) for details about formatting 00357 * options. 00358 * @param va va_list with arguments matching the format 00359 */ 00360 void 00361 Exception::append_va(const char *format, va_list va) throw() 00362 { 00363 // do not append empty messages 00364 if (format == NULL) return; 00365 00366 messages_mutex->lock(); 00367 append_nolock_va(format, va); 00368 messages_mutex->unlock(); 00369 } 00370 00371 00372 /** Append message that are from another Exception. 00373 * @param e Exception to copy messages from 00374 */ 00375 void 00376 Exception::append(const Exception &e) throw() 00377 { 00378 copy_messages(e); 00379 } 00380 00381 00382 /** Append messages without lock. 00383 * this can be used to append messages without locking the mutex if the mutex 00384 * has been locked already to append many messages and keep their order intact 00385 * and thus to prevent messages to be appended inbetween. 00386 * Used for example in copy constructor. 00387 * @param format The format of the primary message. Supports the same 00388 * arguments as append(). The message is copied and not just referenced. 00389 * Thus the memory has to be freed if it is a dynamic string on the heap. 00390 */ 00391 void 00392 Exception::append_nolock(const char *format, ...) throw() 00393 { 00394 va_list arg; 00395 va_start(arg, format); 00396 00397 char *msg; 00398 if ( vasprintf(&msg, format, arg) == -1 ) { 00399 msg = strdup(format); 00400 } 00401 00402 va_end(arg); 00403 00404 if ( messages == NULL ) { 00405 // This is our first message 00406 messages = (message_list_t *)malloc(sizeof(message_list_t)); 00407 messages->next = NULL; 00408 messages->msg = msg; 00409 messages_end = messages; 00410 } else { 00411 message_list_t *ml = (message_list_t *)malloc(sizeof(message_list_t)); 00412 ml->next = NULL; 00413 ml->msg = msg; 00414 messages_end->next = ml; 00415 messages_end = ml; 00416 } 00417 } 00418 00419 00420 /** Prepend messages without lock by formatted string. 00421 * This can be used to append messages without locking the mutex if the mutex 00422 * has been locked already to append many messages and keep their order intact 00423 * and thus to prevent messages to be appended inbetween. 00424 * Used for example in copy constructor. 00425 * @param format format of the message to be appended 00426 * @param ap argument va_list for format 00427 */ 00428 void 00429 Exception::prepend_nolock_va(const char *format, va_list ap) throw() 00430 { 00431 char *msg; 00432 if ( vasprintf(&msg, format, ap) == -1 ) { 00433 msg = strdup(format); 00434 } 00435 00436 if ( messages == NULL ) { 00437 // This is our first message 00438 messages = (message_list_t *)malloc(sizeof(message_list_t)); 00439 messages->next = NULL; 00440 messages->msg = msg; 00441 messages_end = messages; 00442 } else { 00443 message_list_t *ml = (message_list_t *)malloc(sizeof(message_list_t)); 00444 ml->next = messages; 00445 ml->msg = msg; 00446 messages = ml; 00447 } 00448 } 00449 00450 00451 /** Append messages without lock by formatted string. 00452 * this can be used to append messages without locking the mutex if the mutex 00453 * has been locked already to append many messages and keep their order intact 00454 * and thus to prevent messages to be appended inbetween. 00455 * Used for example in copy constructor. 00456 * @param format format of the message to be appended 00457 * @param ap argument va_list for format 00458 */ 00459 void 00460 Exception::append_nolock_va(const char *format, va_list ap) throw() 00461 { 00462 char *msg; 00463 if ( vasprintf(&msg, format, ap) == -1 ) { 00464 msg = strdup(format); 00465 } 00466 00467 if ( messages == NULL ) { 00468 // This is our first message 00469 messages = (message_list_t *)malloc(sizeof(message_list_t)); 00470 messages->next = NULL; 00471 messages->msg = msg; 00472 messages_end = messages; 00473 } else { 00474 message_list_t *ml = (message_list_t *)malloc(sizeof(message_list_t)); 00475 ml->next = NULL; 00476 ml->msg = msg; 00477 messages_end->next = ml; 00478 messages_end = ml; 00479 } 00480 } 00481 00482 00483 /** Append message without copying. 00484 * Can be used in subclasses to append messages that have been allocated 00485 * on the heap. Use with extreme care. Do not add constant strings! This would 00486 * cause your application to crash since the destructor will try to free all 00487 * messages. The message list is not locked. 00488 * @param msg Message to append. 00489 */ 00490 void 00491 Exception::append_nolock_nocopy(char *msg) throw() 00492 { 00493 if ( messages == NULL ) { 00494 // This is our first message 00495 messages = (message_list_t *)malloc(sizeof(message_list_t)); 00496 messages->next = NULL; 00497 messages->msg = msg; 00498 messages_end = messages; 00499 } else { 00500 message_list_t *ml = (message_list_t *)malloc(sizeof(message_list_t)); 00501 ml->next = NULL; 00502 ml->msg = msg; 00503 messages_end->next = ml; 00504 messages_end = ml; 00505 } 00506 } 00507 00508 00509 /** Assign an Exception. 00510 * As this is one of the Big Three (see C++ FAQ at 00511 * http://www.parashift.com/c++-faq-lite/coding-standards.html#faq-27.10) this 00512 * is needed because we already need a copy constructor. Read about the 00513 * copy constructor why this is the case. 00514 * @see Exception(const Exception &exc) 00515 * @param exc The exception with the values to assign to this exception. 00516 * @return reference to this object. Allows assignment chaining. 00517 */ 00518 Exception & 00519 Exception::operator=(const Exception &exc) throw() 00520 { 00521 messages_mutex = new Mutex(); 00522 copy_messages(exc); 00523 00524 return *this; 00525 } 00526 00527 00528 /** Copy messages from given exception. 00529 * Copies the messages from exc to this exception. 00530 * @param exc Exception to copy messages from. 00531 */ 00532 void 00533 Exception::copy_messages(const Exception &exc) throw() 00534 { 00535 messages_mutex->lock(); 00536 exc.messages_mutex->lock(); 00537 00538 // copy messages 00539 messages_iterator = exc.messages; 00540 while ( messages_iterator ) { 00541 append_nolock(messages_iterator->msg); 00542 messages_iterator = messages_iterator->next; 00543 } 00544 00545 exc.messages_mutex->unlock(); 00546 messages_mutex->unlock(); 00547 } 00548 00549 00550 /** This can be used to throw this exception. 00551 * This can be used to throw this exception instance. This is a precaution if 00552 * it is needed. See C++ FAQ 17.10. 00553 */ 00554 void 00555 Exception::raise() 00556 { 00557 throw *this; 00558 } 00559 00560 00561 /** Prints a backtrace. */ 00562 void 00563 Exception::print_backtrace() const throw() 00564 { 00565 #ifdef HAVE_EXECINFO 00566 void * array[25]; 00567 int size = backtrace(array, 25); 00568 char ** symbols = backtrace_symbols(array, size); 00569 00570 printf("Backtrace:\n"); 00571 for (int i = 0; i < size; ++i) { 00572 printf(" %s\n", symbols[i]); 00573 } 00574 00575 free(symbols); 00576 #else 00577 printf("Backtrace not available on current system\n"); 00578 #endif 00579 } 00580 00581 00582 /** Generate backtrace string. 00583 * @return freshly allocated string of backtrace. Free after you are done. 00584 */ 00585 char * 00586 Exception::generate_backtrace() const throw() 00587 { 00588 #ifdef HAVE_BACKTRACE 00589 void * array[25]; 00590 int size = backtrace(array, 25); 00591 char ** symbols = backtrace_symbols(array, size); 00592 00593 size_t total_size = 1; //null termination 00594 for (int i = 0; i < size; ++i) { 00595 total_size += strlen(symbols[i]) + 1; 00596 } 00597 char *rv = (char *)calloc(1, total_size); 00598 char *r = rv; 00599 for (int i = 0; i < size; ++i) { 00600 sprintf(r, "%s\n", symbols[i]); 00601 r += strlen(symbols[i]); 00602 } 00603 00604 free(symbols); 00605 #else 00606 char *rv = strdup("Backtrace not available on current system\n"); 00607 #endif 00608 00609 return rv; 00610 } 00611 00612 00613 /** Prints trace to stderr. 00614 * This prints out a message trace of all messages appended to the exception 00615 * in chronological order starting with the oldest (first message appended 00616 * via constructor or append(). Output will be sent to stderr. 00617 */ 00618 void 00619 Exception::print_trace() throw() 00620 { 00621 messages_mutex->lock(); 00622 fprintf(stderr, 00623 "=================================================== BEGIN OF EXCEPTION =====\n"); 00624 if ( messages == NULL ) { 00625 fprintf(stderr, "No messages recorded.\n"); 00626 } else { 00627 messages_iterator = messages; 00628 while ( messages_iterator ) { 00629 fprintf(stderr, "%s\n", messages_iterator->msg); 00630 messages_iterator = messages_iterator->next; 00631 } 00632 } 00633 fprintf(stderr, 00634 "=================================================== END OF EXCEPTION =======\n"); 00635 messages_mutex->unlock(); 00636 } 00637 00638 00639 /** Get errno. 00640 * @return error number, may be 0 if not set 00641 */ 00642 int 00643 Exception::get_errno() throw() 00644 { 00645 return _errno; 00646 } 00647 00648 00649 /** Get primary string. 00650 * Messages are stored in a list. The first entry in this list is called primary 00651 * message. This is why it is important to have a meaningful first message! 00652 * @return Returns a constant char pointer with the message. The message is 00653 * private to the exception and may not be modified or freed (hence const) 00654 * If no message has been set "Unknown error" is returned. This method may be 00655 * overidden by other exceptions. 00656 * This method is also called by the runtime system if the exception was not 00657 * caught and resulted in a program termination. 00658 * @return string describing the general cause of the current error 00659 */ 00660 const char * 00661 Exception::what() const throw() 00662 { 00663 #ifdef HAVE_EXECINFO 00664 print_backtrace(); 00665 #endif 00666 if ( messages != NULL ) { 00667 return messages->msg; 00668 } else { 00669 return "Unknown error"; 00670 } 00671 } 00672 00673 00674 /** Get iterator for messages. 00675 * @return iterator for messages 00676 */ 00677 Exception::iterator 00678 Exception::begin() throw() 00679 { 00680 Exception::iterator i(messages); 00681 return i; 00682 } 00683 00684 00685 /** @class Exception::iterator <core/exception.h> 00686 * Message iterator for exceptions. 00687 * This iterator allows for iterating over all messages carried by an Exception. 00688 * @author Tim Niemueller 00689 */ 00690 00691 /** Get end iterator for messages. 00692 * @return end iterator for messages. 00693 */ 00694 Exception::iterator 00695 Exception::end() throw() 00696 { 00697 Exception::iterator i; 00698 return i; 00699 } 00700 00701 00702 /** Constructor. 00703 * @param message_list list of messages, will be used unlocked so use 00704 * with care. 00705 */ 00706 Exception::iterator::iterator(message_list_t *message_list) 00707 { 00708 mlist = message_list; 00709 } 00710 00711 00712 /** Plain constructor. 00713 * Creates a new invalid iterator (same as Exception::end()). 00714 */ 00715 Exception::iterator::iterator() 00716 { 00717 this->mlist = NULL; 00718 } 00719 00720 00721 /** Copy constructor. 00722 * @param i iterator to copy 00723 */ 00724 Exception::iterator::iterator(const Exception::iterator & i) 00725 { 00726 this->mlist = i.mlist; 00727 } 00728 00729 00730 /** Prefix ++ operator. 00731 * @return reference to this iterator after advancing. 00732 */ 00733 Exception::iterator & 00734 Exception::iterator::operator++() 00735 { 00736 if ( mlist != NULL ) { 00737 mlist = mlist->next; 00738 } 00739 return *this; 00740 } 00741 00742 00743 /** Postfix ++ operator. 00744 * @param inc used to denote postfix operator 00745 * @return copy of iterator before advancing. 00746 */ 00747 Exception::iterator 00748 Exception::iterator::operator++(int inc) 00749 { 00750 iterator i(mlist); 00751 if ( mlist != NULL ) { 00752 mlist = mlist->next; 00753 } 00754 return i; 00755 } 00756 00757 00758 /** Check equality. 00759 * @param i iterator to compare to 00760 * @return true, if iterators point to the same message, false otherwise 00761 */ 00762 bool 00763 Exception::iterator::operator==(const iterator & i) const 00764 { 00765 return (mlist == i.mlist); 00766 } 00767 00768 00769 /** Check inequality. 00770 * @param i iterator to compare to 00771 * @return true, if iterators point to different messages, false otherwise 00772 */ 00773 bool 00774 Exception::iterator::operator!=(const iterator & i) const 00775 { 00776 return (mlist != i.mlist); 00777 } 00778 00779 00780 /** Get current message. 00781 * Get message at current position. Returns NULL for the invalid ieterator. 00782 * @return message or NULL if iterator is invalid 00783 */ 00784 const char * 00785 Exception::iterator::operator* () const 00786 { 00787 if ( mlist != NULL ) { 00788 return mlist->msg; 00789 } else { 00790 return NULL; 00791 } 00792 } 00793 00794 00795 /** Assignment operator. 00796 * @param i iterator to assign to this iterator. 00797 * @return reference to this iterator. 00798 */ 00799 Exception::iterator & 00800 Exception::iterator::operator=(const iterator &i) 00801 { 00802 this->mlist = i.mlist; 00803 return *this; 00804 } 00805 00806 00807 } // end namespace fawkes