A TRY element is a new (versions ≥ 3.29-07), non-standard element in a structogram, expressing structured exeption handling (aka error handling), which is an advanced concept for experienced programmers. Therefore TRY elements are not available in Simplified toolbars mode. If you are not familiar with structured exception handling you might read the background explanation first.
The TRY element is not among the official set of elements proposed by Isaac Nassi and Ben Shneiderman nor has it been included in the DIN 66261 standard (it simply hadn't been "invented" back then), but is integrating surprisingly well in the element zoo. (The implementation in Structorizer, available since version 3.29-07, is of experimental character.)
How do you insert and use a TRY element in Structorizer?
Imagine the following scenario: You want to write a routine that opens a file with given name and tries to read an integral number from it. Several things might go wrong: The file might not exist, not at least in the current directory, or the content might not be readable as number. The routine itself won't have an idea, from where and for what purpose it was called, so it can only detect an occurring problem but not propose sensible workarounds. It will have to leave it to the caller. The caller, however, must get an information about the nature of the problem in order to handle it. The routine might look as follows (see File I/O API, also for other examples making use of TRY blocks):
The result will be put to variable number. Therefore it is declared and initialized at top. Then the routine tries to open the file. The built-in function fileOpen will not raise an error but return a value equal to or less than 0 if the opening fails. So in this case our routine will raise an exception itself with some meaningful text. A throw Jump exits from the routine by stack unwinding (see background explanation). In the other case our routine attempts to read an integer via fileReadInt. This function will cause an error in case the file is empty or the initial part of the contained text in the file cannot be converted into an integral number. This would be okay, but we don't want to leave the file open. Therefore we put the error-prone instruction into a TRY block such that we can force the closing of the file in the finally section. The TRY element will catch (and thus neutralize) the error, though, no matter whether the catch section does anything or not — the error would be regarded as caught and handled. The routine would end regularly. But we don't want to let the error go unnoticed (though the caller might possibly conclude from the unchanged result value 0 that something must have gone wrong). So we decide to rethrow the caught error. This is done by simply inserting an empty throw Jump into the catch section. The effect of the rethrowing will be postponed until the finally section gets accomplished.
Now let's construct this routine in Structorizer.
First of all: You must have switched off the Simplified toolbars mode (menu "Preferences › Simplified toolbars?"): TRY blocks are only available in standard GUI mode.
1. Select the element before or after which you want to insert the TRY element:
2. Select the TRY icon in the toolbar (holding the Shift key down if you want to insert the TRY block above the selected element) or add it via the respective menu item "Diagram › Add › Before/After › TRY" (or via the context menu). Akternatively you may also press the <Ctrl><F5> key combination (versions ≥ 3.29-13). The element editor will pop up (see screenshot below). Now please resist the temptation to write the protected instructions here! Instead fill in just a name (or entire declaration) for the exception variable. This variable will hold the exception string thrown by the failing code (or by the Executor itself) such that you can work with it in the catch section. Again: DON'T write the protected code here (this will be done in the next step)! You won't do wrong, however, to write some useful comment in the comment field.
Note the checkbox "Show the FINALLY block even if empty" marked with the blue box in the screenshot above. It was introduced with version 3.30-15. If it is not checked then the created TRY element will not show a finally block, such that we could not add elements to it. Our plan, however, is to ensure the file gets closed no matter whether an error occurs or not. So we will need the finally section (see step 5 below), hence make sure to have the box checked. No problem if you forgot it: you may still check it any time later, whenever you open the editor for the TRY element again. Before version 3.30-15, the finally section was always drawn, which unnecessarily consumed space for empty finally sections.
3. The inserted TRY element looks as shown in the following screenshot. Now it gets time to put instructions or arbitrary elements in the protected upper section (between the labels "try" and "catch". You do so by selecting the field with the empty set symbol and inserting the elements you like as described in the respective sections of this guide:
4. Now select the central block of the TRY element to specify the error handling activities. This part will typically make use of the declared exception variable to find out what's the matter and possily to output the content to the user. Just insert the elements you need in the usual way. In our example we want to rethrow the exception, so we will insert an EXIT element:
The Jump element is to be filled with the keyword configured in the Parser Preferences for the EXIT statement on error:
5. The lower block in a TRY element is reserved for the "cleanup" activities. These are guaranteed to be carried out, no matter what happened in the try and the catch sections. This section may be empty if no cleanup is necessary. In our example, we want to make sure that the obtained file resource is released to the operating system, so we add a fileClose instruction:
6. After having filled the entire TRY element we may go on adding elements to the routine. So just select the TRY element as a whole (clicking on its outer frame) and append the return instruction for the routine:
7. Now the routine is ready and can be pushed to the Arranger (which is serving as subroutine pool for the Executor).
7. We might now create a main diagram, making use of the above routine. Let's assume we want to retrieve a million Euro from the account of the user and ask him to tell us the name of a file where the account number is stored (assuming that it is a plain number, not an IBAN string). Now we can use our routine getNumberFromFile just built, but we know it may throw errors, so we better prepare. Sensibly, we will call it in a TRY element. This time, a finally section isn't needed, since there is no resource we introduce on the main level (the file is already cared for by the routine itself). In the handling section we just tell the user, what error text we obtained — it will be in the variable specified in the TRY element editor, here we named it exception. Then the user is asked to choose another file:
This is of course a very unsophisticated algorithm, not even offering the user an escape. So it is open for your improvements. (And we don't show you how to retrieve money with a Structorizer routine, of course . You ought to create a dummy routine retrieveMoney(2), see CALL elements how to derive a routine diagram from a CALL.)
You may check this little program in Executor to watch how the mechanism works. The Arrangement archive may be downloaded here:
TryDemo.arrz
Here is a more simple example (the finally section was left empty, so from version 3.30-15 on, it will not show by default):
Background explanation:
When Nassi and Shneiderman proposed their modelling approach for structured programming around 1973, the concept of structured exception handling (SEH) hadn't been coined yet. So it is no surprise that the set of Nassi-Shneiderman diagram elements (according to DIN 66261, in particular) does not contain elements for structured exception handling. Most modern languages, however, provide specific syntactic structures to support error handling in a safe and convenient way, typically in form of try-catch blocks, complemented by a means to raise ("throw") a problem up to the level that can handle it. If not being excessively abused, structured exception handling is a powerful programming technique that avoids the recursive passing of status values as routine results possibly several call levels up, which requires repeated checking and gets in the way of ordinary result propagation.
Programmers frequently face a problem, often related to external resources (e.g. a file that cannot be opened or has unsuited content), that could be solved (or worked around) but not sensibly on the current level within nested loops or in a deep call situation. The solution may require user interaction or at least some larger overview at a higher level where more knowledge about the context and aims is available. So the information about the problem is to be passed to that higher competence level, usually several call levels up. To do this via the ordinary subroutine parameter lists or return values is possible but pollutes the call interfaces (routine signatures) along the path with lots of sporadically needed pecularities. To do this out of a deeply nested algorithm structure may be even harder.
The central idea is now to provide a protected environment — the "Try" block — where operation sequences being prone to fail at some low-level detail can be put into. This instruction sequence is guarded by some error handling code — the "Catch" block — that goes into action if one of the expected potential problems (exceptions) actually occurs. To this purpose, the code part immediately facing the error (e.g. a low-level subroutine) simply "throws" the error information up in the hope that it might be "caught" and handled by the lurking catcher of the "Try" environment. If it wasn't in such Try context then the thrown error will rise up to the top program level and make it crash.
"Try" structures may be nested. Hence, if the catcher of a "Try" block decides not to be competent enough to handle the exception it may "rethrow" it, such that it might be caught by some outer "Try" block guard. Again, if no "Catch" block along the stack path up handles the problem then the program will get killed.
The raising of the exception goes along with "stack unwinding", i.e. the call contexts being left are removed from the stack as if they had returned, though they are not of course continued, the remaining operations of the unwound levels are simply abandoned.
On the catching level, however, there is the helpful possibility to specify a "Finally" block that is executed either way, no matter whether the tried instructions succeeded or caused an exception, and no matter whether the exception was caught and handled or rethrown (passed further up). This Finally block is intended for necessary cleanup activities, i.e. to dismiss ressources acquired within or immediately before the Try block. Its execution is guaranteed unless it causes an error itself. But if an exception occurred and was not handled or rethrown then stack unwinding and raising of the exception will inevitably go on after the Finally block is finished. The instructions following the Finally block will only be executed either if no exception occurred or if the occurred exception had been handled by the "Catch" block. |