Topic: Defect Report: Should be allowed to catch exceptions with ambiguous public base classes
Author: Francis Glassborow <francis.glassborow@ntlworld.com>
Date: 10 Sep 2001 15:23:25 GMT Raw View
In article <3B9C6326.A1EAE3C0@iobox.com>, Sergey P. Derevyago <non-
existent@iobox.com> writes
> My favorite TC++PL (by Bjarne Stroustrup) in 14.2.2 "Composite
>Exceptions"
>section contains the following code:
>
>class Netfile_err : public Network_err, public File_system_err { /* ... */
>};
>
>and the explanation why multiple inheritance is useful for EH. But the book
>doesn't suggest to use virtual inheritance (which is IMHO a conceptual
>flaw). So one can artlessly write something like this:
>
>class Exception { /* */ }; // the common exception class
>class Network_err : public Exception { /* */ };
>class File_system_err : public Exception { /* */ };
>class Netfile_err : public Network_err, public File_system_err { /* ... */
>};
>
>and try to catch(Exception&).
'Conceptual flaw'? 'Artlessly'? Does that mean that you think it should
not be done that way? In that case how is this an example, except as how
not to do it? Clearly, classes with Exception as a direct base should
use virtual inheritance.
Now can you give an example where an exception object 'needs' to have
two subobjects of the same type?
Francis Glassborow ACCU
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: iltchenko@yahoo.com (Andrei Iltchenko)
Date: Wed, 12 Sep 2001 17:23:58 GMT Raw View
"Sergey P. Derevyago" <non-existent@iobox.com> wrote in message news:<3B9C6326.A1EAE3C0@iobox.com>...
> So far I've got a constructive reply from Herb Sutter (thanks, Herb!) with
> the following questions. I'm going to propose my answers.
I you had been a bit more attentive, you would've noticed that so far
you had constructive feedback from at least 5 other people besides
Herb Sutter.
> > On the contrary, all of the responses you've got so far are serious, on-topic,
Hope you will now take note of the responses you get.
> class Exception { /* */ }; // the common exception class
> class Network_err : public Exception { /* */ };
> class File_system_err : public Exception { /* */ };
> class Netfile_err : public Network_err, public File_system_err { /* ... */
> };
>
> and try to catch(Exception&).
>
> > - What would you like the amended wording to be? Your proposal is incomplete
> > because you suggest changing the text but not how to change it.
> Unfortunately, my english prevents me from the giving of any "exact
> wording" :( But, in principle, I see two solutions:
> 1. Catch objects with ambiguous base subobjects by some of these
> subobjects.
> 2. Don't allow to throw such objects at all.
>
> > - In your example code, if you could catch an ambiguous object, which base
> > subobject do you want to be caught? Why that one and not the other one? Or
> > should be left to be implementation-defined?
> Implementation-defined, but see below.
>
> > - What do you want to do about the fact that you still have left exactly the
> > same "hole" in that the other base subobject that wasn't chosen is still
> > "leaked" the same way as before?
> Not exact :) There will be the slicing if you catch the object in question
> with catch(Base), but catch(Base&) clause catches the "whole" object.
Not exact :(, with 'catch(Base)' you will catch the exception thrown
and not the exception object in question. And in the handler the name
'Base' will refer to a copy of the exception object or to a copy of
the latter's anambiguous public base class sub-object by name 'Base'.
> But it seems like in current C++ you have no tools to inspect obect with
> ambiguous base sub-objects.
Again not exact -- it's not a sub-object that can be ambiguous but its
name.
> May be it's time for another Defect Report: IMHO
> RTTI should be improved in this respect.
Could be. Although, maybe it's high time for you to clear some things
up first?
Regards,
Andrei Iltchenko.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: joerg.barfurth@attglobal.net (Joerg Barfurth)
Date: Thu, 13 Sep 2001 00:58:46 GMT Raw View
Sergey P. Derevyago <non-existent@iobox.com> wrote:
> try {
> // ...
> }
> catch (Matherr& m) {
> // ...
> }
>=20
> he really wants to catch _all_ Matherrs rather than to allow some of th=
e
> Matherrs to escape:
>=20
> class SomeMatherr : public Matherr { /* */ };
> struct TrickyBase1 : public SomeMatherr { /* */ };
> struct TrickyBase2 : public SomeMatherr { /* */ };
> struct TrickyMatherr : public TrickyBase1, TrickyBase2 { /* */ };
>=20
> According to the standard TrickyMatherr will leak through the catch
> (Matherr& m) clause. For example:
> -----------------------------------8<----------------------------------=
-
The standard does not support implicit conversions to an ambiguous base
class anywhere. Why should exception handling be different ?
Note that the standard is wholly consistent here, even wrt exception
specifications.
What, in your opinion, will be the output of the following program ?
#include <exception>
#include <iostream>
#include "Sergey's MathErr classes"
using std::cerr;
void ambiguous()=20
{=20
cerr << "Unexpected: Not a Matherr";
std::terminate();
}
void raiseMathErr() throw (Matherr)
{
throw SomeMatherr();
}
int main()
{
std::set_unexpected( ambiguous );
try
{
raiseMathErr()
}
catch(MathErr&)
{
cerr << "Expected: A Matherr is caught";
}
catch(...&)
{
cerr << "What ?? A Matherr escaped";
}
}
The answer is, that nothing that really IS A Matherr will and can
escape.
Ambiguous public base classes are an unfortunate artifact of the C++
type system. They do not represent IS A (or should this be IS-TWO here
?) and are not treated as such.
Regards, Joerg
--=20
J=F6rg Barfurth joerg.barfurth@attglobal.net
<<<<<<<<<<<<< using std::disclaimer; <<<<<<<<<<<<<<<<<<<<<<<<<<<<
Software Developer http://www.OpenOffice.org
StarOffice Configuration http://www.sun.com/staroffice
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "John D. Hickin" <hickin@cam.org>
Date: Tue, 4 Sep 2001 23:44:05 GMT Raw View
Let me try to kill two birds with one stone.
First, with the current behavior, it is always possible to avoid what Sergey
calls an exception leak by catching the most derived class. If exception
catching were made to work as he desires you cannot avoid leaks unless you
catch the most derived class. If, in the example, a TrickyMatherr is caught
as a Matherr&, to which Matherr part does *this refer? It can only refer to
one part; the other part _leaks_ unless one resorts to a dynamic_cast to
locate it, which is equivalent, but less nice, IMO, to catching the most
derived class. Calling a virtual function on the exception instance also
results in somewhat indeterminate results uless you override in the most
derived class.
And now to Francis' point. One might take advantage of the behavior that
Sergey dislikes by making all (or perhaps, some selected) exception types
inherit as does TrickyMatherr. When a new Matherr exception type is added to
the hierarchy you can locate your catch statements where the new exception
should be explicitly handled through the following device:
catch ( Matherr& ex ) { throw UntreatedException( typeid(ex).name ); }
...
Regards, John.
----- Original Message -----
From: "Francis Glassborow" <francis.glassborow@ntlworld.com>
> In article <3B9486C5.3394A15C@iobox.com>, Sergey P. Derevyago <non-
> existent@iobox.com> writes
> >he really wants to catch _all_ Matherrs rather than to allow some of the
> >Matherrs to escape:
> >
> > class SomeMatherr : public Matherr { /* */ };
> > struct TrickyBase1 : public SomeMatherr { /* */ };
> > struct TrickyBase2 : public SomeMatherr { /* */ };
> > struct TrickyMatherr : public TrickyBase1, TrickyBase2 { /* */ };
> >
> >According to the standard TrickyMatherr will leak through the catch
> >(Matherr& m) clause. For example:
>
> I would like to see some real code where a programmer would want to have
> such a hierarchy. I really find it hard to conceive of an exception
> hierarchy that includes multiple inheritance. Maybe it is my limited
> imagination.
>
>
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Andrei Iltchenko" <iltchenko@yahoo.com>
Date: Wed, 5 Sep 2001 16:44:43 GMT Raw View
"Sergey P. Derevyago" <non-existent@iobox.com> wrote in message
news:3B9486C5.3394A15C@iobox.com...
> Section: 15.3 Handling an exception [except.handle]
> Submitter: Sergey P. Derevyago
>
> p.3 contains the following text:
> A handler is a match for a throw-expression with an object of type E if
> - The handler is of type cv T or cv T& and E and T are the same type
> (ignoring the top-level cv- qualifiers), or
> - the handler is of type cv T or cv T& and T is an unambiguous public
base
> class of E, or
> - the handler is of type cv1 T* cv2 and E is a pointer type that can be
> converted to the type of the handler by either or both of
> - a standard pointer conversion (4.10) not involving conversions to
> pointers to private or protected or ambiguous classes
> - a qualification conversion
>
> I propose to alter this text to allow to catch exceptions with ambiguous
> public base classes by some of the public subobjects.
This would in my opinion be just awful. How would the implementation
initialize the parameter of the catch clause of type 'Matherr&' if the
exception object has type 'TrickyMatherr'? This is ambiguous -- there are
two subobjects -- which one shall the parameter 'm' refer to?
Or did you mean that this should only be allowed under special
circumstances, like when the subobject duplicated has no non-static data
members and the implementation opted to allocate no storage for it? If so,
then it's also a bad idea not only because this would make yet another
special case but also because this would compilcate the run-time checks that
is carried out when an exception is thrown, which are already compilcated to
begin with.
> I'm really sure that
> if someone writes:
>
> try {
> // ...
> }
> catch (Matherr& m) {
> // ...
> }
>
> he really wants to catch _all_ Matherrs rather than to allow some of the
One would expect that the handler would catch only exceptions of types that
are publicly and unambiguously derived from 'Matherr'. The person who
expects otherwise is likely to expect a lot of other illigal constructs to
work. You do not propose the 'dynamic_cast' operator to be able to convert
to public ambiguous base classes, do you?
> Also I see one more possible solution: to forbid objects with ambiguous
> base classes to be "exceptional objects" (for example Borland C++ goes
this
> way) but it seems to be unnecessary restrictive.
This one is much better than that of yours for it incurs no extra overhead
and might remove some of the burden inherent to EH implementations. But I do
not regard your findings as a problem in the first place.
Regards,
Andrei Iltchenko.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Andrei Iltchenko" <iltchenko@yahoo.com>
Date: Wed, 5 Sep 2001 19:22:59 GMT Raw View
"Sergey P. Derevyago" <non-existent@iobox.com> wrote in message
news:3B965ECD.1881A72E@iobox.com...
> Andrei Iltchenko wrote:
>
> > How would the implementation
> > initialize the parameter of the catch clause of type 'Matherr&' if the
> > exception object has type 'TrickyMatherr'? This is ambiguous -- there
are
> > two subobjects -- which one shall the parameter 'm' refer to?
> My answer is: it refers to some of the subobjects. Yes it's ambiguous but
> it works.
How would it work? If I access a data member of the duplicated suobject,
what shall the result be? Don't you see that this will introduce some
obscure erroneous semantics to an already fairly complicated language.
Besides you seem to've ignored my comments on the added complexity to the
run time EH check that your idea would incure.
> Hmm. In TC++PL 14.2 "Grouping of Exceptions" you can find:
> -----------------------------------8<-----------------------------------
> void f()
> {
> try {
> // ...
> }
> catch (Overflow) {
> // handle Overflow or anything derived from Overflow
> }
> catch (Matherr) {
> // handle any Matherr that is not Overflow
> }
> }
>
> Here, an Overflow is handled specifically. All other Matherr exceptions
> will be handled by the general case.
> -----------------------------------8<-----------------------------------
> Do you really think Bjarne don't know C++? ;)
This has nothing to do with Bjarne knowing C++. The document that the
language is based upon is ISO/IEC 14882 and your references should be to it,
otherwise they do not belong to comp.std.c++.
Just for the sake of your curiosity, here is what Bjarne wrote on this news
group when he had mistakenly answered a question and other people corrected
him:
> On Thu, 7 Jun 2001, Bjarne Stroustrup wrote:
> ...
> Oops. Of course. Most embarrasing. Sorry. Thanks for the correction.
>
> In the context of TC++PL, that means that 16.1.2 can be misleading.
> 9.2.2 (referred to by 16.1.2) gets it right.
>
> Someone took the opportunity to lecture on the topic of only the standard
> being authoritative (and not I). Please note that I have never, after the
> formation of the C++ standards committee, claimed that my writings take
> priority over the standard drafts.
>
> I refer to TC++PL because it is more accessible that the standard and has
> a far wider distribution. I would hope that most readers of comp.std.c++
> have access to the standard and are familiar enough with it to benefit
from
> it, but most people don't and can't.
> > > Also I see one more possible solution: to forbid objects with
ambiguous
> > > base classes to be "exceptional objects" (for example Borland C++ goes
> > > this way) but it seems to be unnecessary restrictive.
> > This one is much better than that of yours for it incurs no extra
overhead
> > and might remove some of the burden inherent to EH implementations. But
I do
> > not regard your findings as a problem in the first place.
> In some kinds of library code it _can_ be the problem in the first place.
Then I would question the design of such a library. Anyway that's now up to
the Committee to decide if there is an issue, but IMO what you've brought
out is most likely to end up under the "Not A Defect" status.
Cheers,
Andrei Iltchenko.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Andrei Iltchenko" <iltchenko@yahoo.com>
Date: Thu, 6 Sep 2001 22:43:25 GMT Raw View
"Sergey P. Derevyago" <non-existent@iobox.com> wrote in message
news:3B972238.5B439F4B@iobox.com...
> Did you take a look at 15.3 of ISO/IEC 14882? If not than it's time to do
> it: it contains the same example.
> -----------------------------------8<-----------------------------------
> 4 [Example:
>
> class Matherr { /* ... */ virtual vf(); };
> class Overflow: public Matherr { /* ... */ };
> class Underflow: public Matherr { /* ... */ };
> class Zerodivide: public Matherr { /* ... */ };
>
> void f()
> {
> try {
> g();
> }
> catch (Overflow oo) {
> // ...
> }
> catch (Matherr mm) {
> // ...
> }
> }
You should've started off by giving a reference to this place in the IS, the
text after the example is indeed misleading. If you had stated that in your
original post and pointed out that the comment after the example was wrong,
I'd have agreed.
> > Just for the sake of your curiosity, here is what Bjarne wrote on this
news
> > group when he had mistakenly answered a question and other people
corrected
> > him:
> Thanks, but I read it at the time.
>
> > the Committee to decide if there is an issue, but IMO what you've
brought
> > out is most likely to end up under the "Not A Defect" status.
> Oh, you've broken my heart! :)
Watch it!!!
> The intent of this subject is to show the
> C++ community that catch(Base) idiom doesn't work as people used to
> suppose. IMHO C++ has the real flaw with this issue and the flaw should be
> adressed.
The only thing that I see is a wrong comment in the IS after the example in
15.3/4, nothing more. And what should be addressed is that inconsistency and
not what you brough up in your original posting. I stand by everything I
said in my previous posts.
> PS Besides you seem to've ignored my comment on the dynamic_cast and
> ambiguous base classes. How can we tell apart whether the class has
> ambiguous base class or doesn't have it at all?
I just didn't take it seriously :)
Regards,
Andrei Iltchenko.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Richard Smith" <richard@ex-parrot.com>
Date: Fri, 7 Sep 2001 18:37:36 GMT Raw View
"Sergey P. Derevyago" <non-existent@iobox.com> wrote in message
news:3B9486C5.3394A15C@iobox.com...
> #include <stdio.h>
>
> struct B {};
> struct B1 : B {};
> struct B2 : B {};
> struct D : B1, B2 {}; // D() has two B() subobjects
>
> void f() { throw D(); }
>
> int main()
> {
> try { f(); }
> catch (B& b) { puts("B&"); } // passed
> catch (D& d) { puts("D&"); } // really works _after_ B&!!!
> }
If you are really concerned that these semantics could cause problems for
the programmer, then perhaps you should look not at changing the semantics
of catch but of throw. For example, a compiler could perfectly reasonably
give a compile-time warning that you are throwing an object with an
ambiguous base class. I don't really think that this should be a language
issue (i.e. I don't think that it should be illegal to throw such objects),
but it is a perfectly reasonable QoI issue.
--
Richard Smith
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Sergey P. Derevyago" <non-existent@iobox.com>
Date: 4 Sep 2001 15:47:27 GMT Raw View
Section: 15.3 Handling an exception [except.handle]
Submitter: Sergey P. Derevyago
p.3 contains the following text:
A handler is a match for a throw-expression with an object of type E if
- The handler is of type cv T or cv T& and E and T are the same type
(ignoring the top-level cv- qualifiers), or
- the handler is of type cv T or cv T& and T is an unambiguous public base
class of E, or
- the handler is of type cv1 T* cv2 and E is a pointer type that can be
converted to the type of the handler by either or both of
- a standard pointer conversion (4.10) not involving conversions to
pointers to private or protected or ambiguous classes
- a qualification conversion
I propose to alter this text to allow to catch exceptions with ambiguous
public base classes by some of the public subobjects. I'm really sure that
if someone writes:
try {
// ...
}
catch (Matherr& m) {
// ...
}
he really wants to catch _all_ Matherrs rather than to allow some of the
Matherrs to escape:
class SomeMatherr : public Matherr { /* */ };
struct TrickyBase1 : public SomeMatherr { /* */ };
struct TrickyBase2 : public SomeMatherr { /* */ };
struct TrickyMatherr : public TrickyBase1, TrickyBase2 { /* */ };
According to the standard TrickyMatherr will leak through the catch
(Matherr& m) clause. For example:
-----------------------------------8<-----------------------------------
#include <stdio.h>
struct B {};
struct B1 : B {};
struct B2 : B {};
struct D : B1, B2 {}; // D() has two B() subobjects
void f() { throw D(); }
int main()
{
try { f(); }
catch (B& b) { puts("B&"); } // passed
catch (D& d) { puts("D&"); } // really works _after_ B&!!!
}
-----------------------------------8<-----------------------------------
Also I see one more possible solution: to forbid objects with ambiguous
base classes to be "exceptional objects" (for example Borland C++ goes this
way) but it seems to be unnecessary restrictive.
--
With all respect, Sergey. http://cpp3.virtualave.net/
mailto : ders at skeptik.net
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: Francis Glassborow <francis.glassborow@ntlworld.com>
Date: Tue, 4 Sep 2001 16:57:23 GMT Raw View
In article <3B9486C5.3394A15C@iobox.com>, Sergey P. Derevyago <non-
existent@iobox.com> writes
>he really wants to catch _all_ Matherrs rather than to allow some of the
>Matherrs to escape:
>
> class SomeMatherr : public Matherr { /* */ };
> struct TrickyBase1 : public SomeMatherr { /* */ };
> struct TrickyBase2 : public SomeMatherr { /* */ };
> struct TrickyMatherr : public TrickyBase1, TrickyBase2 { /* */ };
>
>According to the standard TrickyMatherr will leak through the catch
>(Matherr& m) clause. For example:
I would like to see some real code where a programmer would want to have
such a hierarchy. I really find it hard to conceive of an exception
hierarchy that includes multiple inheritance. Maybe it is my limited
imagination.
Francis Glassborow ACCU
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]