User Tools

Site Tools


guide_20to_20writing_20libraries

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
guide_20to_20writing_20libraries [2018/04/15 18:19] – Added syntax highlighting tbest3112guide_20to_20writing_20libraries [2024/01/05 00:22] (current) – external edit 127.0.0.1
Line 1: Line 1:
 =====Guide to writing libraries===== =====Guide to writing libraries=====
  
-//by Richard Russell, March 2010//\\ \\ **Note:** This article specifically applies to re-usable code libraries. Another use of the [[http://www.bbcbasic.co.uk/bbcwin/manual/bbcwin5.html#install|INSTALL]] statement is as a means of splitting a large program into modules. Several of the guidelines listed below //do not apply// to that use, apart from the prohibition on the use of line numbers and labels, and the necessity to enable the **Crunch embedded program files** compile option. \\ \\  A //BBC BASIC for Windows// **LIBRARY** is a special kind of program file which contains functions and/or procedures which can be called from the main program in which it is [[http://www.bbcbasic.co.uk/bbcwin/manual/bbcwin5.html#install|INSTALLed]] (or from another library installed by that same program). Fundamentally it is no different from any other program file except that it contains no 'main program' (unless you wish to include some code which will report an error if it is executed directly, see [[/Preventing%20a%20library%20from%20being%20run|Preventing a library from being run]]). In addition, a library must be saved as an **internal format** (tokenised) .BBC file rather than a plain-text (ASCII) .BAS file. If you attempt to INSTALL a library saved in the wrong format a [[http://www.bbcbasic.co.uk/bbcwin/manual/bbcwinc.html#badlibrary|Bad library]] error will result.\\ \\  Having stated that a library is fundamentally no different from an ordinary BBC BASIC program, special considerations apply which don't (at least to the same extent) to a conventional program. Some of these are technical restrictions (for example that line numbers cannot be used) and others are issues of style which become much more important in the case of a re-usable library. When you write a program for your sole use you can decide whether you want to risk the potential consequences of ignoring 'good practice', but when writing a library (assuming that you intend to publish it for others to use) you mustn't impose such risks on your end users. For them to use your library with confidence, it should adhere to the programming guidelines listed below.\\ \\  An overriding consideration, which lies behind many of the recommendations, is that a library must be as self-contained as possible. Ideally it should not be dependent on any specific characteristics of the program in which it is INSTALLed, nor should it impose any avoidable restrictions on that program.\\ \\ +//by Richard Russell, March 2010// 
 + 
 +**Note:** This article specifically applies to re-usable code libraries. Another use of the [[http://www.bbcbasic.co.uk/bbcwin/manual/bbcwin5.html#install|INSTALL]] statement is as a means of splitting a large program into modules. Several of the guidelines listed below //do not apply// to that use, apart from the prohibition on the use of line numbers and labels, and the necessity to enable the **Crunch embedded program files** compile option. 
 + 
 +A //BBC BASIC// **LIBRARY** is a special kind of program file which contains functions and/or procedures which can be called from the main program in which it is [[http://www.bbcbasic.co.uk/bbcwin/manual/bbcwin5.html#install|INSTALLed]] (or from another library installed by that same program). Fundamentally it is no different from any other program file except that it contains no 'main program' (unless you wish to include some code which will report an error if it is executed directly, see [[/Preventing%20a%20library%20from%20being%20run|Preventing a library from being run]]). In addition, a library must be saved as an **internal format** (tokenised) .BBC file rather than a plain-text (ASCII) .BAS file. If you attempt to INSTALL a library saved in the wrong format a [[http://www.bbcbasic.co.uk/bbcwin/manual/bbcwinc.html#badlibrary|Bad library]] error will result. 
 + 
 +Having stated that a library is fundamentally no different from an ordinary BBC BASIC program, special considerations apply which don't (at least to the same extent) to a conventional program. Some of these are technical restrictions (for example that line numbers cannot be used) and others are issues of style which become much more important in the case of a re-usable library. When you write a program for your sole use you can decide whether you want to risk the potential consequences of ignoring 'good practice', but when writing a library (assuming that you intend to publish it for others to use) you mustn't impose such risks on your end users. For them to use your library with confidence, it should adhere to the programming guidelines listed below. 
 + 
 +An overriding consideration, which lies behind many of the recommendations, is that a library must be as self-contained as possible. Ideally it should not be dependent on any specific characteristics of the program in which it is INSTALLed, nor should it impose any avoidable restrictions on that program.
  
   * Libraries may contain neither **line numbers** nor **labels**. It follows that a library should not use **GOTO**, **GOSUB** or **ON GOSUB** statements. A library may use **RESTORE** but only the special [[http://www.bbcbasic.co.uk/bbcwin/manual/bbcwin7.html#restore|RESTORE +n]] **relative** form.   * Libraries may contain neither **line numbers** nor **labels**. It follows that a library should not use **GOTO**, **GOSUB** or **ON GOSUB** statements. A library may use **RESTORE** but only the special [[http://www.bbcbasic.co.uk/bbcwin/manual/bbcwin7.html#restore|RESTORE +n]] **relative** form.
-\\  
  
   * Libraries should not share **global** variables with the main program. When compiling a program, it is possible to select the **Abbreviate names** //crunch// option for the main program, but not select the **Crunch embedded program files** option. With that combination of settings the names of variables in the main program and in the library - even if originally the same - will end up being different. Additionally, accessing a global variable in a library forces the main program to use a specific name for that variable, which is undesirable (for example, suppose another library uses the **same** name for a **different** global variable!).   * Libraries should not share **global** variables with the main program. When compiling a program, it is possible to select the **Abbreviate names** //crunch// option for the main program, but not select the **Crunch embedded program files** option. With that combination of settings the names of variables in the main program and in the library - even if originally the same - will end up being different. Additionally, accessing a global variable in a library forces the main program to use a specific name for that variable, which is undesirable (for example, suppose another library uses the **same** name for a **different** global variable!).
-\\  
  
   * If you want to pass a structure as a parameter to a library routine, be aware that the previous consideration also applies: if the main program is crunched but the library isn't, the **member names** of the structure will be different and the program won't run. This can be circumvented by using **static integer names** (A% to Z%), **numeric member names** (e.g. 1&, 2$), names starting with the **@** character (e.g. @a&, @b$) or names specified in a **REM!Keep** compiler directive, since they are never modified by crunching. Otherwise it is best to avoid passing information between the main program and a library in a structure. If there is no acceptable alternative you will need to inform your users that they must select the **Crunch embedded program files** compile option.   * If you want to pass a structure as a parameter to a library routine, be aware that the previous consideration also applies: if the main program is crunched but the library isn't, the **member names** of the structure will be different and the program won't run. This can be circumvented by using **static integer names** (A% to Z%), **numeric member names** (e.g. 1&, 2$), names starting with the **@** character (e.g. @a&, @b$) or names specified in a **REM!Keep** compiler directive, since they are never modified by crunching. Otherwise it is best to avoid passing information between the main program and a library in a structure. If there is no acceptable alternative you will need to inform your users that they must select the **Crunch embedded program files** compile option.
-\\  
  
   * As an exception to the above, it is perfectly acceptable to pass a structure into or out of a library if it is **opaque**. What this means is that the structure is **declared** within the library and its members are only **accessed** within the library; the main program passes the structure between one library routine and another, but does not itself need to access the individual members. You can always provide library routines for reading and writing specific members of the structure. See the [[http://www.bbcbasic.co.uk/bbcwin/manual/bbcwing.html#xmllib|XMLLIB]] library for an example of the use of an opaque structure.   * As an exception to the above, it is perfectly acceptable to pass a structure into or out of a library if it is **opaque**. What this means is that the structure is **declared** within the library and its members are only **accessed** within the library; the main program passes the structure between one library routine and another, but does not itself need to access the individual members. You can always provide library routines for reading and writing specific members of the structure. See the [[http://www.bbcbasic.co.uk/bbcwin/manual/bbcwing.html#xmllib|XMLLIB]] library for an example of the use of an opaque structure.
-\\  
  
   * Libraries should strictly adhere to the recommended [[http://www.bbcbasic.co.uk/bbcwin/manual/bbcwin2.html#conventions|variable naming conventions]], that is LOCAL and PRIVATE variables (and the formal parameters of functions and procedures) should normally be **lowercase** and constants should be entirely **CAPITALS**. Globals should be a mixture of upper and lower case, but a library should not be accessing globals anyway (see below for **library globals**). The importance of this is that, in the presence of asynchronous interrupts (e.g. ON SYS, ON TIME) a **global** variable and a **local** variable must never share the same name. Since the end user shouldn't need to know the names of variables used in a library, adherence to a naming convention is the only way to guarantee that.   * Libraries should strictly adhere to the recommended [[http://www.bbcbasic.co.uk/bbcwin/manual/bbcwin2.html#conventions|variable naming conventions]], that is LOCAL and PRIVATE variables (and the formal parameters of functions and procedures) should normally be **lowercase** and constants should be entirely **CAPITALS**. Globals should be a mixture of upper and lower case, but a library should not be accessing globals anyway (see below for **library globals**). The importance of this is that, in the presence of asynchronous interrupts (e.g. ON SYS, ON TIME) a **global** variable and a **local** variable must never share the same name. Since the end user shouldn't need to know the names of variables used in a library, adherence to a naming convention is the only way to guarantee that.
-\\  
  
   * It will occasionally be necessary for two or more routines within a library to share information. There are three main ways in which this may be achieved:   * It will occasionally be necessary for two or more routines within a library to share information. There are three main ways in which this may be achieved:
Line 23: Line 26:
   - The functions and procedures may share information by means of PRIVATE variables, arrays and/or structures. See the article [[/Sharing%20PRIVATE%20variables|Sharing PRIVATE variables]] for more information.   - The functions and procedures may share information by means of PRIVATE variables, arrays and/or structures. See the article [[/Sharing%20PRIVATE%20variables|Sharing PRIVATE variables]] for more information.
   - The functions and procedures may share global variables. This is the least satisfactory method because there is no way of guaranteeing that the global variable names are unique and don't clash with other variables in the main program or another library. If you use this method choose variable names with care, such as incorporating a [[/Creating%20a%20Globally%20Unique%20Identifier|Globally Unique Identifier]] or the name of your library, for example **globalvar@mylibrary** (variable names containing an **@** are conventionally reserved for use in libraries).   - The functions and procedures may share global variables. This is the least satisfactory method because there is no way of guaranteeing that the global variable names are unique and don't clash with other variables in the main program or another library. If you use this method choose variable names with care, such as incorporating a [[/Creating%20a%20Globally%20Unique%20Identifier|Globally Unique Identifier]] or the name of your library, for example **globalvar@mylibrary** (variable names containing an **@** are conventionally reserved for use in libraries).
-\\  
  
   * Temporary variables (other than formal parameters) used within an individual procedure or function should **always** be declared as LOCAL or PRIVATE. Note that PRIVATE variables are incompatible with certain types of error trapping, in which the main program attempts to recover from an error. This is because if an error occurs at a time when a variable has been made PRIVATE you cannot call that routine again (it will be treated as a re-entrant call). This problem can be circumvented by careful use of local error trapping as follows:    * Temporary variables (other than formal parameters) used within an individual procedure or function should **always** be declared as LOCAL or PRIVATE. Note that PRIVATE variables are incompatible with certain types of error trapping, in which the main program attempts to recover from an error. This is because if an error occurs at a time when a variable has been made PRIVATE you cannot call that routine again (it will be treated as a re-entrant call). This problem can be circumvented by careful use of local error trapping as follows: 
 +
 <code bb4w> <code bb4w>
         DEF PROCtest         DEF PROCtest
Line 32: Line 35:
         REM Rest of procedure here         REM Rest of procedure here
         ENDPROC         ENDPROC
-</code>\\ +</code>
  
   * Avoid doing anything within the library which could disrupt operation of the main program. For example don't disable interrupts for any longer than necessary (e.g. **WAIT 0** is probably acceptable but **WAIT 1** isn't) and don't change any global settings (e.g. the ***FLOAT** mode) - even if you restore them before returning from the library the operation of an interrupt routine could be affected.   * Avoid doing anything within the library which could disrupt operation of the main program. For example don't disable interrupts for any longer than necessary (e.g. **WAIT 0** is probably acceptable but **WAIT 1** isn't) and don't change any global settings (e.g. the ***FLOAT** mode) - even if you restore them before returning from the library the operation of an interrupt routine could be affected.
-\\  
  
   * If the library needs to report an error condition, preferably do so by returning a status value to the calling program. This gives the greatest degree of flexibility to the user, without forcing his error-handling mechanism (**ON ERROR**) to be activated. If you consider that it is appropriate to use the **ERROR** statement to trigger an error (for example if the condition indicates a serious failure) then at least allocate different error codes (**ERR**) to the different cases, and avoid those codes most commonly used by a main program (e.g. 100).   * If the library needs to report an error condition, preferably do so by returning a status value to the calling program. This gives the greatest degree of flexibility to the user, without forcing his error-handling mechanism (**ON ERROR**) to be activated. If you consider that it is appropriate to use the **ERROR** statement to trigger an error (for example if the condition indicates a serious failure) then at least allocate different error codes (**ERR**) to the different cases, and avoid those codes most commonly used by a main program (e.g. 100).
-\\  Several of these recommendations are also valid for ordinary functions and procedures within a conventional program, but do not have the same importance when they affect only that one program. Only in the case of a published library, when the reliability of an end user's program could be affected, should they ideally be strictly followed.+ 
 +Several of these recommendations are also valid for ordinary functions and procedures within a conventional program, but do not have the same importance when they affect only that one program. Only in the case of a published library, when the reliability of an end user's program could be affected, should they ideally be strictly followed.
guide_20to_20writing_20libraries.1523816377.txt.gz · Last modified: 2024/01/05 00:17 (external edit)