exception catch

R

Rahul

Hi Everyone,

I have the following exception class,

class E1
{
};

class E2
{
public: E2(const E1&)
{
printf("automatic type conversion\n");
}
};

int main()
{
try
{
throw E1();
}
catch(E2 obj)
{
printf("first handler\n");
}
catch(E1 obj1)
{
printf("second handler\n");
}
}

IN the above case, the E1 object is not converted into E2 and is
handled by the second handler, this suggests that automatic type
conversion doesn't happen with exceptions, i was wondering if this is
specified in standards and is there any reason for that?

Thanks in advance!!!
 
I

Ian Collins

Rahul said:
Hi Everyone,

I have the following exception class,

class E1
{
};

class E2
{
public: E2(const E1&)
{
printf("automatic type conversion\n");
}
};

int main()
{
try
{
throw E1();
}
catch(E2 obj)

Don't catch exceptions by value, use a const reference.
 
K

Kai-Uwe Bux

Rahul said:
Hi Everyone,

I have the following exception class,

class E1
{
};

class E2
{
public: E2(const E1&)
{
printf("automatic type conversion\n");
}
};

int main()
{
try
{
throw E1();
}
catch(E2 obj)
{
printf("first handler\n");
}
catch(E1 obj1)
{
printf("second handler\n");
}
}

IN the above case, the E1 object is not converted into E2 and is
handled by the second handler, this suggests that automatic type
conversion doesn't happen with exceptions, i was wondering if this is
specified in standards and is there any reason for that?

It is specified in [15.3/3].

As for a reason, I can only guess. However, I would conjecture that the
problem is that when you throw, you do not necessarily have control over
the catch. I.e., a library component might throw precisely because it
cannot handle the condition locally and wants the client code to supply a
catch handler. If you allowed for user-defined conversions in this setting,
there could be surprise catch-matches.


Best

Kai-Uwe Bux
 
D

Dave Rahardja

Rahul said:
Hi Everyone,

I have the following exception class,

class E1
{
};

class E2
{
public: E2(const E1&)
{
printf("automatic type conversion\n");
}
};

int main()
{
try
{
throw E1();
}
catch(E2 obj)
{
printf("first handler\n");
}
catch(E1 obj1)
{
printf("second handler\n");
}
}

IN the above case, the E1 object is not converted into E2 and is
handled by the second handler, this suggests that automatic type
conversion doesn't happen with exceptions, i was wondering if this is
specified in standards and is there any reason for that?

That is correct, according to 15.3. In my opinion, this is a desired
behavior, as exception handlers are meant to handle specific exceptions,
not simply any exception that can be conveniently converted. Imagine the
confusion when you have a more-appropriate exception handler higher up
the call tree, and some other exception handler down below preempts it
due to automatic type conversion.

Note that exception handlers will handle an exception if it is a derived
class of its parameter type.

-dr
 
R

Rahul

Don't catch exceptions by value, use a const reference.

Why do you say that exception's should be caught by value?
is it because of object slicing when a derived class exception is
caught by a base class exception handler?
 
A

Alf P. Steinbach

* Rahul:
Why do you say that exception's should be caught by value?
is it because of object slicing when a derived class exception is
caught by a base class exception handler?

On reason is to avoid copy construction.

Consider the case where the copy constructor might trow (e.g. allocating
a string).

A copy construction exception at the throw point is bad but at least you
do get an exception -- just not necessarily the one you asked for --
but a copy construction exception at catch point is t r o u b l e.

Another reason is efficiency, avoiding the possible copying.

And a third reason is clarity: by catching by reference to const you're
telling the reader of the source code that you're not going to modify
this exception object, and you don't need it copied.

Since catching by reference to const is the most common idiom, anything
else indicates that for some reason you need something else.

Cheers, & hth.,

- Alf
 
A

Abhishek Padmanabh

* Rahul:







On reason is to avoid copy construction.

Consider the case where the copy constructor might trow (e.g. allocating
a string).

A copy construction exception at the throw point is bad but at least you
do get an exception -- just not necessarily the one you asked for --
but a copy construction exception at catch point is t r o u b l e.

Aren't exceptions thrown always copied (atleast once) after they are
thrown and before they are caught? To make it persistent during the
stack unwinding? And if copy construction can throw, it is a problem
even before catching the exception. I think the most important reason
for not catching exceptions by value is efficiency i.e. we avoid
atleast one copy creation of the exception object.

The other reason could be writing a generic base exception handler
where all derived types can be caught without slicing.
 
R

Rahul

Aren't exceptions thrown always copied (atleast once) after they are
thrown and before they are caught? To make it persistent during the
stack unwinding? And if copy construction can throw, it is a problem
even before catching the exception. I think the most important reason
for not catching exceptions by value is efficiency i.e. we avoid
atleast one copy creation of the exception object.

The other reason could be writing a generic base exception handler
where all derived types can be caught without slicing.

Yes i think so, they are copied atleast once, and this code
illustrates the same,

class E
{
public : E()
{
printf("in default constructor...\n");
}
E(const E& ref)
{
printf("copy constructor is invoked...\n");
}
};

int main()
{
try
{
printf("in try\n");
throw E();
}
catch(E& ref)
{
printf("in handler\n");
}
return(0);
}

output is

in try
in default constructor...
copy constructor is invoked...
in handler
Press any key to continue
 
E

Erik Wikström

That is correct, according to 15.3. In my opinion, this is a desired
behavior, as exception handlers are meant to handle specific exceptions,
not simply any exception that can be conveniently converted. Imagine the
confusion when you have a more-appropriate exception handler higher up
the call tree, and some other exception handler down below preempts it
due to automatic type conversion.

That would never happen since the handlers are tried top down for a
match and the first match is used. So if an exact match is above one
that would require a conversion the exact match would always be used.

I believe that what you were thinking of was the reverse, when the
handler requiring a conversion was placed above the exact match, in
which case the match requiring a conversion would be used. Notice that
this can happen if you have a handler taking a base-class above a
handler for a derived class, the handler for the derived class would
never be used.
 
J

James Kanze

Concerning the original question: basically, the only
conversions considered are derived to base, and then only if the
base is an unambiguous public base. (In the case of pointers,
the conversion to void* is also considered---but you shouldn't
be throwing pointers---and in all cases, top level cv qualifiers
are ignored. And I think a few very, very basic conversions
also take place systematically: array to pointer, for example.)
Aren't exceptions thrown always copied (atleast once) after they are
thrown and before they are caught? To make it persistent during the
stack unwinding? And if copy construction can throw, it is a problem
even before catching the exception.

The formal semantics of a throw is copy initialization of the
exception somewhere where it won't disappear. As always with
copy initialization, there must be an accessible copy
constructor, and as always, the compiler is allowed to elide the
copy constructor.

The exception is not considered as being thrown until this copy
is finished, however. If the copy triggers an exception, that
exception will be thrown, rather than the original exception.
The exception is not considered caught until the code in the
catch block is entered, after the copy (if the catch is by
value). And exception in this copy will cause terminate to be
called.
I think the most important reason
for not catching exceptions by value is efficiency i.e. we avoid
atleast one copy creation of the exception object.

Given all the rest that happes when you throw an exception, this
is not normally an issue.
The other reason could be writing a generic base exception handler
where all derived types can be caught without slicing.

That's probably the original motivation behind the rule to catch
by reference. As Alf pointed out, however, catch by reference
has become the "normal" way of doing things in C++; anytime you
do something else, the reader will wonder why. So even if
you're throwing a non-polymorphic object, you should still catch
by reference.
 
J

James Kanze

On Dec 9, 11:27 am, Abhishek Padmanabh
<[email protected]> wrote:

[...]
Yes I think so, they are copied atleast once, and this code
illustrates the same,
class E
{
public : E()
{
printf("in default constructor...\n");
}
E(const E& ref)
{
printf("copy constructor is invoked...\n");
}
};
int main()
{
try
{
printf("in try\n");
throw E();
}
catch(E& ref)
{
printf("in handler\n");
}
return(0);
}
output is
in try
in default constructor...
copy constructor is invoked...
in handler
Press any key to continue

This depends on the compiler. The compiler is allowed to elide
the copy, and all of the compilers I have access to (g++, Sun CC
and VC++) do (and don't output the line "copy constructor is
invoked..."). (This depends on the version, however---older
versions of g++ did call the copy constructor here.)
 
J

James Kanze

Rahul wrote:

[...]
It is specified in [15.3/3].
As for a reason, I can only guess. However, I would conjecture that the
problem is that when you throw, you do not necessarily have control over
the catch. I.e., a library component might throw precisely because it
cannot handle the condition locally and wants the client code to supply a
catch handler. If you allowed for user-defined conversions in this setting,
there could be surprise catch-matches.

Implementation considerations may also have played a role.
Remember that the type matching here takes place at runtime (and
so must use some form of RTTI). Derived to base is pretty
straight foreward to do dynamically, but anything else would
require a lot more information in the RTTI structures, and a lot
more work to process it. (Off hand, except for Derived to Base,
the only other conversion which needs to be considered
dynamically is pointer to void*.)

Note that some conversions occur at the throw site,
systematically, and thus do not require RTTI to support them.
Array to pointer, for example: you can declare a catch with a
reference to an array:
catch ( int const (&array)[ 5 ] )
for example, but there's nothing you can throw that will match
it, because the array will be converted to a pointer at the
throw site, before matching starts.
 

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

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,241
Members
46,831
Latest member
RusselWill

Latest Threads

Top