Please open Exception.lava in LavaPE.
Please open ExceptionDemo.lava in LavaPE.
Please open OutFuncFailDemo.lava in LavaPE.
Exception handling in Lava.
You frequently have the situation that a member function of a class is unable to provide the expected output parameters: it "fails". In this case the function would either return an "error code" that must be evaluated by the caller, or it will "throw an exception".
Error codes may be ignored by the caller, and there is a serious danger that the caller will make use of undefined output parameters then.
This and similar kinds of programming errors, caused by inadvertent use of undefined/uninitialized local, member, and output variables, will be hard to locate and fix in many cases. So it is one of the major goals of the entire Lava development to prevent such errors by appropriate static or run time checks, and particularly by enforcing proper use of exceptions.
LavaPE checks already at programming time if every branch of a function assigns a value to every non-optional output parameter. If you cannot fulfill this condition in a branch then you can exit from this branch only by throwing an exception (using the throw <expr> statement, see below) at the end of the branch.
Even if (formally) all branches define all non-optional outputs it may happen that a branch "fails logically" before all outputs could be set, i.e., it contains some relational statement (like "a < b") or the invocation of a function that hasn't output parameters but may evaluate to "false". To catch also these implicit failures, Lava checks on exit from a function if it has output parameters and if its body "failed logically", and if true, an "output function failed" exception is thrown automatically by the run time system. (Cf. the last sample program.)
So Lava guarantees under all circumstances that the caller of a function cannot inadvertently use undefined outputs of failed function invocations. He/she may, however, catch the exception and then use output parameters of the function call "on his/her own account". The worst thing that may happen then is a "null object" exception or an "access to zombified object" exception. "Dangling pointers" aren't possible in Lava because of the Lava storage management by reference counting. However, it could also happen that the failing function has already set certain outputs before it fails and that those outputs actually don't make sense then. So the programmer will have to account on his/her own risk in these cases.
The throw statement of Lava is a special variant of the fail statement and is inserted by clicking the fail button of then keyword toolbar. If you delete the parameter after the inserted throw keword then throw will be replaced by fail. The fail statement may be used to terminate a function execution with result false. It may be used only if all mandatory output parameters of the function have been set before.
If this isn't possible you must throw an exception. The parameter of throw is an expression whose value provides the exception object of the throw statement. You may only throw exceptions whose type is mentioned in the "throws" clause of the declaration of the containing function and is derived from the basic built-in exception class Exception (see std.lava).
An interesting alternative would be to associate such a throws clause with an entire class or even pattern and to enforce the translation of lower-level exceptions into exceptions of this class or pattern in some way or other. The users of the class or pattern would then be sure to get only such exceptions, rather than low-level exceptions that they often won't understand anyway.
The try ... catch statement of Lava has essentially the same structure and semantics as that of C++:
try <guarded compound statement> catch <exception class> <exception variable> <compound statement> catch <exception class> <exception variable> <compound statement> ... catch <exception class> <exception variable> <compound statement> #try
If an exception occurs deeply nested within the <guarded compound statement> that isn't caught already on a lower level of the current call stack then it will be caught and processed by the first catch clause whose <exception class> is a base class (or the exact class) of the run time class of the current exception object. All exception classes must be derived from the basic built-in class Exception, and you can use the members of Exception to display a meaningful exception message and the current call stack.
You may also throw a new exception from within a catch clause and in this way raise the exception to a higher level which is possibly more meaningful for the caller.
You may also re-throw the original exception if you decide that the exception should rather be processed on a higher level of the call stack.
In Lava, all exception classes are p-derived from the same base class Exception. Class Exception is non-creatable and has two member variables code and message. The type <ERRORCODE> of code is virtual with default value Enumeration. When a concrete exception type is p-derived from Exception the base value Enumeration of <ERRORCODE> will be specialized to become a concrete enumeration type whose enumerated items distinguish the various individual manifestations of the concrete exception type, for instance ZeroDivideException or IntegerOverflow for a HardwareExeption, see below.
An item of a Lava enumeration type may be associated with an explanatory text/comment, which can also be accessed and displayed as a String object. In the exception context this comment is used as a default error text associated with the respective error code.
Class Exception (declared in std.lava) has a static member function callStack. It delivers the call stack at the time when the exception occurred as a String to the caller.
Exceptions may be raised also implicitly by the hardware (hardware exceptions) or by the Lava run time system (run time exceptions). "Division by zero", "integer overflow", "access violation" are typical hardware exceptions, "assertion violation", "array index out of range", "out-of-memory exception" are typical run time exceptions.
The common base class Exception for all types of exceptions provides a default exception handler function catch, as well as a member function show, which simply displays the respective exception code and the associated message text. Catch simply calls show, but may be overridden in user-defined exception classes to exhibit a more specific behavior.
The default catch function is called automatically if the respective program doesn't handle the pending exception and its call stack is finally left "at its upper end" as a consequence.
Please open Exception.lava in LavaPE.
This sample demonstrates a user-defined exception type (MyException) which is raised by a throw statement (in function C1::f1). It is caught in the ExceptionDemo initiator, first in more object-oriented style by the MyException::catch method (which overrides Exception::catch), and then in "old-fashioned style" by a try statement with several catch clauses.
Additionally you could provoke a run time exception by changing the invariant of class C1.
Please open ExceptionDemo.lava in LavaPE.
This sample demonstrates a hardware exception and two run time exceptions.
Please open OutFuncFailDemo.lava in LavaPE.
This sample shows that Lava throws an "output function failed" exception automatically if the body of a function evaluates to "false" and the function has output parameters and doesn't throw an exception explicitly.
Sample Array.lava provokes an "array index out of range" exception.