By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
443,795 Members | 1,660 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 443,795 IT Pros & Developers. It's quick & easy.

deriving from std::exception and overiding what() to construct dynamic messages.

P: 8
Hello Guys and Galls,

To start off, I have reached the solution I was looking for, but I would like comments and feedback on the solution I have reached and tips/tricks on making it more elegant. I am not satisfied with the underlying machinery of the solution though.

I am an advanced C programmer and most do object-based programming in C++. Please do not reply to this article with references to basic material or obvious tips.

An overview to the problem I was trying to solve ; I have written a very complex plugin for a webserver and it shows some bugs only with live use (to be specific a ISAPI Filter for Microsoft IIS). I was not given the go ahead to do live debugging on the server and it might not have helped track the bug anyway. In order to catch it I have to improve the instrumentation of my software.

The error codes in the server (win32 API numeric error codes) are mostly converted into exceptions but the exceptions weren't intelligent in any way... they were all stand-alone and did not derive from a standard heirarchy. I need a standard heirarchy to maintain sanity and I need the debug messages to be dynamic. std::exception as a base class was the obvious choice (wish I hadn't gone down that route now :D ) .

I want the exceptions to have a title and a body and I would obviously want to generate the body at runtime.The problem is that the what() method signature from std::exception defines the what as a const method.

Getting arround the const restriction leads to a ton of fidley code, which I should say I cannot accept it has made me hate aspects of the C++ language and the std library, I certainly would have faired better implementing the following in straight C, however saying this I want my faith restored in the language and shown why I am being unfair. The following solution has taken me about 10 hours of refactoring.

The custom exception for my project requires the following semantics.

Expand|Select|Wrap|Line Numbers
  1. class CustomException
  2.     : public SStreamException
  3. {
  4. public :
  5.     CustomException(char * file_in,DWORD line_in)
  6.         : SStreamException(file_in,line_in) {}
  7.     virtual const char * getType() const
  8.     {
  9.         return "testing";
  10.     }
  11.     virtual void getBody(std::stringstream & body_in) const
  12.     {
  13.         body_in << "My Sexy Body";
  14.     }
  15. };
so the two methods getType() and getBody() are called from the what() method in SStreamException(which extends std::exception) resulting in a runtime dynamic error message. I guess this uses the delegate pattern. I am satisfied with these semantics and it should make my exception definitions look clean and elegant.

The Following is the underlying machinery to support the dynamic what() method.

Expand|Select|Wrap|Line Numbers
  1. #include <sstream>
  2. #include <string>
  3. #include <windows.h>
  4.  
  5. /* Uses std::string to maintain the string. string::c_str() is a (const char *). 
  6.  * if std::stringstream::str()::c_str() is used the intermediary string object
  7.  * vanishes at end of scope (return). Note that it derives from strinstream 
  8.  */
  9. class DynamicConstString
  10.     : public std::stringstream
  11. {
  12. private : 
  13.     std::string * _privateConstString;
  14. public:
  15.     DynamicConstString()
  16.         : _privateConstString(NULL)
  17.     {
  18.         _privateConstString=new std::string();
  19.     }
  20.     ~DynamicConstString()
  21.     {
  22.         if(_privateConstString!=NULL)
  23.             delete _privateConstString;
  24.     }
  25.  
  26.     /* prior to a clear the string is constant */
  27.     const char * getCString()
  28.     {
  29.         _privateConstString->assign((*this).str());
  30.         return _privateConstString->c_str();
  31.     }
  32.  
  33.     /* empty the string state in preparation of new data */
  34.     void recycle()
  35.     {
  36.         _privateConstString->assign("");
  37.         (*this).str(*_privateConstString);
  38.     }
  39. };
  40.  
  41.  
  42. class SStreamException
  43.     : public std::exception
  44. {
  45.     char * file;
  46.     DWORD line;
  47.     /* For some reason (cannot remember)  the following two objects need to be 
  48.      * heap allocated for const correctness */
  49.     DynamicConstString * __debugMsgString;
  50.     DynamicConstString * __bodyString;
  51.  
  52. public:
  53.     /* Following methods need to be defined by the Children */
  54.     virtual const char * getType() const
  55.     {
  56.         return "Type Unspecified";
  57.     }
  58.     virtual void getBody(std::stringstream & _bodyString_in) const
  59.     {
  60.            _bodyString_in << "";
  61.     }
  62.  
  63.     SStreamException(char * file_in, DWORD line_in)
  64.         : file(file_in), line(line_in),__debugMsgString(NULL),__bodyString(NULL)
  65.     {
  66.         __debugMsgString=new DynamicConstString();
  67.         __bodyString=new DynamicConstString();
  68.     }
  69.     ~SStreamException()
  70.     {
  71.         if(__debugMsgString!=NULL)
  72.             delete __debugMsgString;
  73.         if(__debugMsgString!=NULL)
  74.             delete __bodyString;
  75.     }
  76.  
  77.  
  78.  
  79.     const char * what() const throw()
  80.     {
  81.  
  82.         __debugMsgString->recycle();
  83.         __bodyString->recycle();
  84.  
  85.         std::stringstream & debugMsgString=*__debugMsgString;
  86.         std::stringstream & bodyString=*__bodyString;
  87.  
  88.         debugMsgString << "Exception : " << getType() << std::endl;
  89.         debugMsgString << "(" << file << ":" << line << ")" << std::endl;
  90.  
  91.         getBody(bodyString);
  92.  
  93.         /* if (bodyString.str()) is not used the pointer of (__bodystring) is used 
  94.          * for some reason. weird reason */
  95.         debugMsgString << (bodyString.str());
  96.  
  97.         return __debugMsgString->getCString();        
  98.     }
  99. };
  100.  
To end I guess the core things I want to know are,

(1) Why the hell is what() marked as a const function ? is this braindead
or is there a justification for this.

(2) Have I circumvented the const correctness of std::exception::what()

(3) is their a more elegant way to do this.

(4) are there more specialized libraries that do what I am trying to do for c++. (perhaps in boost ? Or Such).

(5) Could someone point me to some advanced articles on const correctness and the pitfalls of C++ object orientation.

(6) Is C++0x or the TR1 of boost attempting to make expressing such semantics in C++ better ? (perhaps the new garbage collector) And if so How ?

Looking forward to your feedback

Regards

Vain
Apr 10 '09 #1
Share this Question
Share on Google+
3 Replies


weaknessforcats
Expert Mod 5K+
P: 9,197
The STL exception::what() is a const member function so you can use with const objects.

It returns a const char* so you can't alter the string pointed at since that may itself be a const string.

Personally, when I use this thing, my override a) dumps all my data to a log file, b) obtains the userid, machine name, IP address, system date an time, etc... and dumps all that to the log file.

Then I return "It bombed!".

exception::what() was never intended to be used just by looking at the returned char*.

Since you are an advanced C++ developer, you know that a cast of any kind means a) you are calling a relic C function, or b) your C++ design is screwed up.

That said, you never alter const. The only safe alteration of const is to const_cast a const object to a non_const object so you can call a non-const function with a const object. Going the other way, a const_cast is only safe when you know the non-const object was const to begin with. All other const casts are hacks and elicit undefined behavior by your compiler.

This is basic Stroustrup and it's in his book.

The one weakness of the STL is that is persists in using public virtual functions which are a big no-no since they merge the implementation (the virtual function) with the interface (the public methods). Scott Meyers has material on this or you could just look at the Template design pattern.
Apr 10 '09 #2

P: 8
const_cast had completely slipped my mind. If I had marked the objects I am using for string manipulation as const (I think) I would not have had to add an extra level of indirection to my code, I would have done a const_cast.

Your tip has directly addressed my confusion. Thank you.

I am still a bit miffed at how the const keyword is used in C++. I appreciate the need for it in the design of an API and as a tool to reduce complexity. I guess by specifying the virtual function as const the library writer can say "Hey MR. DeriveFromMyObject,, I don't want to be responsible for altering the state of your object, so I will ask Mr. Compiler to police you".

Now that I understand the problem and its solution I am wondering if perhaps in this scenario the prevention of misuse should better be left to the programmer ? It lowers the barrier to entry for C++ programming. In my opinion the real skill lies in understanding the low-level programming and not in the knowledge of a complex language.
Apr 14 '09 #3

weaknessforcats
Expert Mod 5K+
P: 9,197
In my opinion the real skill lies in understanding the low-level programming and not in the knowledge of a complex language.
True for C but not true for C++.

C++ precisely wants you to avoid that low-level stuff. That's why there is an STL that uses algorithms of only none, one or two arguments. More argments that that need intermediate binder objects. All of this to avoid constant recoding of low-level stuff.

With C++ you stay with creating derived objects that are manipulated using base class pointers. Derived methods not in the base class are executed by using a Visitor. Derived objects that are modified notify a Mediator who sends messages to the necessary Obervers.
Apr 19 '09 #4

Post your reply

Sign in to post your reply or Sign up for a free account.