Topic: Defect Report: Should be allowed to catch exceptions with ambiguous


Author: "Sergey P. Derevyago" <non-existent@iobox.com>
Date: Mon, 10 Sep 2001 10:13:52 GMT
Raw View
So far I've got a constructive reply from Herb Sutter (thanks, Herb!) with
the following questions. I'm going to propose my answers.

> On the contrary, all of the responses you've got so far are serious, on-topic,
> and raise very good questions, such as:
>
> - Can you help by providing example code where you might have encountered this
> in real-world use? That would show this is not just a theoretical problem;
> every language always has lots of theoretical problems not worth pursuing, and
> the ones that bite programmers in real life are the ones worth pursuing.
 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&).

> - 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. But
it seems like in current C++ you have no tools to inspect obect with
ambiguous base subobjects. May be it's time for another Defect Report: IMHO
RTTI should be improved in this respect.
--
         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: "Sergey P. Derevyago" <non-existent@iobox.com>
Date: Mon, 10 Sep 2001 17:57:13 GMT
Raw View
[2mods: pls discard my previous message, it contains a bug :)]

Francis Glassborow wrote:
> >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?
        No, it doesn't.

> In that case how is this an example, except as how not to do it?
        Well, we have the "similar" examples in current C++:

const double pi=3.14;

void f()
{
 pi=3;  // to simplify our calculations ;)
 // ...
}

but you can't do this in C++: it would be "artlessly" :) Every "calculator"
shouldn't modify the pi value but compilator will try to catch such
situations anyway.

> Clearly, classes with Exception as a direct base should
> use virtual inheritance.
        From the most of C++ tutorials you can't (easily) fetch this
information. Even the standard contains incorrect comments in this respect.
Should the Matherr from 15.3.p4 be a virtual base class? I think it should,
and the standard has to clearly explain why it's so important to use
virtual inheritance in exceptional hierarchies.

> Now can you give an example where an exception object 'needs' to have
> two subobjects of the same type?
        Yes it's a bizzare situation, but think at the moment that one uses
EH to return objects from a "very recursive algorithm". Also a malicious
programmer can deliberately throw tricky objects. I think you've got two
"real situations" :)
--
         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: "Sergey P. Derevyago" <non-existent@iobox.com>
Date: Mon, 10 Sep 2001 20:38:28 GMT
Raw View
Francis Glassborow wrote:
> >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?
 Yes, it does.

> In that case how is this an example, except as how not to do it?
 Well, we have the "similar" examples in current C++:

const double pi=3.14;

void f()
{
 pi=3;  // to simplify our calculations ;)
 // ...
}

but you can't do this in C++: it would be "artlessly" :) Every "calculator"
shouldn't modify the pi value but compilator will try to catch such
situations anyway.

> Clearly, classes with Exception as a direct base should
> use virtual inheritance.
 From the most of C++ tutorials you can't (easily) fetch this information.
Even the standard contains incorrect comments in this respect. Should the
Matherr from 15.3.p4 be a virtual base class? I think it should, and the
standard has to clearly explain why it's so important to use virtual
inheritance in exceptional hierarchies.

> Now can you give an example where an exception object 'needs' to have
> two subobjects of the same type?
 Yes it's a bizzare situation, but think at the moment that one uses EH to
return objects from a "very recursive algorithm". Also a malicious
programmer can deliberately throw tricky objects. I think you've got two
"real situations" :)
--
         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: Valentin.Bonnard@free.fr (Valentin Bonnard)
Date: Mon, 10 Sep 2001 22:31:45 GMT
Raw View
"Sergey P. Derevyago"  wrote:

> Francis Glassborow wrote:
>> >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?
>         No, it doesn't.
>
>> In that case how is this an example, except as how not to do it?
>         Well, we have the "similar" examples in current C++:

I don't see how it is similar.

> but you can't do this in C++: it would be "artlessly"

I didn't knew that word.

>> Clearly, classes with Exception as a direct base should
>> use virtual inheritance.
>         From the most of C++ tutorials you can't (easily) fetch this
> information.

Throw these tutorials away.

> Even the standard contains incorrect comments in this respect.

The comment is too broad. That doesn't mean _anything_.

> Should the Matherr from 15.3.p4 be a virtual base class? I think it should,

So do I.

> and the standard has to clearly explain why it's so important to use
> virtual inheritance in exceptional hierarchies.

No, the standard isn't a tutorial. It doesn't have to clearly
express anything. OTOH, it doesn't have to make misleading or
incorrect comments. The comment is, at least, misleading and
should be corrected (but it isn't a top-priority IMO).

>> Now can you give an example where an exception object 'needs' to have
>> two subobjects of the same type?
>         Yes it's a bizzare situation, but think at the moment that one uses
> EH to return objects from a "very recursive algorithm".

Not a good idea, at least from the efficiency point of view.

> Also a malicious
> programmer can deliberately throw tricky objects.

So what   ? Do you believe that C++ should do something about
malicious programmers   ?

  --   VB

---
[ 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: Thu, 13 Sep 2001 16:43:01 GMT
Raw View
Joerg Barfurth wrote:
> The standard does not support implicit conversions to an ambiguous base
> class anywhere. Why should exception handling be different ?
 IMHO EH _is_ different from any other "goto construct". So some
"exceptional rules" for EH seem to be quite reasonable.

> Note that the standard is wholly consistent here, even wrt exception
> specifications.
 But it doesn't mean that the standard has got this things right :)

> What, in your opinion, will be the output of the following program ?
 You can consult with 15.4p7 :) As for me, current exception specifications
is a dead-born feature.

> The answer is, that nothing that really IS A Matherr will and can
> escape.
 "IS A" is too complex issue to get it right in C++ :) I can easily chose
the appropriate subobject (static_cast<TrickyBase1&> or
static_cast<TrickyBase2&>), cast it to Matherr and say: that's it!

> Ambiguous public base classes are an unfortunate artifact of the C++
> type system.
 In some situations they seem to be a quite natural approach.
--
         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: "Sergey P. Derevyago" <non-existent@iobox.com>
Date: Wed, 5 Sep 2001 09:09:10 GMT
Raw View
Francis Glassborow wrote:
> 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.
 IMHO it doesn't matter :) Someone _can_ do it and I have _no_ chance to
catch such Matherrs as Matherr&. BTW one may think that "Zerodivide leads
to Overflow" and "simply" express this as: class ZerodivideOverflow :
public Zerodivide, public Overflow { /* */ };

> I really find it hard to conceive of an exception
> hierarchy that includes multiple inheritance.
 I suggest to take a look at TC++PL 14.2.2 Composite Exceptions for some
ideas:

class Netfile_err : public Network_err, public File_system_err { /* ... */
};

> Maybe it is my limited imagination.
--
         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: "Sergey P. Derevyago" <non-existent@iobox.com>
Date: Wed, 5 Sep 2001 09:09:40 GMT
Raw View
"John D. Hickin" wrote:
> First, with the current behavior, it is always possible to avoid what Sergey
> calls an exception leak by catching the most derived class.
 I'm afraid, there is no such thing as "the most derived class" :) One can
always take TheMostDerivedClass and do the same thing:

 struct TrickyBase1 : public TheMostDerivedClass { /* */ };
 struct TrickyBase2 : public TheMostDerivedClass { /* */ };
 struct Oops : TrickyBase1, TrickyBase2 { /* */ };

It's the _real_ problem if you write a library code.

> 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?
 Undefined :) But you've caught it and can (try to) inspect with RTTI.
--
         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: "Sergey P. Derevyago" <non-existent@iobox.com>
Date: Wed, 5 Sep 2001 17:31:26 GMT
Raw View
Andrei Iltchenko wrote:
> > 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.
 IMHO it's much more awful to allow them to leak :)

> 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.

> > 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.
 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++? ;)

 Yes, in 14.3 "Catching Exceptions" he wrote: "[2] if H is an unambiguous
public base of E" but he didn't give any ideas what to do if "H is an
ambiguous public base of E". In fact, I don't know what to do either: you
can use catch(...) but it's not useful.

> You do not propose the 'dynamic_cast' operator to be able to convert
> to public ambiguous base classes, do you?
 Why not? How can you cast to _ambiguous_ base class in current C++? Are
there any ideas?

> > 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.
--
         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: "Sergey P. Derevyago" <non-existent@iobox.com>
Date: Thu, 6 Sep 2001 17:40:30 GMT
Raw View
Andrei Iltchenko wrote:
> > > 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.
 No I don't. Could you point out this "obscure erroneous semantics"?

 In particular, you can deal with ambiguous base classes but you have to
know at what point ambiguity disappears:

 struct B {};
 struct B1 : B {};
 struct B2 : B {};
 struct D : B1, B2 {};

 D* d=new D;
 B* b=d;                     // error
 B* b1=static_cast<B1*>(d);  // ok: left subobject
 B* b2=static_cast<B2*>(d);  // ok: right subobject

But in the case of user-level derived exception you can't know at what
point the ambiguity disappears.

> Besides you seem to've ignored my comments on the added complexity to the
> run time EH check that your idea would incure.
 This "added complexity" will give some programmers a chance to survive :)
So I think it's necessary.

> > 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++.
 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) {
                // ...
           }
     }


Here, the Overflow handler will catch exceptions of type Overflow and the
Matherr handler will catch exceptions of type Matherr and of all types
publicly derived from Matherr including exceptions of type Underflow and
Zerodivide. ]
-----------------------------------8<-----------------------------------
Do you think the comment above is quite correct? As for me, I don't see any
mention of ambiguity.

> 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! :) 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.

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?
--
         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                ]