Topic: Must exception classes have copy constructors?


Author: David Byrden <Goyra@iol.ie>
Date: 1996/04/06
Raw View
Alan Griffiths <aGriffiths@ma.ccngroup.com> wrote:

>I've just tried porting some code to MSVC4 (it compiles and works with
>SC7.1 and BC4.5).
>
>In effect it appears that the Microsoft compiler _requires_ anything
>thrown as an exception to have an accessible copy constructor.  (I've
>tended to make copy constructors private to ensure catch-by-reference
>and aviod slicing bugs.)
>
>Looking through the DWP I can't see any reason to require a copy
>constructor, but there are references to copying temporaries.

          What DWP did you read? The January 96 version requires that the
"throw" statement have access to the copy constructor and destructor of a
thrown object.

                                    David
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: jdmorris@ix.netcom.com (Jason D. Morris)
Date: 1996/04/06
Raw View
On 05 Apr 96 02:58:28 GMT, Alan Griffiths <aGriffiths@ma.ccngroup.com>
wrote:

>I've just tried porting some code to MSVC4 (it compiles and works with
>SC7.1 and BC4.5).
>
>In effect it appears that the Microsoft compiler _requires_ anything
>thrown as an exception to have an accessible copy constructor.  (I've
>tended to make copy constructors private to ensure catch-by-reference
>and aviod slicing bugs.)
>
>Looking through the DWP I can't see any reason to require a copy
>constructor, but there are references to copying temporaries.
>
>Is MSVC4 correct?
>
>code:
>class MyBaseException {
>public:  MyBaseException() {}
>         virtual char* what() = 0;
>private: MyBaseException(const MyBaseException&);
>};
>
>class MyException1 : public  MyBaseException {
>public:     virtual char* what() { return "MyException1"; }
>};
>
>int main() {
>    try     {
>        throw MyException1();
>// error C2700: 'class MyException1' : cannot be thrown (use -W4 for more info)
>
>// warning C4671: 'MyException1' : the copy constructor is inaccessible
>    } catch (MyBaseException& e) {
>        cout << "OK - got here (" << __LINE__ << ") : have a " << e.what()
>  << endl;
>    } catch (...) {
>        cout << "ERROR - got here (" << __LINE__ << ")" << endl;
>    }
>    return 0;
>}
>

Here's what BC++ 5.0 generates...

Borland C++ 5.0 for Win32 Copyright (c) 1993, 1996 Borland
International
MS-EXCPT.CPP:
Error MS-EXCPT.CPP 15: Compiler could not generate copy constructor
for class 'MyException1' in function main()
*** 1 errors in Compile ***

It appears we have an agreement in interpretation of the DWP.

Jason
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1996/04/06
Raw View
Alan Griffiths <aGriffiths@ma.ccngroup.com> writes:

>I've just tried porting some code to MSVC4 (it compiles and works with
>SC7.1 and BC4.5).

>In effect it appears that the Microsoft compiler _requires_ anything
>thrown as an exception to have an accessible copy constructor.  (I've
>tended to make copy constructors private to ensure catch-by-reference
>and aviod slicing bugs.)

According to the draft standard, when you throw something, what is
thrown is actually a copy of that something. If the copy constructor
for that something is not accessible, the copy can't be made.

Under some circumstances the copy can be optimized away. The draft
standard was recently clarified to say that when logically a copy
is needed, the copy constructor must be accessible even if in a
particular case a compiler happens to optimize away the copy.

In your case, you will probably have to throw a pointer instead
of a value, if the type has a private copy constructor.
--
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Rob Stewart <stew@datalytics.com>
Date: 1996/04/08
Raw View
Steve Clamage wrote:
>
> Alan Griffiths <aGriffiths@ma.ccngroup.com> writes:
>
> >I've just tried porting some code to MSVC4 (it compiles and works with
> >SC7.1 and BC4.5).
>
> >In effect it appears that the Microsoft compiler _requires_ anything
> >thrown as an exception to have an accessible copy constructor.  (I've
> >tended to make copy constructors private to ensure catch-by-reference
> >and aviod slicing bugs.)
>
> According to the draft standard, when you throw something, what is
> thrown is actually a copy of that something. If the copy constructor
> for that something is not accessible, the copy can't be made.
> [snip]

> In your case, you will probably have to throw a pointer instead
> of a value, if the type has a private copy constructor.

You'll have to implement an exception object management policy.
(I know the exception is actually the pointer to some class,
but I'm using "exception object" to mean the object pointed
to by that exception.)  You must allocate your exception objects
on the heap, and delete them in the final catch block.  That is,
so long as you throw the pointer up the stack, as it were, you
cannot delete what the pointer points to.  Only in the catch
block in which you no longer rethrow the exception can you
delete the memory (exception object) it points to.

--
Robert Stewart  | My opinions are usually my own.
Datalytics, Inc. | stew@datalytics.com
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Philippe Verdy <100105.3120@compuserve.com>
Date: 1996/04/09
Raw View
David Byrden <Goyra@iol.ie> s'   crit :
> Alan Griffiths <aGriffiths@ma.ccngroup.com> wrote:
> >Looking through the DWP I can't see any reason to require a copy
> >constructor, but there are references to copying temporaries.
>           What DWP did you read? The January 96 version requires that the
> "throw" statement have access to the copy constructor and destructor of a
> thrown object.
          I have submitted an article which has been rejected
(I don't know why) about copy constructors needed. What I meant
is such a restriction about the copy constructor is only to
allow cleaner exception handling, concerning stack unrolling.
In effect, if the compiler dis not have access to the copy
constructor, the exception catcher which effectively matches
the exception in the current context should be limited to
use references only on the object.

Because an error catcher can catch only a base class of the
thrown object, and not the full object, when the exception
matches, the thrown object will have to be copied to a catcher
variable which may have less members than the original thrown
object. This copy will occur while the throwing function con-
text is still active, because the original thrown object was
constructed within the thrower and may still reference some
fields which will be destructed by the stack unrolling algo-
rithm once the catcher receives the focus to do the appro-
priate action the handler require.

The reason why I am putting this submission is that I don't
know why this scheme does not allow resumable exceptions:
after all, when an assertion fails in a function which has
to throw an exception, it first constructs an exception object
within its context, then calls a library function to perform
the exception matching. Once the matching is found, this
library first call the exception handler to perform the
copy constructor to a temporary object with type equal to
the one taken by the catcher. Then only the library returns
to the exception thrower, which will perform sannity in its
context, and return to its caller in exception state (may be
by returning an extra flag in a register, or by modifying
its return address to an laternate return address given by
its caller, so that the caller can continue stack unrolling
on its active block and eventually jump to the catcher block
if the calling block was a try { } block.

So if the catcher has temporarily access to the thrower's
context which is still valid, I think that exceptions can
be resumed:
instead of just performing the copy contructor of the
catched exception, the library could first enter into a
status function whose execution will say if the exception
can be recovered or not. If it cannot, this context can be
chained to other pending resumable catchers, until one of
them return a status that says the exception can be resumed.

If no resumable catcher is found in this chain, then the
library will return a "don't-resume" status to the thrower
which will effectively destroy its own exception object, then
will start the stack unrolling for an effective exception.

But if a catcher returns a "resume" status, this status can
be immediately returned to the thrower, which will continue
as if the exception had not occured.

This has several impacts on the code specification, because
the programmer should have to specify whever its throw()
invocation could resume or not. So this requires an additional
keyword like throw_resumable(), which does not have the "goto"
or "break" or "continue" or "return" semantics, in the fact
that the exception is not fatal (the jump is conditional).
The compiler would generate a different library call for this
addtional operator.

By the same way, a resumable error catcher could fail to find
a way which will satisfy the resume condition indicated by
the exception object. But there could be other resumable
catchers pending. This gives the opportunity of the resumable
exception catchers chain (this only adds a unique static code
pointer in each execution thread). The rest of the storage for
this chain can be allocated at the catch point.

Finally, resumable exception handlers must indicate that they
can resume the exception. The semantics of the standard
catch() construct is not sufficient for it as the catcher code
to execute is not od the same kind: a standard catch()
handler executes its code once the exception has occured, in a
constext where the thrower does not exists any more.
So this requires another keyword or construction like
catch_resumable() instead of catch().

For this new construction, the copy constructor does not have
to be invoked, because the thrower context is still valid, so
a catch_resumable() handler can receive directly a reference
to the thrown object. The code in that catcher must be quite
short if the exception kind is a lack of resource, because
the thrower context is still active.

In order to indicate its intention of resuming the exception,
this code should use a specific construct (for example: retry;
without parameters, which means "cancel the pending exception
and resume to the resumable thrower"). This would make the
resumable catcher return a "resume" state to the exception
library which will be reported to the thrower. In that case
all it would have to do is just delete its temporary thrown
object and continue the execution.

Such resumable exceptions should be very useful for time-out
exceptions, or device-not-ready exceptions (which may be
resumed after the user has been noticed of it by an alert
with choices like "Retry" or "Cancel"), or for exceptions
caused by lack of resources (a resumable exception catcher
could check if it can free up some of the resources indicated
by the exception object, for example it could free up some
unused cache-buffers to allow more memory for the thrower).

This model has no-impact on the existing code, and will add
overhead only when new keywords are in actions. All other
semantics of stancard throw()/catch() pairs are preserved.
What do you think of this model ?

Example of resumable exceptions for out-of-memory:
--------------
in the catcher
--------------
struct OutOfMemory; // fatal exception
struct NeedMemory
: public OutOfMemory // resumable exception
{
  NeedMemory(size_t size) : OutOfMemory(size) {}
}
...
try {
  ...
  function() // this can send a resumable exception
  ...
}
catch_resumable(NeedMemory &exc) {
  // check if can free up some memory for exc.size()
  if (can_resume == true)
    retry; // resume in the thrower
} // cannot resume, check other catch_resumable() handlers now
catch(OutOfMemory &exc) {
  // Fatal exception, or resumable exception could not be
  // resumed by an explicit use of "retry;" in any pending
  // resumable_catchers.
  // Make some cleanup required by this error.
} // continue at end of try/catch construct

----------------------------------
Now in the exception thrower code:
----------------------------------
void function()
{
   size_t size;
   ...
   int retries;
   for (retries = 2; --retries >= 0) {
     // needs some memory resource (size_t size)
     ...
     if (...can allocate size...)
        break; // exits the for loop
     if (retries == 0)
        throw OutOfMemory(size); // fatal
     throw_resumable NeedMemory(size);
     // if could resume, continue here
   } // retry loop
   if (retries < 0)
     throw OutOfMemory(size); // fatal
   // OK, resource allocation succeeded
   ...
}
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Alan Griffiths <aGriffiths@ma.ccngroup.com>
Date: 1996/04/05
Raw View
I've just tried porting some code to MSVC4 (it compiles and works with
SC7.1 and BC4.5).

In effect it appears that the Microsoft compiler _requires_ anything
thrown as an exception to have an accessible copy constructor.  (I've
tended to make copy constructors private to ensure catch-by-reference
and aviod slicing bugs.)

Looking through the DWP I can't see any reason to require a copy
constructor, but there are references to copying temporaries.

Is MSVC4 correct?

code:
class MyBaseException {
public:  MyBaseException() {}
         virtual char* what() = 0;
private: MyBaseException(const MyBaseException&);
};

class MyException1 : public  MyBaseException {
public:     virtual char* what() { return "MyException1"; }
};

int main() {
    try     {
        throw MyException1();
// error C2700: 'class MyException1' : cannot be thrown (use -W4 for more info)

// warning C4671: 'MyException1' : the copy constructor is inaccessible
    } catch (MyBaseException& e) {
        cout << "OK - got here (" << __LINE__ << ") : have a " << e.what()
  << endl;
    } catch (...) {
        cout << "ERROR - got here (" << __LINE__ << ")" << endl;
    }
    return 0;
}


--
Alan Griffiths               | Also editor of: The ISDF Newsletter
Senior Systems Consultant,   | (An Association of C and C++ Users publication)
CCN Group Limited.           | (ISDF editor  : isdf@octopull.demon.co.uk)
(agriffiths@ma.ccngroup.com) | (For ACCU see : http://bach.cis.temple.edu/accu)

---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]