Odd Exception Behavior

N

none

Hi,

I have an exception class like so:

class CMyException
{
public:

// Streams for creating the message
std::eek:stringstream msg;
std::eek:stringstream short_msg;

CMyException() {}
CMyException(const CMyException &rhs) { copy(rhs); }
CMyException(const std::string simple_msg)
{ msg << simple_msg; short_msg << simple_msg; }
~CMyException() {}

CMyException &operator=(const CMyException &rhs) { copy(rhs); }

operator const char *() { return msg.str().c_str(); }

protected:

// std::eek:stringstream cannot be copied, so copy() must
// only copy the internal strings
void copy(const CMyException &rhs)
{
msg << (rhs.msg.str().empty() ?
rhs.short_msg.str() : rhs.msg.str());
short_msg << (rhs.short_msg.str().empty() ?
rhs.msg.str() : rhs.short_msg.str());
}
};


It's kind of a work in progress. The idea is that you can set either the
"msg" or the "short_msg" or both. You could use it like so:

if (parse_failed)
{
CMyException e;

// Write long message
e.msg << "Something bad happened on line " << line;
e.msg << " in file " << filename << ".";

// Write short message
e.short_msg << "Something bad happened.";

throw e;
}

Then, the "catcher" can decide which message to display:

try
{
parse(something);
}
catch (CMyException e)
{
// Display the long error message
ErrorMessage(e.msg.str().c_str());
}

For convenience, I added the (const char *) type-cast operator so that you
could also do this, assuming you want the longer message:

catch (CMyException e)
{
// Display the long error message
ErrorMessage(e);
}

But something very strange happens. My ErrorMessage() function displays
garbage when I use the type-cast operator. It works fine if I write
"ErrorMessage(e.msg.str().c_str())" but not if I write
"ErrorMessage(e)". I traced into the ErrorMessage() function in both
cases, and what I see is that the actual const char * pointer is off by
exactly 256 bytes in the latter case. I just don't see the difference
between the two!

I am using Visual Studio 2005, if that makes a difference.
 
N

none

none said:
But something very strange happens. My ErrorMessage() function
displays garbage when I use the type-cast operator. It works fine if
I write "ErrorMessage(e.msg.str().c_str())" but not if I write
"ErrorMessage(e)". I traced into the ErrorMessage() function in both
cases, and what I see is that the actual const char * pointer is off
by exactly 256 bytes in the latter case. I just don't see the
difference between the two!


AAAAAARRRRRGH

The str() function returns by *value*. So the type-cast function, as I
have it written, calls c_str() on a temporary that ceases to exist as soon
as the function returns.
 
A

Alf P. Steinbach

* none:
AAAAAARRRRRGH

The str() function returns by *value*. So the type-cast function, as I
have it written, calls c_str() on a temporary that ceases to exist as soon
as the function returns.

Yes. :)

You're absolutely not the first to walk into that, but perhaps one of the first
here to recognize it yourself -- at least, I can't remember it happen b4.

Some other tips for the code:

* "C"-prefix for a class is a Microsoft-ism, therefore (almost automatically)
ungood.

* The assignment operator implementaiton is a bit dangerous, think about
assigning twice to same exception object.

* The copy function seems to jumble up the short and long messages, anyway,
copy will not be perfect copy with the code as-is.

* The 'cast' operator should perhaps best be 'const'.

* Most importantly, the whole "stream whatever into a string" can be
/separated/ as a very very simple class. Then it can be used for whatever.
:)


Cheers & hth.,

- Alf
 
N

none

Alf said:
You're absolutely not the first to walk into that, but perhaps one of
the first here to recognize it yourself -- at least, I can't
remember it happen b4.

I get lucky, on occaision. :) Is there any particular reason why str()
doesn't return by reference? Or, at least, why there isn't an
alternative method available to do so?

Some other tips for the code:

* "C"-prefix for a class is a Microsoft-ism, therefore (almost
automatically)
ungood.

Guilty as charged. I've been using the "C" prefix since the early days
of Visual Studio.
* The assignment operator implementaiton is a bit dangerous, think
about
assigning twice to same exception object.

I should be checking for "&rhs == this"?
* The copy function seems to jumble up the short and long messages,
anyway,
copy will not be perfect copy with the code as-is.

Yes, the idea was that the "thrower" would not need to set both the
short and long messages. If only one is set, the other will just be a
duplicate.
* The 'cast' operator should perhaps best be 'const'.

Ah, good point.
* Most importantly, the whole "stream whatever into a string" can
be
/separated/ as a very very simple class. Then it can be used for
whatever.
:)

That's interesting. So I would basically be creating a "copyable
ostringstream."

Thanks a lot for the feedback.
 
A

Alf P. Steinbach

* none:
I get lucky, on occaision. :) Is there any particular reason why str()
doesn't return by reference? Or, at least, why there isn't an
alternative method available to do so?

It would require the stream to hold a string instance.

Thus it would conflict with a main guideline of the design of the C++ language,
that you don't have to pay for what you don't use.

Guilty as charged. I've been using the "C" prefix since the early days
of Visual Studio.


I should be checking for "&rhs == this"?

That too, if you choose to implement the assignment operator that way.

But with the code as presented you can accumulate text by repeated assignments.

A generally better way is to let the data members copy themselves. Then the
autoamtically generated assignment operator is good enough. I.e. store strings
instead of streams.

Yes, the idea was that the "thrower" would not need to set both the
short and long messages. If only one is set, the other will just be a
duplicate.


Ah, good point.


That's interesting. So I would basically be creating a "copyable
ostringstream."

Thanks a lot for the feedback.

I forgot to mention, unless this is meant as a "hard exception" class it should
preferably, directly or indirectly, derive from std::exception.

std::runtime_error is a generally good choice as base class for a custom
exception class.


Cheers & hth.,

- Alf
 
J

Jorgen Grahn

Alf P. Steinbach wrote: ....

Guilty as charged. I've been using the "C" prefix since the early days
of Visual Studio.

More generally: I see this class CFoo scheme in a lot of postings
here. Why do people use it, *really*?

Microsoft may have had some reason to use it back in the 1980s or so,
before namespaces, before C++ was widely adopted, before their
programmers knew the language well, and they may have to keep it for
backwards-compatible reasons.

But that doesn't imply that *everyone else* has to use it, in their
own code, for new classes, in 2010. It provides no information and is
just in the way.

Are there broken tools in Microsoft-land which require it?
Misinformation in popular books? What?

/Jorgen
 
B

Bo Persson

Jorgen said:
More generally: I see this class CFoo scheme in a lot of postings
here. Why do people use it, *really*?

Microsoft may have had some reason to use it back in the 1980s or
so, before namespaces, before C++ was widely adopted, before their
programmers knew the language well, and they may have to keep it for
backwards-compatible reasons.

But that doesn't imply that *everyone else* has to use it, in their
own code, for new classes, in 2010. It provides no information and
is just in the way.

Are there broken tools in Microsoft-land which require it?
Misinformation in popular books? What?

Just people following a bad example. "If the big guys at Microsoft do
it this way, of course it must be good."


Bo Persson
 
B

Branimir Maksimovic

Jorgen said:
More generally: I see this class CFoo scheme in a lot of postings
here. Why do people use it, *really*?

I beleive that C is hungarian notation. C stands for "class".

S is for struct, I guess?

Greets
 
T

tonydee

I see this class CFoo scheme in a lot of postings
here.  Why do people use it, *really*?
...
It provides no information and is just in the way.

It does provide information. The information just isn't of
significant utility, and doesn't belong there. The practice reduces
identifier readability and frustrates the evolution of code, such that
- for example - should CFoo need to be replaced with a namespace, a
programmer may leave the name as CFoo to avoid needing to change a
potentially large or even unreachable body of client code, after which
the 'C' is actively misleading.

Why does anyone do it? I guess because they've been exposed to it and
not worked on the scale of code where the downsides dominate....

Cheers,
Tony
 
J

Jorgen Grahn

I usually don't know or care if my types are structs or classes ...

[Coding Style Conventions]

It seems from that list that the main use of the 'C' is in
COM-infested code, where you don't want to confuse classes with COM
objects of various kinds.

(I hope this is an outdated document. I particularly strongly dislike
the file, class and method documentation headers they list further
down. They're the kind which are guaranteed to go out of sync with
reality, take up lots of screen space, and yet not say anything worth
knowing.)

/Jorgen
 
J

James Kanze

I usually don't know or care if my types are structs or classes ...
[Coding Style Conventions]
It seems from that list that the main use of the 'C' is in
COM-infested code, where you don't want to confuse classes
with COM objects of various kinds.
(I hope this is an outdated document. I particularly strongly
dislike the file, class and method documentation headers they
list further down. They're the kind which are guaranteed to
go out of sync with reality, take up lots of screen space, and
yet not say anything worth knowing.)

Note that the document doesn't say you should do it. It just
describes what is done in the samples. And in examples, there
is a (very very small) justification: names like Foo and Bar
don't give the slightest hint as to whether they're a class or a
namespace, for example. (Whereas if you need such prefixes in
production code, you should use better names.)
 
N

none

Jorgen said:
More generally: I see this class CFoo scheme in a lot of postings
here. Why do people use it, *really*?
....

Are there broken tools in Microsoft-land which require it?
Misinformation in popular books? What?


It's not that Microsoft's tools *require* it, it's that they *generate* it.
Lots of newbies get their start in C++ using Visual Studio. If you use any
of their predefined project types (like MFC dialog), Visual Studio will
generate lots of code to get you started, and all the classes will be named
like:

CMyFirstProgram
CMyFirstProgramApp
CMyFirstProgramDialog

.... so yo get roped into it and you barely even realize it. If you were
really determined, you could go through every line of the generated code
and remove the "C" prefixes, but that's not a trivial task for a newbie.
 

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,968
Messages
2,570,149
Members
46,695
Latest member
StanleyDri

Latest Threads

Top