need virtual behavior for friend operator<<()

N

noone

hi.

I don't use exceptions much in the embedded world, but for my plugin
interface to a hardware MPEG encoder I'd like to, since there are so many
places that the crummy kernel driver can do bad things to my userland
program. I hope this news client doesn't reformat my example below and
make it impossible to read. :^)

consider the following exception heirarchy

// ----------------------------

struct base {
string message;
base(const char* mess): message(mess) {}
friend ostream& operator<<(ostream& s, base& r) {
s << r.message;
return s;
}
virtual const char* is_a() { return "base"; }
};

// ------------------------

struct derived: public base {
string method;
derived(const char* m, const char* f=""):
base(m), method(f) {}
const char* is_a() { return "derived"; }
friend ostream& operator<<(ostream& s, derived& r) {
s << r.message;
if (r.method.length()) s << " in " << r.method;
return s;
};


int main() {
try {
throw derived("in try block","main()");
}
catch(base& b)
{ cout << b.is_a() << " " << b << endl; };
// previous line does b.is_a() virtual correctly
// but uses friend operator<<() from base instead of
// from derived

return 0;
}


I want a generic catch block that catches the superclass of my exceptions
and correctly binds to the class that really threw the exception. I know
that I could make a virtual output() method for each derived class and
output << b.output() but is there an elegant way to do this without
adding another virtual into each derived class?

in the past I've tried overloading the redirect operators within the
classes but never had any luck. I've only ever been able to make them
work well as friend methods and I don't believe a virtual friend would
exist...Ever have a virtual friend as a kid?

I hope I'm clearly explaining, and I anticipate some interesting
comments on this one. :^)
 
N

noone

I was afraid of this...the blasted news client joined some lines.

I'll try to fix them below
 
M

Michael

noone said:
consider the following exception heirarchy

// ----------------------------

struct base {
string message;
base(const char* mess): message(mess) {}
friend ostream& operator<<(ostream& s, base& r) {
s << r.message;
return s;
}
virtual const char* is_a() { return "base"; }
};

// ------------------------

struct derived: public base {
string method;
derived(const char* m, const char* f=""):
base(m), method(f) {}
const char* is_a() { return "derived"; }
friend ostream& operator<<(ostream& s, derived& r) {
s << r.message;
if (r.method.length()) s << " in " << r.method;
return s;
};


int main() {
try {
throw derived("in try block","main()");
}
catch(base& b)
{ cout << b.is_a() << " " << b << endl; };
// previous line does b.is_a() virtual correctly
// but uses friend operator<<() from base instead of
// from derived

return 0;
}


I want a generic catch block that catches the superclass of my exceptions
and correctly binds to the class that really threw the exception. I know
that I could make a virtual output() method for each derived class and
output << b.output() but is there an elegant way to do this without
adding another virtual into each derived class?

in the past I've tried overloading the redirect operators within the
classes but never had any luck. I've only ever been able to make them
work well as friend methods and I don't believe a virtual friend would
exist...Ever have a virtual friend as a kid?

Here's how I've solved this in the past:

1) Create a virtual method 'output,' as you suggested
virtual ostream& output(ostream& s) const;

I forget whether I had to make it public, or could make it protected.

2) Implement it for each of the classes.

3) For your base class (only), implement the << operator, in terms of
output:
friend ostream& operator<<(ostream& s, const base& b) {
return b.output(s);
};

Then any/all clients of the class can just do something like:
cout << my_instance;

It seems a little yucky, but the yuckiness is localized to your
exception hierarchy, the code that uses it is clean.

Michael
 
N

noone

Here's how I've solved this in the past:

1) Create a virtual method 'output,' as you suggested virtual ostream&
output(ostream& s) const;

Yes. I was hoping to avoid this but you are confirming my suspicions
I forget whether I had to make it public, or could make it protected.

I'm a bad boy. I generally make things public unless an overwhelming
reason not to. :^)

It seems a little yucky, but the yuckiness is localized to your exception
hierarchy, the code that uses it is clean.

Yeah...yucky, but if it works and is maintainable then it's OK by me. I
just had to see if someone could think of something more elegant before I
go that route in the morning.

thanks
 
G

Greg

noone said:
Yes. I was hoping to avoid this but you are confirming my suspicions


Yeah...yucky, but if it works and is maintainable then it's OK by me. I
just had to see if someone could think of something more elegant before I
go that route in the morning.

The best way to deal with an over-engineered solution is not to look
for ways to under-engineer its integration into the program.
Instead, I would recommend adopting a solution more in line with the
scope of the problem that needs to be solved.

Specifically - and I feel that I must square with you - there is no
such thing as an "exception hierarchy." There is no literature that
describes "exception hierarchies", there are no experts who study them
for a living, no journals dedicated to their research - in short,
nothing.

Nor is there much value in thinking about exceptions as a "hierarchy"
in the first place. In fact, few mental models could be any less
accurate. In reality exceptions are nearly all identical - only the
particulars of error itself vary. Therefore I think it would be more
productive (and a lot less work) to replace the elaborate exception
class hiearchy with a single exception type. And have this type simply
store the exception's useful information - which could include:

1. an exception id to uniquely identify the error (int)
2. description as a string (for diagnostic messages)
3. point at which exception was thrown (filename, line number)
4. other exception-specific information

After all, the goal of this work should be for the program make the
best use out of C++ expections. The goal should not be how to take an
elaborate class hiearachy that happens to be lying around, stick it
into a program so that a) it will do something useful and b) won't
require a lot of work to get to point a).

Or to put it another way: the focus should never be on finishing the
solution - the focus should always be on eliminating the problem.

Greg
 
N

noone

Specifically - and I feel that I must square with you - there is no such
thing as an "exception hierarchy." There is no literature that describes
"exception hierarchies", there are no experts who study them for a living,
no journals dedicated to their research - in short, nothing.
Nor is there much value in thinking about exceptions as a "hierarchy" in
the first place. In fact, few mental models could be any less accurate. In
reality exceptions are nearly all identical - only the particulars of
error itself vary. Therefore I think it would be more productive (and a
lot less work) to replace the elaborate exception class hiearchy with a
single exception type. And have this type simply store the exception's
useful information - which could include:

1. an exception id to uniquely identify the error (int) 2.
description as a string (for diagnostic messages) 3. point at which
exception was thrown (filename, line number) 4. other
exception-specific information

After all, the goal of this work should be for the program make the best
use out of C++ expections. The goal should not be how to take an elaborate
class hiearachy that happens to be lying around, stick it into a program
so that a) it will do something useful and b) won't require a lot of work
to get to point a).


Greg.

I suggest getting a copy of "The C++ Programming Language" by the
creator of the language Bjarne Stroustrup and reading the section on
exceptions thoroughly before writing such things, especially given the
authoritative tone that seems to come across in what is written above.

The original post was to address the capabilities of virtuals vs friends.
Exceptions just happened to be the real-world example case I chose to
display the current and preferred behavior.

Happy hacking!
Rob
 

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,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top