D
Dmitry A. Kazakov
Dmitry A. Kazakov said:Ludovic Brentawrites: [...][...]package Ada.Exceptions.Extra is
type Extra_Information is abstract tagged null record;
procedure Raise_Exception (E : in Exception_Id;
Information : in Extra_Information'Class);
function Exception_Information (X : Exception_Occurrence)
return Extra_Information'Class;
end Ada.Exceptions.Extra;
A pair of simple questions:
1. Dispatching to non-existing methods of Extra_Information?
You'd have to "down-cast", which would raise Constraint_Error if the
Extra_Information was not of the expected type.
What if Extra_Information is extended:
type My_Extra_Information is new Extra_Information with private;
procedure Say (Info : My_Extra_Information);
At some non-library level Say gets overridden. Then this new object is
attached to an exception which is propagated out of the scope of the
override. A handler then makes a dispatching call to Say. What happens?
One possible solution is to force the attached object to the base class
when the child type gets out of scope. But then things become even more
interesting, because Say might be abstract! Then object need to be
partially finalized etc. That would be quite sort of C++! -))
Another solution is to route the exception to Program_Error when anything
from My_Extra_Information gets out of scope. This would be bullet proof,
but I don't believe many would enjoy it.
I would say that the actual type (that extends Exra_Information) must
be visible in the exception handler and at the point of
Raise_Exception.
But how would you check it? The exception handler knows nothing about any
derived types. Yet it can dispatch ... to something that no more exists.
I hadn't thought about that. For the mechanism to be useful, I
suppose that finalization would have to occur at the end of the
exception handler.
I thought about controlled components and pointers to controlled objects.
They may get out of scope before a handler catches the exception.
However it may not be desirable that
Extra_Information be controlled, as this would make it possible for
Deallocate to raise an exception.
The Extra_Information doesn't have to be protected, it can contain an
access to a protected object. This would make it quite easy to raise
two copies of it in two tasks.
Also it will be two copies of Extra_Information? I definitely do not like
it. This would mean that the behavior will vary depending on where
exception was raised.
I do prefer present by-value semantics of Ada model. But if it gets
extended, and the direction is tagged types, then that is another
semantics: tagged types are by-reference. The only possible logical
consequence of this is that user-defined exception objects have to be
protected.
I would say, at the end of the last exception handler (i.e. the one
that doesn't reraise the exception).
OK that means that the mechanics will be pretty inefficient. Upon
Raise_Exception, the object will always be copied.
What do you have in mind?
Let's consider a model based on [sub]types. If there are exception
contracts then all possible exception types are known in each handler
context. If you don't allow open-end things like exception'Class in
contracts then there is nothing to surprise you in any handler. OK, that
would probably be too rigid. We could relax it by allowing classes
constrained to ranges of types: sort of
type E1 is new exception with ...;
....
type EN is new EN-1 with ...;
procedure Foo
exception E1'Class (EN) | Constraint_Error | ...;
or
procedure Foo
exception E1..EN | Constraint_Error | ...;
The point is that Foo knows what it will raise, the handler knows if the
body may call Foo. Further Foo cannot raise anything defined within Foo.
Same is applicable to any scope. If I try
declare
My_Exception is new exception;
begin
...
end;
Then I'll get compile error telling that My_Exception may propagate out of
the scope.
declare
My_Exception is new exception;
Old_Style_Exception : exception; -- That's OK, no new type here
begin
...
exception
when My_Exception =>
... -- Alright now
end;