ExceptionHandler class & list of error messages ?

M

mast2as

Hi everyone... I have a TExceptionHandler class that is uses in the
code to thow exceptions. Whenever an exception is thrown the
TExceptionHander constructor takes an error code (int) as an argument.
I was hoping to create a map<int, const char*> that would be used in
the showError member function of the TExceptionHandler class where the
key (int) would be the error code and const char* the message printed
out to the console.

My question is the following. What would be the best way of creating
that map ? Here is why I am asking. I believe when you throw an
exception you actually create a copy of the object TExceptionHandler.
So if the map is a member variable of the class and that each entry in
the map is intialised with a string (as an error message) it would be a
waste of CPU time & memory (just stupide to create that list eveytime a
TExceptionHandler object is created).

Is the best way, to make the TExceptionHandler class a singleton ? The
map could a static member variable and would be intialised only once.
Or should I make the map a global variable (like an array of const
char* or string) and just use that global variable in the showError()
function ?

I am sure there's a good way of doing that ;-)

I looked on the net and the groups' archives but couldn't find anything
related on that precise aspect of exception handling.

thank you, mark

(ps I know i ask a lot to the group at the moment, but i also learn a
lot from you guys ! I hope i'll be able to help other people one
day...)

class TExceptionHandler
{
public:
TExceptionHandler( int error ) :
m_code( error ),
m_severity( GIE_ERRORPRINT ),
m_message( "" )
{}

TExceptionHandler( int error, int severity ) :
m_code( error ),
m_severity( severity ),
m_message( "" )
{}

~TExceptionHandler() {}
void showError()
{
// print out the error message base of the error code
cout << globalErrorMessageList[ m_error ] << endl; << is that the
best option ?
}
private:
int m_code;
int m_severity;
const char* m_message;
};

// The class used somewhere in the code
try {
popModel( TAppliState::kModelA );
} catch ( TExceptionHandler &e ) {
e.showError();
}
 
M

mast2as

oops sorry there's a typo mistake it should be

cout << globalErrorMessageList[ m_code ] << endl;
 
V

Vikram

Hi everyone... I have a TExceptionHandler class that is uses in the
code to thow exceptions. Whenever an exception is thrown the
TExceptionHander constructor takes an error code (int) as an argument.
I was hoping to create a map<int, const char*> that would be used in
the showError member function of the TExceptionHandler class where the
key (int) would be the error code and const char* the message printed
out to the console.

My question is the following. What would be the best way of creating
that map ? Here is why I am asking. I believe when you throw an
exception you actually create a copy of the object TExceptionHandler.
So if the map is a member variable of the class and that each entry in
the map is intialised with a string (as an error message) it would be a
waste of CPU time & memory (just stupide to create that list eveytime a
TExceptionHandler object is created).

Is the best way, to make the TExceptionHandler class a singleton ? The
map could a static member variable and would be intialised only once.
Or should I make the map a global variable (like an array of const
char* or string) and just use that global variable in the showError()
function ?

Why not just detach the error code and the map? Have separate classes
TExceptionCode and TExceptionHandler. Have the map and all the logic in
the handler and just throw the TExceptionCode object rather than
Handler.
 
M

mlimber

Hi everyone... I have a TExceptionHandler class that is uses in the
code to thow exceptions. Whenever an exception is thrown the
TExceptionHander constructor takes an error code (int) as an argument.
I was hoping to create a map<int, const char*> that would be used in
the showError member function of the TExceptionHandler class where the
key (int) would be the error code and const char* the message printed
out to the console.

My question is the following. What would be the best way of creating
that map ? Here is why I am asking. I believe when you throw an
exception you actually create a copy of the object TExceptionHandler.
So if the map is a member variable of the class and that each entry in
the map is intialised with a string (as an error message) it would be a
waste of CPU time & memory (just stupide to create that list eveytime a
TExceptionHandler object is created).

Is the best way, to make the TExceptionHandler class a singleton ? The
map could a static member variable and would be intialised only once.
Or should I make the map a global variable (like an array of const
char* or string) and just use that global variable in the showError()
function ?

I am sure there's a good way of doing that ;-)

I looked on the net and the groups' archives but couldn't find anything
related on that precise aspect of exception handling.

Why not make it a private static member in your class:

class TException
: std::exception
{
public:
enum ErrorCode
{
SOMETHING_HAPPENED = 1,
SOMETHING_ELSE = 5,
SOMETHING_STRANGE = 42
};

typedef std::map<ErrorCode, const char*> MsgMap;

// Only one constructor is needed
TException(
const ErrorCode code,
const int severity = GIE_ERRORPRINT )
: m_code( error )
, m_severity( severity )
{}

virtual const char* what() const
{
return msgMap_[ code_ ];
}

int severity() const
{
return severity_;
}

private:
const static MsgMap msgMap_;
const ErrorCode code_;
const int severity_;
};

Then in the .cpp file, use an initializer helper class to initialize
it:

namespace // anonymous
{
class MsgMapInitializer
{
TException::MsgMap m_;
public:
operator TException::MsgMap() const { return m_; }

MsgMapInitializer& Add( const int i, const char* s )
{
m_ = s;
return *this;
}
};
}

const TException::MsgMap TException::msgMap_ = MsgMapInitializer()
.Add( TException::SOMETHING_HAPPENED, "Msg 1" )
.Add( TException::SOMETHING_ELSE, "Msg 2" )
.Add( TException::SOMETHING_STRANGE, "Msg 3" );

Then you can use it like this:

try
{
throw TException( TException::SOMETHING_ELSE, 100 );
}
catch( const std::exception& e )
{
std::cerr << e.what() << std::endl; // Or whatever
}

[snip]
void showError()
{
// print out the error message base of the error code
cout << globalErrorMessageList[ m_error ] << endl; << is that the
best option ?
}

Typically it is best to inherit from std::exception and override the
virtual function what() to return an error message. Then the user can
do whatever s/he wants with it rather than being forced to use cout
(which is often impractical in GUI and embedded applications).
private:
int m_code;
int m_severity;
const char* m_message;
};

Why store the message locally? Just look it up when its time to use it
(probably only once).

Cheers! --M
 
M

mast2as

Thanks a lot for your help. They are 2 good ideas... and things I
wasn't sure how to implement ;-)

m_message wasn't intended to be the message attached to an error code.
I wanted to use it to have an additional error message to the
predifined one.

For example:

#define GIE_UNIMPLEMENTED

errorMessages[ GIE_UNIMPLEMENTED ] = "feature is not implemented";

throw( TExceptionHander( GIE_UNIMPLEMENTED, GIE_PRINTERROR, "carrot is
not a fruit !" ) );
....
catch( TExceptionHandler &e ) {
cout << e.what() << endl; // prints out "feature is not implemented
(carrot is not a fruit)";
}

thanks again.
 
M

mast2as

Hi I tried to "copy" your code (the names are a bit different) and i
get an error when i compile which i don't understand well... could you
help me a bit more please ? thank you...

/////// COMPILE ERROR

test.cpp: In member function `virtual const char* TException::what()
const':
test.cpp:28: error: passing `const std::map<const int, const char*,
' as `this' argument of `_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::eek:perator[](const _Key&) [with _Key = const int, _Tp = const char*, _Compare = std::less<const int>, _Alloc = std::allocator<std::pair<const int, const char*> >]' discards qualifiers

/////// CODE

#include <iostream>
#include <deque>
#include <map>
#include <exception>

#define PRINTERROR 0
#define ABORTERROR 1

class TException : public std::exception
{
public:
enum errorCode {
TEST1 = 1,
TEST2,
TEST3
};
typedef std::map<const int, const char*> MsgMap;
public:
TException( const int code, const int severity = PRINTERROR ) :
m_code( code ),
m_severity( severity )
{}

~TException() throw() {}

virtual const char* what() const throw()
{
return myMsgMap[ m_code ];//"some text";
}
public:
private:
const int m_severity;
const int m_code;
const static MsgMap myMsgMap;
};

namespace
{
class TInitialise
{
public:
TException::MsgMap m_;
public:
operator TException::MsgMap() const { return m_; }
TInitialise() {};
TInitialise &Add() { std::cout << "in add\n"; return *this; }
};
}

const TException::MsgMap TException::myMsgMap = TInitialise()
.Add()
.Add();

int main()
{
return 0;
}
 
M

mlimber

Hi I tried to "copy" your code (the names are a bit different) and i
get an error when i compile which i don't understand well... could you
help me a bit more please ? thank you...

/////// COMPILE ERROR

test.cpp: In member function `virtual const char* TException::what()
const':
test.cpp:28: error: passing `const std::map<const int, const char*,
' as `this' argument of `_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::eek:perator[](const _Key&) [with _Key = const int, _Tp = const char*, _Compare = std::less<const int>, _Alloc = std::allocator<std::pair<const int, const char*> >]' discards qualifiers

/////// CODE

#include <iostream>
#include <deque>

FYI, this header isn't used (but maybe that's because you deleted the
code that referenced it for simplicity).
#include <map>
#include <exception>

#define PRINTERROR 0
#define ABORTERROR 1

Prefer const ints or (better) an enumeration to these defines since
macros are evil (cf.
http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.5)
and should be avoided in favor of C++ constructs when possible. These
two lines won't likely cause a problem, but it's better to be in the
habit of doing things correctly.
class TException : public std::exception
{
public:
enum errorCode {
TEST1 = 1,
TEST2,
TEST3
};
typedef std::map<const int, const char*> MsgMap;

The problem is the const on the int. The map's key type is always made
const, and so you can't add that qualifier here lest you end up with a
"const const int". However, you should change the key type to errorCode
for an added bit of type safety.
public:
TException( const int code, const int severity = PRINTERROR ) :

Change this int to errorCode, too, for type safety. It won't guarantee
that the user doesn't cast an invalid integer value into your
enumeration type, but it will be helpful for non-devious users. For
instance, this line wouldn't compile if you made this change:

throw TException( 42 );

but this line would:

throw TException( 1 );

The latter is less explicit and more prone to accidents than the
equivalent "TException( TException::TEST1 )". Prefer to use your
enumeration to help the programmer use your class correctly.
m_code( code ),
m_severity( severity )
{}

~TException() throw() {}

virtual const char* what() const throw()
{
return myMsgMap[ m_code ];//"some text";
}

There's another problem here. I didn't actually try compiling the code
before, and I overlooked the fact that the [] operator for map isn't
const, and since the map is (rightly) declared const in your class, you
can't call non-const methods on it (cf.
http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.10
and
http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.12).
The correct version of this function is:

virtual const char* what() const throw()
{
const MsgMap::const_iterator iter = myMsgMap.find( m_code );
if( iter != myMsgMap.end() )
{
return iter->second;
}
return "No error message found!";
}
public:
private:
const int m_severity;
const int m_code;

Change this to errorCode as well.
const static MsgMap myMsgMap;
};

Remember, the following lines need to be in a .cpp file somewhere, not
a header.
namespace
{
class TInitialise
{
public:
TException::MsgMap m_;
public:
operator TException::MsgMap() const { return m_; }
TInitialise() {};
TInitialise &Add() { std::cout << "in add\n"; return *this; }
};
}

const TException::MsgMap TException::myMsgMap = TInitialise()
.Add()
.Add();

int main()
{
return 0;
}

Cheers! --M
 
M

mast2as

Hi Thank you again...

I will do the changes you mention. It is true that for the test code i
went for the easiest way sometimes like the #define thing but was
planning to write it the proper way (as well as the casting but as I
got problem compiling it i tried to simplify the whole program).

This the first I see this syntax ?

const TException::MsgMap TException::myMsgMap = TInitialise()
.Add()
.Add();

Would you have a link where I can read about it ? (calls to the .Add()
function)

Thanks again for your help and advices.

mark
Hi I tried to "copy" your code (the names are a bit different) and i
get an error when i compile which i don't understand well... could you
help me a bit more please ? thank you...

/////// COMPILE ERROR

test.cpp: In member function `virtual const char* TException::what()
const':
test.cpp:28: error: passing `const std::map<const int, const char*,
' as `this' argument of `_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::eek:perator[](const _Key&) [with _Key = const int, _Tp = const char*, _Compare = std::less<const int>, _Alloc = std::allocator<std::pair<const int, const char*> >]' discards qualifiers

/////// CODE

#include <iostream>
#include <deque>

FYI, this header isn't used (but maybe that's because you deleted the
code that referenced it for simplicity).
#include <map>
#include <exception>

#define PRINTERROR 0
#define ABORTERROR 1

Prefer const ints or (better) an enumeration to these defines since
macros are evil (cf.
http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.5)
and should be avoided in favor of C++ constructs when possible. These
two lines won't likely cause a problem, but it's better to be in the
habit of doing things correctly.
class TException : public std::exception
{
public:
enum errorCode {
TEST1 = 1,
TEST2,
TEST3
};
typedef std::map<const int, const char*> MsgMap;

The problem is the const on the int. The map's key type is always made
const, and so you can't add that qualifier here lest you end up with a
"const const int". However, you should change the key type to errorCode
for an added bit of type safety.
public:
TException( const int code, const int severity = PRINTERROR ) :

Change this int to errorCode, too, for type safety. It won't guarantee
that the user doesn't cast an invalid integer value into your
enumeration type, but it will be helpful for non-devious users. For
instance, this line wouldn't compile if you made this change:

throw TException( 42 );

but this line would:

throw TException( 1 );

The latter is less explicit and more prone to accidents than the
equivalent "TException( TException::TEST1 )". Prefer to use your
enumeration to help the programmer use your class correctly.
m_code( code ),
m_severity( severity )
{}

~TException() throw() {}

virtual const char* what() const throw()
{
return myMsgMap[ m_code ];//"some text";
}

There's another problem here. I didn't actually try compiling the code
before, and I overlooked the fact that the [] operator for map isn't
const, and since the map is (rightly) declared const in your class, you
can't call non-const methods on it (cf.
http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.10
and
http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.12).
The correct version of this function is:

virtual const char* what() const throw()
{
const MsgMap::const_iterator iter = myMsgMap.find( m_code );
if( iter != myMsgMap.end() )
{
return iter->second;
}
return "No error message found!";
}
public:
private:
const int m_severity;
const int m_code;

Change this to errorCode as well.
const static MsgMap myMsgMap;
};

Remember, the following lines need to be in a .cpp file somewhere, not
a header.
namespace
{
class TInitialise
{
public:
TException::MsgMap m_;
public:
operator TException::MsgMap() const { return m_; }
TInitialise() {};
TInitialise &Add() { std::cout << "in add\n"; return *this; }
};
}

const TException::MsgMap TException::myMsgMap = TInitialise()
.Add()
.Add();

int main()
{
return 0;
}

Cheers! --M
 
M

mlimber

This the first I see this syntax ?

const TException::MsgMap TException::myMsgMap = TInitialise()
.Add()
.Add();

Would you have a link where I can read about it ? (calls to the .Add()
function)

First, please put your responses below or inline the message you are
responding to. That's the custom here.

As for that syntax, see these two FAQs:

http://www.parashift.com/c++-faq-lite/references.html#faq-8.4
http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.17

At the moment, this site seems to be having problems. Here's a
(possibly out-dated) mirror:

http://geneura.ugr.es/~jmerelo/c++-faq/references.html#faq-8.4
http://geneura.ugr.es/~jmerelo/c++-faq/ctors.html#faq-10.17

Cheers! --M
 
M

mast2as

First, please put your responses below or inline the message you are
responding to. That's the custom here.

Here is a version of the code that works. Because i found it really
interesting (I quite a few new tricks) I thought I would post a
complete version that compiles and runs. Hopefully doing all the
changes you mentionned. I just removed const. Not sure it was really
necessary but this way i could make your first version of the code
work.

Thank you again your help -

#include <iostream>
#include <deque>
#include <map>
#include <exception>

class TException : public std::exception
{
public:

enum errorCode
{
kTest1 = 1,
kTest2 = 10,
kTest3 = 12,
kTest4 = 20
};

// error severity levels
//
// #define RIE_INFO 0
// #define RIE_WARNING 1
// #define RIE_ERROR 2
// #define RIE_SEVERE 3

enum severityLevel
{
kInfo = 0, // Rendering stats and other info
kWarning, // Something seems wrong, maybe okay
kError, // Problem. Results may be wrong
kSevere // So bad you should probably abort
};

// error handlers codes
//
// #define RIE_ERRORIGNORE 0
// #define RIE_ERRORPRINT 1
// #define RIE_ERRORABORT 2

enum handlerCode
{
kErrorIgnore = 0,
kErrorPrint,
kErrorAbort,
};

typedef std::map<TException::errorCode, const char*> MsgMap;

public:
TException( TException::errorCode code, TException::handlerCode
handler =
kErrorIgnore, TException::severityLevel severity = kInfo ) :
m_error( code ),
m_severity( severity ),
m_handler( handler )
{}

~TException() throw() {}

const char* what() const throw()
{
return myMsgMap[ m_error ];
}

private:
TException::errorCode m_error;
TException::handlerCode m_handler;
TException::severityLevel m_severity;

static MsgMap myMsgMap;
};

class MsgMapInitializer
{
TException::MsgMap m_;
public:
operator TException::MsgMap() const { return m_; }
MsgMapInitializer& Add( TException::errorCode error, const char * msg
)
{
m_[ error ] = msg;
return *this;
}
};

TException::MsgMap TException::myMsgMap = MsgMapInitializer()
.Add( TException::kTest1, "test1" )
.Add( TException::kTest2, "test2" )
.Add( TException::kTest3, "test3" );

int main()
{
try {
// throw something
throw( TException( TException::kTest1, TException::kErrorPrint,
TException::kWarning ) );
} catch( TException &e ) {
std::cout << e.what() << std::endl;
};
return 0;
}
 
M

mlimber

Here is a version of the code that works. Because i found it really
interesting (I quite a few new tricks) I thought I would post a
complete version that compiles and runs. Hopefully doing all the
changes you mentionned. I just removed const. Not sure it was really
necessary but this way i could make your first version of the code
work.

Thank you again your help -

You're welcome. More comments follow.
#include <iostream>
#include <deque>

Nit-pick: you don't use deque.
#include <map>
#include <exception>

class TException : public std::exception
{
public:

enum errorCode
{
kTest1 = 1,
kTest2 = 10,
kTest3 = 12,
kTest4 = 20
};

// error severity levels
//
// #define RIE_INFO 0
// #define RIE_WARNING 1
// #define RIE_ERROR 2
// #define RIE_SEVERE 3

enum severityLevel
{
kInfo = 0, // Rendering stats and other info
kWarning, // Something seems wrong, maybe okay
kError, // Problem. Results may be wrong
kSevere // So bad you should probably abort
};

// error handlers codes
//
// #define RIE_ERRORIGNORE 0
// #define RIE_ERRORPRINT 1
// #define RIE_ERRORABORT 2

enum handlerCode
{
kErrorIgnore = 0,
kErrorPrint,
kErrorAbort,
};

typedef std::map<TException::errorCode, const char*> MsgMap;

You don't need to qualify errorCode or other member types with the
current class name. This could more simply be:

typedef std::map said:

This "public" is redundant, though you might prefer it for aesthetic
reasons.
TException( TException::errorCode code, TException::handlerCode
handler =
kErrorIgnore, TException::severityLevel severity = kInfo ) :

Again, no need to qualify member types in this context.
m_error( code ),
m_severity( severity ),
m_handler( handler )
{}

~TException() throw() {}

This is unnecessary. The compiler will automatically generate the same
code for you, so you may as well delete it.
const char* what() const throw()
{
return myMsgMap[ m_error ];
}

This function is much inferior to the one I presented previously. Here
is mine again for reference:

virtual const char* what() const throw()
{
const MsgMap::const_iterator iter = myMsgMap.find( m_code );
if( iter != myMsgMap.end() )
{
return iter->second;
}
return "No error message found!";
}

The chief difference is this: yours could return a null pointer, while
mine cannot. Try this evil but perfectly legal code with your existing
class and see what happens:

throw TException( TException::errorCode( 17 ) );

When mine is called with an invalid m_code (enums aren't guaranteed to
prevent every error, after all), it returns "No error message found!".
When yours is called with the same parameter, it returns the result of
this expression (cf. http://www.sgi.com/tech/stl/Map.html#3):

(*((myMsgMap.insert(
MsgMap::value_type(k, MsgMap::data_type()))).first)).second

You'll note that it calls std::map's insert() member function, which is
non-const. This version of the function returns an iterator pointing to
the current element with the specified key if it already exists *but*
inserts a new key/data pair with the default-constructed data if it
doesn't already exist. (It also returns a bool indicating if the key
was found or not, but that is ignored here.) The default construction
of a pointer is a null pointer, and chances are your client assumes
std::exception::what() returns a non-null pointer, which is probably
immediately dereferenced, and bang! you're dead.

The proper way to prevent this is to declare myMsgMap as a const static
private:
TException::errorCode m_error;
TException::handlerCode m_handler;
TException::severityLevel m_severity;

static MsgMap myMsgMap;

These should all be const since you don't want them getting mutated by
any member functions (accidentally or on purpose!). See
<http://www.parashift.com/c++-faq-lite/const-correctness.html>. Note
that the *non-static* members are all implicitly made const within the
context of TException::what() because it is a const member function
(see FAQ 18.10), but you should still declare them const here since it
makes the code "easier to understand, track, and reason about" (Sutter
and Alexandrescu, _C++ Coding Standards_, Item 15) because the
programmer's intention is clear.

Moreover, a const member function does not affect the constness of
static data members such as myMsgMap. This means, obviously, that your
map is modifiable even in const functions. The proper solution that
avoids altering the map, which shouldn't change after start up, is to
keep myMsgMap const (as I had in my original code) but use the what()
function body given above since it only utilizes const member functions
of std::map.
};

class MsgMapInitializer
{
TException::MsgMap m_;
public:
operator TException::MsgMap() const { return m_; }
MsgMapInitializer& Add( TException::errorCode error, const char * msg
)
{
m_[ error ] = msg;
return *this;
}
};

TException::MsgMap TException::myMsgMap = MsgMapInitializer()
.Add( TException::kTest1, "test1" )
.Add( TException::kTest2, "test2" )
.Add( TException::kTest3, "test3" );

int main()
{
try {
// throw something
throw( TException( TException::kTest1, TException::kErrorPrint,
TException::kWarning ) );
} catch( TException &e ) {

Catch a const exception when possible (const-correctness again):

catch( const TException &e )

You could also catch a "const std::exception&" here instead since you
inherit from it and don't use any extra members from TException in the
exception handler.
std::cout << e.what() << std::endl;
};
return 0;
}

Cheers! --M
 
M

mast2as

-
You're welcome. More comments follow.

okay thank you i did the changes. It scares me a bit knowing that the
exception handling class is only a small percentage of the code that I
have written so far. So if you make so many remarks about that code, I
can only imagine how crappy is the rest of the code that I wrote ;-(

At least I hope I get this one almost right now...
 
M

mlimber

okay thank you i did the changes. It scares me a bit knowing that the
exception handling class is only a small percentage of the code that I
have written so far. So if you make so many remarks about that code, I
can only imagine how crappy is the rest of the code that I wrote ;-(

At least I hope I get this one almost right now...

Well, writing exception safe code is tricky even for gurus like those
who write the books. See these links:

http://www.gotw.ca/gotw/059.htm
http://www.gotw.ca/gotw/060.htm
http://www.research.att.com/~bs/3rd_safe0.html

Make sure you have good books that you have read. The three types we'd
recommend can be found here:

http://www.parashift.com/c++-faq-lite/how-to-learn-cpp.html#faq-28.4

And plenty of book reviews can be found here:

http://accu.org/

Cheers! --M
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,539
Members
45,024
Latest member
ARDU_PROgrammER

Latest Threads

Top