If you develop software, correct software is certainly the stated goal, in order to solve with it a certain task or one class of tasks at any time without errors. You surely know from your own experience that this can be an arduous path. In different phases of the software development you will have to deal with errors again and again.
In order to track down these errors, it helps to know the possible error classes in order to track down the errors. If you only think of errors in the source code, the following overview and the articles on error management in this and the following chapters will certainly help you.
The instructions TRY, CATCH and FINALLY are described in chapter 11.5.1_Try_Finally_Catch; likewise in chapter 11.5.1.4 the event Application_Error(). Possible error sources during programming are pointed out in chapter 11.5.2. These detailed notes should help you to convert the algorithms underlying the programs error-free into Gambas source code. Chapter 11.5.3 provides basic information on using the GNU debugger and the Valgrind program to help you track down runtime errors.
Following https://de.wikipedia.org/wiki/Programmfehler#Arten_von_Programmfehlern, only a brief overview of software bugs is included here, otherwise refer to the source provided.
Figure 11.5.0.0.1: Syntax errors (IDE)
The so-called runtime errors are to be distinguished from these errors: While the above errors mean an erroneous program that is either not executable or gives erroneous results, even a `correct` program can lead to errors during its execution. Runtime errors are all kinds of errors, such as wrong or faulty runtime environment, wrong input data (type, value) or faulty libraries, which occur while the correct program is being processed. Runtime errors can, for example, manifest themselves in undesirable behavior, cause a program crash, or cause the program to “freeze” as unoperable.
The next two error categories are errors only in an extended sense:
As far as handling errors is concerned, most programming languages can be divided into two camps. On the one hand, you have C-style error handling, where a function has a return value that must be examined by the caller and whose value distinguishes the success from the error of the operation. Possibly the return value also contains more detailed information about the success for example how many files were deleted or the error that occurred during the deletion. Functions here have no return value or a return value only to describe the success of the operation in more detail. If an error occurs during the operation, an exception is thrown. This manifests itself in a runtime error. The programmer must catch this runtime error - in Gambas with Try or Catch - handle it and thus eliminate it.
Modern languages probably tend towards the exception paradigm. The advantage of exceptions is that the return value of a function does not have to be divided into two concepts (success and error). Often, in the first camp, one finds the convention that a function returns an integer value where non-negative values represent success and negative ones represent an error. At the same time, if an error occurs, the numeric return value is an index to an array of error messages, so it accurately describes the error. But what do you want to do if you have a function that can also return certain negative values in case of success?
Another advantage of exceptions is that you force the programmer to handle them. While the return value of a function can be ignored intentionally or unintentionally, a runtime error cannot be ignored. You would have to explicitly write a try in front of the statement to do so - so you can't unintentionally forget to handle the error case. The gain is that a program running in exception-based error handling will always be terminated by unexpected errors, and will not continue to run under the false assumption that no error happened, as would happen when ignoring error-indicating return values.
Gambas supports both paradigms, but more often uses exceptions, which you raise in Gambas with Error.Raise(…). For example, if you cannot delete a file with the KILL filepath statement, Gambas will throw a runtime error that you must catch with Try or Catch - otherwise the program will not continue.
However, Gambas is not consistent with the exception paradigm. You can only call Error.Raise(“string”) with a single string as an individual error message. If you want to know exactly what happened in the error case, you have to parse this information out of the string. But what happens if the developer changes the content and format of the error message or if the project and therefore the error message is translated? In other languages like Python, you can create exception classes. Instead of using the Error.Raise(“string”) method, one can populate an entire object with specific information (in native data types of the language) and put this object into Error.Raise(oObject). The programmer intercepts this object and has the complex data of the error neatly packaged in native data types. In Gambas, you can accomplish the same thing - just a little more awkwardly. Suppose you use the Memcached class. Give this class a property called “LastError”. This property can be of any class type. Therefore, create a class _Memcached_Error. The underscores are convention in Gambas: the leading underscore hides the class and the second underscore between “Memcached” and “Error” indicates the membership of the Memcached class. There you declare properties that are necessary to describe Memcached errors in detail.
Then, before raising an error in Memcached with Error.Raise(), you create a new object of type _Memcached_Error, populate it with data about the current error that occurred, and assign it to the LastError property. Here's what an extension to the component might look like:
' Memcached.class aus gb.memcached Property Read LastError As _Memcached_Error Private $hLastError As _Memcached_Error Private Sub LastError_Read() As _Memcached_Error Return $hLastError End Public Sub Delete(sKey As String) ... If _HadError(...) Then $hLastError = New _Memcached_Error ' $hLastError fill with information Error.Raise(...) Endif ... End
Try $hMemcached.Delete(...) If Error Then ' -> Use $hMemcached.LastError to handle the error or to compile a meaningful error message. Endif
This approach is probably already sufficient. Remember: If you use only one _Memcached_Error class, you would have to describe all error properties in that one class. Information that is relevant for certain errors is not needed for other errors, making the _Memcached_Error class unnecessarily large and general. But if you want to go a step further with a consistent, object-oriented view of classes, you can derive special classes from _Memcached_Error that describe individual errors. An error deleting a key has its own class _Memcached_Error_NotFound exactly for the case that deletion was impossible because the key does not exist. However, one could also imagine that the key exists but was not enabled for deletion. Therefore deletion fails because of missing permissions. For this case, another class _Memcached_Error_Permission would have to be planned.
The increased overhead brings two benefits. One is that the available information will differ by error and thus be more detailed. The other advantage of having so many error classes is that one can use the IS operator to determine the type of error:
Try $hMemcached.Delete(...) If Error Then If $hMemcached.LastError Is _Memcached_Error_NotFound Then ' -> Deletion was impossible because the file was not found Else If $hMemcached.LastError Is _Memecahed_Error_Permissions Then ' -> The authorisation to delete the key is missing Else ... Endif Endif
For example, instead of an exception object, the HttpClient class of gb.net.curl has two properties, ErrorText and Status, which you can use to diagnose exceptions (runtime errors or in the Error event).
Projects