Topic: Do exception classes still need no-fail copy constructors, in C++0x?


Author: Howard Hinnant <howard.hinnant@gmail.com>
Date: Sat, 28 Jul 2007 08:46:43 CST
Raw View
In article <46aa4363$0$236$e4fe514c@news.xs4all.nl>,
 "Niels Dekker - no return address" <invalid@noreply.com> wrote:

> The question remains: should the copy constructor of an exception class
> still be non-throwing, in C++0x?  (In order to avoid std::terminate)  I
> don't think so, because providing a non-throwing move constructor would be
> good enough.  (And actually, this appears to be confirmed by Howard's test
> program.)  Which would be great, because writing non-throwing copy
> constructors is quite painful...  (Reference counting, I know!)

It is possible someone might want to "preallocate" exceptions and then
throw copies of that preallocated set:

my_exception e;

void foo()
{
    if (something_goes_wrong())
       throw e;  // copies e
}

and that would require a copy.  Otoh, if you don't want to support such
uses, I *suspect* you can get away with a throw'ing copy ctor in C++0X.
I should really let the core guys take over here though...

-Howard

---
[ 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.comeaucomputing.com/csc/faq.html                      ]





Author: noreply@this.is.invalid (Niels Dekker - no return address)
Date: Sun, 22 Jul 2007 18:14:25 GMT
Raw View
When writing a custom exception class in C++03, it's recommended to have
its copy constructor supporting the no-fail guarantee, to prevent the
exception handling mechanism from calling std::terminate().  [E.g., Herb
Sutter & Andrei Alexandrescu - C++ Coding Standards, item 32]  Is that
recommendation still relevant in C++0x?  I got the impressing that in
C++0x, it's good enough to just make sure that the exception class has a
non-failing move constructor.

Should an STL implementation still take care of proving non-failing copy
constructors for exception classes like std::runtime_error, in C++0x?
Or would it be sufficient to provide non-failing move constructors for
these classes?

--
Niels Dekker
http://www.xs4all.nl/~nd/dekkerware
Scientific programmer at LKEB, Leiden University Medical Center

---
[ 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.comeaucomputing.com/csc/faq.html                      ]





Author: howard.hinnant@gmail.com (Howard Hinnant)
Date: Mon, 23 Jul 2007 16:13:42 GMT
Raw View
In article <46A1E5DD.8F11AD8C@this.is.invalid>,
 noreply@this.is.invalid (Niels Dekker - no return address) wrote:

> When writing a custom exception class in C++03, it's recommended to have
> its copy constructor supporting the no-fail guarantee, to prevent the
> exception handling mechanism from calling std::terminate().  [E.g., Herb
> Sutter & Andrei Alexandrescu - C++ Coding Standards, item 32]  Is that
> recommendation still relevant in C++0x?  I got the impressing that in
> C++0x, it's good enough to just make sure that the exception class has a
> non-failing move constructor.
>
> Should an STL implementation still take care of proving non-failing copy
> constructors for exception classes like std::runtime_error, in C++0x?
> Or would it be sufficient to provide non-failing move constructors for
> these classes?

The following program in conceptgcc:

#include <iostream>

class A
{
    A(const A&);
    A& operator=(const A&);
public:
    A() {}
    A(A&&) {}
};

void f()
{
    A a;
    throw a;
}

int main()
{
    try
    {
        f();
    }
    catch (A&)
    {
        std::cout << "Pass\n";
    }
}

Outputs "Pass".  Furthermore it doesn't compile if the move constructor
is commented out.  That being said, I don't honestly know the answer to
your question.  Certainly if you catch by value, instead of by
reference, you'll need a copy ctor.

Also if you use the (brand new, N2179):

   std::exception_ptr e = std::current_exception();

You may need a copy constructor as well.  And making your exception
types usable with std::current_exception() is probably motivation enough
to keep them copyable (non-mutable refcounted strings are still a
valuable tool :-)).

-Howard

---
[ 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.comeaucomputing.com/csc/faq.html                      ]





Author: noreply@this.is.invalid (Niels Dekker - no return address)
Date: Wed, 25 Jul 2007 14:59:39 GMT
Raw View
>> When writing a custom exception class in C++03, it's recommended to have
>> its copy constructor supporting the no-fail guarantee, to prevent the
>> exception handling mechanism from calling std::terminate().  [...]
>> I got the impression that in C++0x, it's good enough to just make
>> sure that the exception class has a non-failing move constructor.

Howard Hinnant replied:
> #include <iostream>
>
> class A
> {
>     A(const A&);
>     A& operator=(const A&);
> public:
>     A() {}
>     A(A&&) {}
> };
>
> void f()
> {
>     A a;
>     throw a;
> }
>
> int main()
> {
>     try
>     {
>         f();
>     }
>     catch (A&)
>     {
>         std::cout << "Pass\n";
>     }
> }
>
> Outputs "Pass".  Furthermore it doesn't compile if the move constructor
> is commented out.

Thank you, Howard!  Interestingly, your program *does* compile on the
compiler I'm currently using, when the move constructor commented out.
It only gets me an error at link time:
  error LNK2019: unresolved external symbol "private: __thiscall
A::A(class A const &)" (??0A@@AAE@ABV0@@Z) referenced in function "void
__cdecl f(void)" (?f@@YAXXZ)

This link error would be easily fixed, just by implementing the copy
construction, while keeping it private!  Is that according to the
Standard?

> Certainly if you catch by value, instead of by
> reference, you'll need a copy ctor.

Ok, but that wouldn't be a reason for me to care too much about making
the copy constructor non-throwing.  The end user can easily
try-and-catch a bad_alloc caused by a catch by value.  It's the
std::terminate() scenario's that scare me!

BTW, catch by value isn't recommended anyway.  (Scott Meyers, More
Effective C++, Item 13: Catch exceptions by reference.)  So I might even
prefer the end user to get a compile-time error, when she catches an
instance of my exception class by value...

> Also if you use the (brand new, N2179):
>    std::exception_ptr e = std::current_exception();
>
> You may need a copy constructor as well.  And making your exception
> types usable with std::current_exception() is probably motivation enough
> to keep them copyable

Thanks!  But does the copy constructor of my exception class need to be
non-throwing, in order to avoid an std::terminate()?  At least it seems
like std::current_exception() won't terminate when my exception class
throws a bad_alloc:
>
> exception_ptr current_exception();
>
> Returns: An exception_ptr that refers to the currently handled exception
> or a copy of the currently handled exception, or a null exception_ptr if
> no exception is being handled. If the function needs to allocate memory
> and the attempt fails, it returns an exception_ptr that refers to an
> instance of bad_alloc.
>
( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html )

Also I'm wondering, do I need to provide std::current_exception() a
*public* copy constructor?


> (non-mutable refcounted strings are still a valuable tool :-)).

For the exception class I wrote, I manually reference-counted the
collection of std::strings that it contained...  which was quite a pain!


Kind regards,

  Niels

---
[ 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.comeaucomputing.com/csc/faq.html                      ]





Author: howard.hinnant@gmail.com (Howard Hinnant)
Date: Thu, 26 Jul 2007 21:03:42 GMT
Raw View
In article <46A702C9.42ED208A@this.is.invalid>,
 noreply@this.is.invalid (Niels Dekker - no return address) wrote:

> Howard Hinnant replied:
> > #include <iostream>
> >
> > class A
> > {
> >     A(const A&);
> >     A& operator=(const A&);
> > public:
> >     A() {}
> >     A(A&&) {}
> > };
> >
> > void f()
> > {
> >     A a;
> >     throw a;
> > }
> >
> > int main()
> > {
> >     try
> >     {
> >         f();
> >     }
> >     catch (A&)
> >     {
> >         std::cout << "Pass\n";
> >     }
> > }
> >
> > Outputs "Pass".  Furthermore it doesn't compile if the move constructor
> > is commented out.
>
> Thank you, Howard!  Interestingly, your program *does* compile on the
> compiler I'm currently using, when the move constructor commented out.
> It only gets me an error at link time:
>   error LNK2019: unresolved external symbol "private: __thiscall
> A::A(class A const &)" (??0A@@AAE@ABV0@@Z) referenced in function "void
> __cdecl f(void)" (?f@@YAXXZ)
>
> This link error would be easily fixed, just by implementing the copy
> construction, while keeping it private!  Is that according to the
> Standard?

I believe C++03 requires "a" to be copied, and for that matching
constructor to be accessible.  It also allows that copy to be elided.
But whether or not the copy is elided, the constructor must be
accessible.

C++0X will change those rules only by treating "a" as an rvalue when
searching for a constructor to make the copy.  If that fails to find a
match, a second pass is made treating "a" as an lvalue.  Whatever is
matched, is then checked for accessibility.  And if an accessible
(matching) constructor is found, it is then permitted to be elided.

> > Also if you use the (brand new, N2179):
> >    std::exception_ptr e = std::current_exception();
> >
> > You may need a copy constructor as well.  And making your exception
> > types usable with std::current_exception() is probably motivation enough
> > to keep them copyable
>
> Thanks!  But does the copy constructor of my exception class need to be
> non-throwing, in order to avoid an std::terminate()?  At least it seems
> like std::current_exception() won't terminate when my exception class
> throws a bad_alloc:

I believe that N2179 specifies that if your exception copy ctor throws,
exception_ptr will simply be set to bad_alloc (please correct me if I'm
wrong Peter).  terminate() will not be called.

> > exception_ptr current_exception();
> >
> > Returns: An exception_ptr that refers to the currently handled exception
> > or a copy of the currently handled exception, or a null exception_ptr if
> > no exception is being handled. If the function needs to allocate memory
> > and the attempt fails, it returns an exception_ptr that refers to an
> > instance of bad_alloc.
> >
> ( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html )
>
> Also I'm wondering, do I need to provide std::current_exception() a
> *public* copy constructor?
>

Morally yes, but I'm not positive that such a requirement can be
enforced.  Some implementations probably won't even attempt a copy of
the exception under current_exception() (and some will).  If a copy is
attempted, I'm not sure what the behavior will be if it is not
accessible.

-Howard

---
[ 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.comeaucomputing.com/csc/faq.html                      ]





Author: "Ofer Porat" <oporat@yahoo.com>
Date: Fri, 27 Jul 2007 10:27:45 CST
Raw View
"Howard Hinnant" <howard.hinnant@gmail.com> wrote in message
news:howard.hinnant-529845.14461126072007@johnf2.biosci.ohio-state.edu...
|
| C++0X will change those rules only by treating "a" as an rvalue when
| searching for a constructor to make the copy.  If that fails to find a
| match, a second pass is made treating "a" as an lvalue.  Whatever is
| matched, is then checked for accessibility.  And if an accessible
| (matching) constructor is found, it is then permitted to be elided.
|

This is in section [class.copy]/15, but section [except.throw]/5 still
says that a throw operand must have an accessible copy constructor.
Section [except.handle]/17 still says that a catch argument must have an
accessible copy constructor.


---
[ 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.comeaucomputing.com/csc/faq.html                      ]





Author: "Niels Dekker - no return address" <invalid@noreply.com>
Date: Fri, 27 Jul 2007 14:27:59 CST
Raw View
Ofer Porat wrote:
>
> Howard Hinnant wrote:
>>
>> C++0X will change those rules only by treating "a" as an rvalue when
>> searching for a constructor to make the copy.  If that fails to find a
>> match, a second pass is made treating "a" as an lvalue.  Whatever is
>> matched, is then checked for accessibility.  And if an accessible
>> (matching) constructor is found, it is then permitted to be elided.
>
>
> This is in section [class.copy]/15, but section [except.throw]/5 still
> says that a throw operand must have an accessible copy constructor.
> Section [except.handle]/17 still says that a catch argument must have
> an accessible copy constructor.

Good catch!  Thank you, Ofer.

But... Howard actually convinced me that an exception class should be
CopyConstructible anyway.  (Even though his conceptgcc test appears to prove
otherwise!!!)  Because the class needs to be compatible with
std::current_exception().

The question remains: should the copy constructor of an exception class
still be non-throwing, in C++0x?  (In order to avoid std::terminate)  I
don't think so, because providing a non-throwing move constructor would be
good enough.  (And actually, this appears to be confirmed by Howard's test
program.)  Which would be great, because writing non-throwing copy
constructors is quite painful...  (Reference counting, I know!)

Kind regards, Niels

PS next week I'm on vacation.  Please feel free to continue the discussion
without me  :-)


---
[ 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.comeaucomputing.com/csc/faq.html                      ]