Access to internal properties

In some applications, you may need to fine-tune the exception handling and memory management mechanisms. You may also need a simple way to access command-line arguments. In less common cases, you may require low-level access to internal properties of objects.

Exception handling

Class EXCEPTIONS enables you to control the handling of exceptions. UNIX_SIGNALS , discussed next, complements it for the special case of fine-grain signal handling on Unix or Unix-like platforms. Both are meant to be inherited by any class that needs their facilities.

The basic exception mechanism treats all exceptions in the same way. In some cases, it may be useful to discriminate in a Rescue clause between the various possible causes.

Class EXCEPTIONS provides the features to do this. Each kind of exception has an integer code, which you can use through several features:

  • The integer-valued query exception which gives the code of the latest exception.
  • Queries which determine the general nature of the latest exception: is_signal which determines whether the exception was an operating system signal; is_developer_exception which determines whether it was explicitly caused by a raise, as explained next; assertion_violation.
  • Query recipient_name which gives the name of the exception's recipient - the routine that was interrupted by the exception.

The class also provides a set of constant integer-valued attributes which denote the various possible codes, such as No_more_memory, Routine_ failure and Precondition_violation. So you can test the value of exception against these codes if you need to ascertain the precise nature of an exception. To keep EXCEPTIONS simple these constant attributes are declared in a class EXCEP_CONST, of which EXCEPTIONS is an heir.

Another occasional requirement is for a mechanism to trigger an exception explicitly. Procedure raise answers this needs; the argument, a string, is the tag chosen for the exception. The code in this case is Developer_exception; the query is_developer_exception will return true; and the tag is accessible through feature tag_name.

You will notice in the interface specification for EXCEPTIONS that for some properties of the latest exception there are two features, one with a name such as exception or recipient_name as seen above and the other with a name prefixed by original_: original_exception, original_recipient_name.

Caution: The reason for the presence of these pairs is that the immediately visible cause of a routine interruption may not be the real one. Assume that routine r from class C, which has a Rescue clause, calls s from D with no Rescue clause, and that some call executed by s causes a precondition violation. Because s has no Rescue clause of its own, s will fail. Up the call chain, the first routine that has a Rescue clause - r itself, or one of its own direct or indirect callers - may process the exception; but if it examines the exception code through attribute exception it will get the value of Routine_failure. This may be what you want; but to handle the situation in a finer way you will usually need to examine the code for the original exception, the one that interrupted s. This code will be accessible through the attribute original_exception, which in this case will have the value of Precondition, the exception code for precondition violations. So you have the choice between exploring the properties of the original exception, or those of the resulting routine failures. Just make sure you know what you are looking for.

As you will see from the header comments in the flat-short form of class EXCEPTIONS , the queries that return detailed information about an exception, such as assertion_violation, all give an answer determined by original_exception rather than exception, since when the two are different (that is to say, when you handle the exception in a routine other than the original recipient) the value of exception is always Routine_failure and there is nothing more to say about it.

Signal handling

The features of class EXCEPTIONS enable you to determine whether a certain exception is a signal - an operating system event such as may result from a child process that disappears, a window that is resized, a user that hits the Break key and many others. But they do not give you more details because the exact set of possible signals is highly platform-dependent.

Class UNIX_SIGNALS complements EXCEP_CONST by providing codes for the signals of Unix and similar systems, such as Sigkill for the 'kill' signal and Sigbus for bus error.

Query is_defined (some_signal), where some_signal is an integer code, will determine whether some_signal is supported on the platform.

A class whose routines need to perform specific processing depending on the nature of signals received should inherit from UNIX_SIGNALS , or a similar class for another platform.

Because signal codes are platform-dependent, the features of UNIX_SIGNALS are implemented as once functions - computed on the first call - rather than constants, although this makes no difference to clients.

Memory management

Class MEMORY , like EXCEPTIONS , is meant to be used as an ancestor by classes that need its facilities. It offers a number of features for controlling memory management and fine-tuning the garbage collection mechanism, a key component of the Eiffel Software environment.

One of the most useful features in this class is dispose. This procedure describes actions to be applied to an unreachable object just before the garbage collector reclaims it. By default, as declared in MEMORY , the procedure does nothing; but you may redefine it in a proper descendant of MEMORY to describe dispose actions. Normally such actions will involve freeing external resources: for example a class describing file descriptors may redefine dispose so that whenever a descriptor object is garbage-collected the corresponding file will be closed.

Caution: This example is typical of proper uses of dispose. In a dispose procedure, you should not include any instruction that could modify the Eiffel object structure, especially if some objects in that structure may themselves have become unreachable: these instructions could conflict with the garbage collector's operations and cause catastrophic behavior. The legitimate use of dispose redefinition is for disposing of non-Eiffel resources.

Other features of MEMORY provide direct control over the operation of the garbage collector. You can in particular stop garbage collection through a call to collection_off, and restart it through a call to collection_on. By default, garbage collection is always on (a testimony to its authors' trust in its efficiency). Garbage collection is normally incremental, so as not to disrupt the application in a perceptible way. To start a complete garbage collection mechanism - reclaiming all unused objects - call procedure full_collect. The remaining features of MEMORY enable finer control of the collection mechanism and are useful in special cases only. You will even find a free procedure providing brave (and competent) developers with a mechanism for reclaiming individual objects manually.

MEM_INFO, the result type for query memory_statistics in MEMORY , describes objects containing information collected about memory usage. The features of GC_INFO provide statistics about the garbage collector's operation.

Command-line arguments

Writing, assembling and compiling a system yields an executable command. The system's users will call that command with arguments. These are normally provided in textual form on the command line, as inyour_system arg1 arg2 arg3

although one may conceive of other ways of entering the command arguments, such as tabular or graphical form-filling. In any case, the software must be able to access the values passed as command arguments.

A language mechanism is available for that purpose: the Root Class rule indicates that the creation procedure of the root class may have a single argument (in the Eiffel sense of argument to a routine) of type ARRAY [STRING]. The corresponding array of strings will be initialized at the beginning of the system's execution with the values entered as arguments to that execution of the command.

Although this facility suffices in many cases, it is not always convenient if you suddenly need to access the command arguments in a class that is far away from the root. An alternative mechanism, class ARGUMENTS, is available. Once again, this is a class from which you should inherit if you need its facilities. It has just two exported features:

  • argument_count, a non-negative integer, is the number of command arguments.
  • argument (i), a string, is the i-th command argument. Here i must be between 0 and argument_count; the convention is that for i = 0 the result is the name of the command itself.