PROPER ERROR MANAGEMENT IN FREEBASIC
Written by Stéphane Richard (Mystikshadows)
INTRODUCTION:
Don't be scared by the title. Believe it or not, it's amazing the problems that can be solved
just by taking the time to implement a proper error management system into your code. Error
management is often overlooked today no matter what platform your projects are intended. Why
you ask? Simply because it is seen as a big beast to implement. This is usually because many
coders who claim this tend to implement error management after the game or application is developed
already. Indeed, to have a good control over the error management system, it needs to be implemented
before and during the coding of the application, not after. Then, you'll see, error management won't
be the big beast it is claimed to be.
This article will attempt to cover error management as it could be implemented in FreeBasic as well
as see how to manage the errors themselves. Indeed, the application doesn't need to be halted everytime
an error occurs, we'll see why here and what you can do instead of halting the program.
WHAT EXACTLY IS ERROR MANAGEMENT:
The best way to explain this concept is to start by defining what an error is in this context. In this document we'll be covering the runtime errors not the syntax errors because the compiler reports those errors so you can correct them. Runtime errors are, well, errors that happen while your program is executing itself. Error management is the art of controling these runtime errors, reporting the problem and taking the proper action for these errors. The first thing I need say is that I split errors up into three general categories. There are the software errors, operating system errors and the hardware errors. Let me explain these:
- Software Errors:
These are errors that were caused by the program itself while it was performing a task that didn't need any hardware to perform. These could be mathematical errors such as "Division by zero error" or any other non hardware related errors. When you run out of memory or stack space are other good examples of these errors. - Operating System Errors:
These are errors that are typically caused when the program attempts to access a device but fails to for whatever reason. They are also created when, for example, you open a file that does not exist, or do not have the rights to write to the folder you are trying to create the file to. These type of error usually entail that there's no hardware failure, but the operating system itself is not letting you do the expected job. - Hardware Errors:
These errors, as the name implies are hardware caused. Printer is not ready error is a good example of such error. when you try to access drive A but there's no floppy in the drive, I also consider that a hardware error because if a floppy was present, everything would go smoothly. This could be true if you're trying to set a graphic mode that simply isn't supported by the hardware present in the computer too. Basically any error that is caused because a hardware device is not present, or not performing it's designated task when asked to is, to me, a hardware error.
The reason why I split the errors that can occur into these three categories is mainly to split the error management task into 3 simpler functions. It is also because I treat these three groups
of error differently. Like I mentionned, not all errors need the same kind of management nor do they need the same type of exit procedure. The art of deciding which method to use and when to
return to the program itself and when to halt everything and go back to the operating system is what proper error management is all about.
WHAT TO DO WHEN AN ERROR IS ENCOUNTERED:
Again I can only share what I know and what I use. So in a typical error management scenario, what you'll see here is how I would cope with the situations. When you get an error, in freebasic and alot of other BASICs i'm familiar with, the error number of the problem can be found in the Err variable. First thing I do is look up the error in a table like this one to know what exactly the error actually is. Then according to the error number, I have the application report the error (if needed) and perform certain recovery functions (if needed) and finally the application can continue (once all the precautions are taken). Here's a rundown of these situations, and what could be done with them. Atleast, it's what I'd do when I encounter them. This is a list of circumstances that could potentially cause errors and what one could do with them. These are suggestions (as in what would/could be considered proper thing to do) in some cases, you might need to halt on every error (really depends on the nature of the application).
- Mathematical Errors: The more classic example is the infamous Division by Zero Error. However there are many reasons such an error would occur. When your application is heavily mathematical, you need to control the flow calculation. Maybe a variable, that should have received a result prior to another part of the set of calculations or formulas, was set yet, Perhaps another error somewhere else in the code wasn't executed. The main thing to ask yourself when mathematical errors are created is how bad it is for the application. Typically, all erroneous results from a formula needs to be reported. However exiting the application abruptly would be overkill in this scenario. The best thing to do is report the error and cancel the operation allowing the user to see if what he did before calling the formula isn't part of the problem (a field that was not entered or validated for example). Basic rule of thumb is if you know which variable doesn't have it's value set, when you're reporting the error, you should add it to the error reporting, it will help the programmers decipher where the error came from better.
- File Access And Manipulation Errors:
You try to open a file and for some reason it won't let you (file not found, File is Read Only (and you're trying to open it to write to the file) and other such errors. This could happen when you physically select a file in a Save or Open dialog. Access Denied Errors could be because you have the file open by another application or someone else on the network opened it. If you hard coded a network path and suddenly the network drive isn't available. There are many situations why a File Access or Manipulation error can occur. It's not a good idea to exit the program abruptly here either especially when the user is just opening a file (they could open other files if the need to so there's still work that can be done even if they can't open the current file. They should be given the option to do so in this scenario. One situation that would be valid to exit the application is if the program needs to connect to a database for any work to be performed then there's no point in staying in the application. You could stay at the beginning of the application to give them a chance to fix the reason why it can't connect to the database, but it would be the same results just exiting the program, then they get the problem fixed, and restart the program. - Network Related Errors:
The reasons why a network error of some sort would happen usually are similar to the situations that would cause file access and manipulation errors. The file doesn't exist, the network drive isn't mapped (in the cases where it needs to be) insufficient rights to perform the requested file or database operation and so on. Because you are in a network environment rather than working locally on your hard drives for example, some error should be treated differently. However, as a general rule of thumb, treating the as file acccess manipulation errors will be considered acceptable. The only thing that could change is if you can't save to a network file, an option to save locally could be added so the work done to a file isn't lost. Then when the network situation is resolved, you could ultimately tell the user and offer to copy the local files to the designated network location. - Peripheral Access Error:
Well this one pretty much speaks for itself when you think about it. Try to write your file a write protected floppy disk for example will cause this type of error. Trying to print a document and the printer isn't ready (no paper, printer is not on, a paper jam has occured) whichever the reason may be, again here cancelling the current operation and offering to start the operation again when the error is fixed is usually the best policy, the civilized policy so to speak. Most of the time these error numbers and description will be able to tell the user what happened and give them a good idea on how to fix the problem so the operation can be carried out successfully. if the drive has bad sectors it will be reported and in some cases it would be useless to try the operation again. In those rare cases, exiting the application could be tolerated if there's no alternatives to the error. - Internal System Errors:
There's two types of internal System Errors. Out of memory errors can be managed in a civilized manner, however Memory leak errors should be reported and the application halted in some way. Before you'd have the option to have a floating point coprocessor present in the system or not. Back then, if your program relied on a mathematics coprocessor to perform an operation and it wasn't present it would be considered an internal operation error. If only that part of the application needs that special function call, you could cancel that process and return to another part of the application (usually the place that caused the error) and let the user do other things otherwise, I usually exit the program as in the current context of the application, the desired operation will never work until such a time where they either add memory to the system, upgrade their hard drive and other situations that will, beyond a shadow of a doubt, need to restart or reboot the system.
Typically, these are the errors you need to be careful and pay special attention to. There are other types of errors but usually they can be handled without being reported. How you treat these errors will vary based on the importance of the situation or the part of the
application that is causing the error. If you're saving you'll want to give a few options to see if there could be a way to not loose the file for example. As files are usually quite important and typically not created just for fun. Now that we know the types of errors and the
situations and circumstances that can cause them, we'll now look at what good error management could be at the level of the language itself, in our case, FreeBasic.
HOW TO CODE FOR THE ERRORS IN FREEBASIC:
The first thing to know about coding for error trapping and management is what the language offers as tools for error control. In the case of FreeBasic, errors are trapped as events so to speak and a series of statements are provided to manage them. Let's start by looking at an empty skeletal code sample to start to situate where things go. In FreeBasic errors are best managed at the sub or function level. So hor first example will be a function example.
FUNCTION OpenDatabaseFile(FilePath AS STRING) AS INTEGER ' ----------------------------------- ' First we Turn On Error Management ' ----------------------------------- ON ERROR GOTO ErrorManager ' -------------------------------------------------------- ' Here we put the code that the function should perform ' -------------------------------------------------------- OpenDatabaseFile = 0 ' ----------------------------------------------------- ' When we're exiting we can turn off Error management ' ----------------------------------------------------- ON ERROR GOTO 0 ' ----------------------------------------------- ' We now need to exit the function so the error ' manager part isn't executed accidentally ' ----------------------------------------------- EXIT FUNCTION ErrorManager: ' --------------------------------- ' Error Management Code goes here ' --------------------------------- RESUME NEXT END FUNCTION
Now let's see what we have here. The first line in the function turns Error trapping on so to speak by telling the program that
if an error occurs, go to the ErrorManager label to execute the code that's there. After that first line goes the code to what
the function should be doing. Here I just assign 0 as the return value of the function. But the actual opening of the database
would have been right before that assignment. Next we turn error management off with the ON ERROR GOTO 0 <- (zero). This is how
you turn off error management in FreeBasic (and most basic dialects today). Since in FreeBasic error trapping is at the sub or function level
you can omit this line as it will be turned off at the end of the subroutine. We then exit the function with EXIT FUNCTION. I can
stress the importance of that EXIT FUNCTION statement. If it is not present the code will naturally continue all the way to the
label ErrorManager and execute that code too because it's part of the function. So you need to exit the function before the
program gets a chances to get to that error manager. If an error Occurs, the program will jump to that label and execute the code,
that's the only time you'll want to execute that code. Finally the error management could should Resume execution of the program with RESUME NEXT
if the error is critical enough to warrant exiting the program it should be done before the RESUME NEXT statement. We'll see examples
of this later in this document.
Now let's put some code in here to see what we can do. Taking our sample function which would typically open a database file it's important to note
that error management in FreeBasic or any other languages should be context sensitive. For example, if this function opens a database file, it wouldn't
make sense to manage calculation errors like division by zero because you won't be calculating in this function. That's what I mean by you have to keep
things in context. Here's some code.
FUNCTION OpenDatabaseFile(FilePath AS STRING) AS INTEGER ' ----------------------------------- ' First we Turn On Error Management ' ----------------------------------- ON ERROR GOTO ErrorManager ' -------------------------------------------------------- ' Here we put the code that the function should perform ' -------------------------------------------------------- OpenDatabaseFile = 0 ' ----------------------------------------------------- ' When we're exiting we can turn off Error management ' ----------------------------------------------------- ON ERROR GOTO 0 ' ----------------------------------------------- ' We now need to exit the function so the error ' manager part isn't executed accidentally ' ----------------------------------------------- EXIT FUNCTION ErrorManager: ' --------------------------------- ' Error Management Code goes here ' --------------------------------- SELECT CASE ERR CASE 2 ' The system cannot find the file specified. Message$ = "The system cannot find the file specified." CASE 3 ' The system cannot find the path specified. Message$ = "The system cannot find the path specified." CASE 4 ' The system cannot open the file. Message$ = "The system cannot open the file." END SELECT PRINT Message$ OpenDatabaseFile = Err END FUNCTION
As you can see, I only manage file related errors and that is the way it should be as well. This is a basic error manager in that it only displays the error that just occured and exit the subroutine, not the whole program, just the faulty function. The code that called this function would either continue with the code or stop should the error justify it. You can use error management to provide alot more information that this however (which is useful, for instance, in a testing and debugging session) where the aim of the error management isn't only to trap errors but to fix them whenever possible). Here's the same code with a bit more useful detail if you wanted to search for the bug.
FUNCTION OpenDatabaseFile(FilePath AS STRING) AS INTEGER ' ----------------------------------- ' First we Turn On Error Management ' ----------------------------------- ON ERROR GOTO ErrorManager ' -------------------------------------------------------- ' Here we put the code that the function should perform ' -------------------------------------------------------- OpenDatabaseFile = 0 ' ----------------------------------------------------- ' When we're exiting we can turn off Error management ' ----------------------------------------------------- ON ERROR GOTO 0 ' ----------------------------------------------- ' We now need to exit the function so the error ' manager part isn't executed accidentally ' ----------------------------------------------- EXIT FUNCTION ErrorManager: ' --------------------------------- ' Error Management Code goes here ' --------------------------------- SELECT CASE ERR CASE 2 ' The system cannot find the file specified. Message$ = "The system cannot find the file specified." & _ "In the OpenDatabaseFile Function with " & _ FilePath & " Passed as a parameter." CASE 3 ' The system cannot find the path specified. Message$ = "The system cannot find the file specified." & _ "In the OpenDatabaseFile Function with " & _ FilePath & " Passed as a parameter." CASE 4 ' The system cannot open the file. Message$ = "The system cannot find the file specified." & _ "In the OpenDatabaseFile Function with " & _ FilePath & " Passed as a parameter." CASE ELSE RESUME NEXT END SELECT PRINT Message$ OpenDatabaseFile = Err END FUNCTION
You can be as detailed in your explanation of the error as you want. The important thing is that
adequate information should be reported with the error to help correct it. If there's more than
one thing that could happen in the process of performing an operation, you could write these
errors to a log file so that all the details of the operation can be recorded and studied at a
later time for possible correction. The CASE ELSE clause is telling the program that if any errors
that aren't 2, 3 or 4 occurs, just continue with the normal execution of the code. I would say
that the best thing to do is to use common sense and what you feel should be done. If you are using
an application and you don't like the fact that the program stops everytime an insignificant error
happens, then you need to assume that most people shouldn't like it if your program exits for the same
reasons. Indeed a good way to learn is to see how other (popular and well known) programs handle
their errors and to mimic them basically. If it's good enough for the others it should be good enough
for your projects.
What can you learn from this? When you manage your errors, while you are coding (not adding error
management to the project) the task doesn't look as bad and yet it requires the same amount of work.
Usually, I have code snippets for all my error management situations and I just paste the appropriate
code in the appropriate sub or function. Saves alot of typing and makes error management quite easy
this way especially if the project is a big one. You can create standards this way that others can
follow which also helps manage the overall quality of the whole project just by having a certain
control over the errors that could occur even if the fault is that of the programmer.
AND IN CONCLUSION:
And this concludes our technique on proper error management. As you can see from the short code samples here,
error management can represent a good part of the programming process. The more errors a sub or function needs
to manage, the longer the error management part for that function or sub will be. And because that, you can
imagine what a job it can be to add after you coded the program. It's doable but it's a big chunk of coding in
itself if kept for the end of the programming phase.
Functions that perform calculations don't all need error management either. If you don't divide a variable by another
in that function, you'll never cause a division by zero so that function can be spared the error management code. You
have to take the time to see what the sub is doing, and what could go wrong when it does it's job. That's why common
sense is of the essence. As always I'm opened to comments, suggestions and ideas about this document. Feel free to
email me, as always, with any questions. Until I write again, have a good error free program.
MystikShadows
Stéphane Richard
srichard@adaworld.com