Lesson 20: Libraries
A library is a collection of useful tools that allow a programmer to develop code quickly and efficiently. A library provides pre-written snippets of code that a programmer can utilize over and over instead of recreating the wheel for each and every piece of software.
Imagine that you are creating software to perform conversions between various units of measures, weights, and temperatures. In lesson 2 we create a few programs of this type but in order to do so we had to either know the conversion formulas or research their structure beforehand. The research, coding, and subsequent testing for accuracy of each formula can be very time consuming. It would be a great benefit if another programmer has already done this formula coding and offered the code for use. This is where libraries can be very beneficial. Let's assume another programmer did the coding and you found the library they offer. The library's name is "UnitConversions.BM" and all you need to do is add the library's code to your own with a statement like this:
You now have all the unit conversion formulas offered in the library literally at your finger tips!
A QB64 library is nothing more than a .BAS source code file containing a collection of subroutines and functions. The file's extension is simply changed to .BM (or .BI, we'll get to that later) to identify the file as a library. Most programming languages offer methods of adding libraries to code and , in fact, most commercial and open source software written today relies heavily upon numerous libraries attached to the source code by the programmer. Most programming forums also include a dedicated area for the discussion and sharing of libraries with others. You can find the QB64 Phoenix Edition library discussion area here.
Building a Library
The best way to understand how a library functions is to build one. Let's create the library mentioned above called "UnitConversions.BM" using the unit conversion formulas from lesson 2. We'll then build a small program that utilizes the library to highlight how effective a library is in the hands of a programmer.
Start the IDE and type in the following code. When finished save the code as UnitConversions.BM. This code is also included as UnitConversions.BM in your .\tutorial\Lesson20 directory.
We now have a library with functions that convert between Celsius to Fahrenheit and millimeters to inches. Let's add this library to a program that allows the user to choose the desired conversion. Create a new listing in your IDE and type in the following program. When finished save the code as UnitConversionDemo.BAS. This code is also included as UnitConversionDemo.BAS in your .\tutorial\Lesson20 directory.
Looking at the code listing above you'll notice that the functions UC_F2C(), UC_C2F, UC_i2mm, and UC_mm2i have all been highlighted in green by the IDE identifying them as functions. By including the library on line 44 the IDE considers the functions contained within the library simply part of the main body code.
There are two types of library files that can be created and included within your code. The first one, .BM files, only contain QB64 functions and subroutines. Library files ending in .BM must be placed at the end of your code since the IDE expects all functions and subroutines to appear after the main body. The second type of library files end in .BI. Library files ending in .BI contain variable and type declarations. Since variables declared with DIM, constants created with CONST, and type definitions created with TYPE ... END TYPE need to be at the beginning of code, so do .BI library files.
If you were to try and include variable declarations, type definitions, and/or constants within the same library file as the functions and subroutines the IDE would complain and generate an error. The need to create two different library files is not a common practice amongst programming languages. QB64 requires this behavior simply because the IDE does not have the capability of sorting out which statements go where. Perhaps in the future the IDE will be updated to support everything placed into one file, but for now you'll need to remember most libraries will require two separate files, a .BI that gets placed at the top of the code and a .BM file that gets placed at the bottom of the code. We'll create another library down below that uses both types of files so you can see this in action.
NOTE: Library files are not required to end in .BI and .BM. This naming convention seems to be the most common amongst most QB64 programmers. In fact, back in 2011 when I started writing QB64 libraries I named the top file .TOP and the bottom file .LIB. However, since the convention is overwhelmingly .BI and .BM this tutorial will stick with this naming convention to avoid confusion.
Beyond the requirement for .BI and .BM files there are a few other rules that should be followed when writing libraries. Keep in mind these "rules" are merely suggestions that will help you and others when utilizing your libraries.
Preface variables, subs, and functions exposed to the main body of code with a unique identifier.
You may have noticed that each function in the UnitConversions.BM file above was prefaced with UC_, such as UC_F2C(). The reason for this is two-fold. First, when each of the functions are used in code the UC_ preface identifies that function as belonging to the unit conversion library. Secondly, it highly reduces the possibility of a library subroutine, function, or variable name as having the same name as a subroutine, function, or variable in the main body code. If you were to create a library that contained an exposed variable named TaxRate, for example, there is a high probability that someone else that uses your library may create a variable with the same name. By naming the variable something like IRSL_TaxRate the probability of the programmer using the same variable name is greatly reduced.
Write your library to be as self sufficient as possible.
Try to avoid reliance on external variables as much as possible. Passing variables into subroutines and functions through parameters is preferential. For example, many QB64 programmers set up boolean truth detectors like so:
CONST FALSE = 0, TRUE = NOT FALSE
If you write your subroutines and functions to rely on the constants TRUE and FALSE you'll need to include them in the .BI file. This can cause confusion when the programmer adds your library and an error is generated about a duplicate constant being created either right away or when the programmer creates those commonly used constants later on. Instead of checking for or setting TRUE and FALSE check for or set 0 and -1 instead:
IF Val1 - Val2 = 0 THEN ZeroResult = -1 ' same as saying ZeroResult = TRUE
IF Val1 - Val2 <> 0 THEN ZeroResult = 0 ' same as saying ZeroResult = FALSE
The upcoming second library example will demonstrate this in more detail.
Document, document, document!
Create documentation for your libraries. I can't stress this enough. That nice shiny library you just created is working perfectly and only contains 20 statements, or commands, to remember. Two months later you'll have completely forgotten how to use those shiny new commands, trust me. Without documentation you'll either be forced to review the entire source code for the library again or look at past projects you created with the library to remember how to use it. If you create libraries that you intend to share with others and don't include documentation on its use then it's useless to other programmers. Documenting libraries is very time consuming. You're going to skip this step because of this. You'll be sorry you did at some point and then remember the words of wisdom you gleaned here.
Include sample source code
If you plan to distribute your library to others always include sample programs showing how to implement your library's functions and subroutines.
Collision Detection Library
Let's build a library that utilizes all of the suggested rules above and as a bonus will greatly aid in the development of your games. In lesson 15 you were introduced to four methods of collision detection; rectangular, circular, line intersection, and pixel perfect. Combining all of these detection routines into one library will save much time and effort when writing your games.
The first thing to consider is the .BI file. Which variables and type definitions do we absolutely need and how can they be designed to be used easily between the library's functions and procedures? Type in the following code and save it as CollisionLibrary.BI when finished. This code is also included as CollisionLibrary.BI in your .\tutorial\Lesson20 directory.
Now it's time for the .BM file which contains the subroutines and functions at the heart of the library. Type in the following code and save it as CollisionLibrary.BM. This code is also included as CollisionLibrary.BM in your .\tutorial\Lesson20 directory.
Now a program is needed to make use of the collision library and test its features. Type in the following code and save it as CollisionLibraryDemo.BAS. This code is also included as CollisionLibraryDemo.BAS in your .\tutorial\Lesson20 directory.
NOTE: You'll also need the files greenoval.PNG and redoval.PNG to execute this code. Both of these files are also included in your .\tutorial\Lesson20 directory.
Finally, no library is complete without documentation explaining how to utilize the library. For personal libraries I create my library documentation within the IDE using REM statements. This way I can simply copy the documentation directly into my main code body at the bottom for easy referral. When I'm finished writing my code I simply delete the documentation lines. The following documentation is included as CollisionLibrary.DOC in your .\tutorial\Lesson20 directory.
This library follows the .BI and .BM file requirements and all of the suggested rules when writing a set of library functions and procedures:
All variables and TYPE definitions in the .BI library file are prefaced with CL_ to identify them as belonging to the library. All functions and subroutines are also prefaced with CL_ in the .BM library file.
All TYPE definitions have been written to work seamlessly between functions and subroutines. Only one variable, CL_Intersect, has been created that is exposed to the main body code.
Documentation has been included to aid the programmer in the use of its features.
A sample program has been included to show the library in action.
A good example of the use of libraries can be found in the Minesweeper game I wrote. It utilizes three libraries that I wrote and brought together to create the game; A button library to create Windows style clickable buttons on screen, a menu library to create Windows style drop down menus, and graphics Line Input library to create interactive Line Input data fields.
Windows API Calls
QB64 also allows libraries within Windows (.DLL files) to be used as subroutines and functions. This functionality is beyond the scope of this tutorial but you need to be aware this exists. This is an advanced topic but opens up a whole new world of possibilities to the QB64 programmer. The following code uses a subroutine within the Windows kernel to report the physically installed amount of RAM within your computer. This code is included as PhysicalRAM.BAS in your .\tutorial\Lesson20 directory.
NOTE: This code was written by Spriggsy and shared on the QB64 forum here.
If you wish to explore using Windows API calls the best place to start investigating and asking questions is the QB64 Phoenix Edition forum.