After you have got a slight overview how ScriptBasic handles the modules get a jump-start looking at the simplest sample module trial.c!
This module was the very first module the developers wrote to test the module handling functionality of ScriptBasic. This does almost nothing, but prints out some trace messages so that you can see how the different functions are called in the module. There is only one function in this module that the basic code can call. This is trial. This function increments a long value and returns the actual value of this state variable.
Here is the whole code:
#include "../../basext.h"
besVERSION_NEGOTIATE
printf("The function bootmodu was started and the requested version is %d\n",Version);
printf("The variation is: %s\n",pszVariation);
printf("We are returning accepted version %d\n",(int)INTERFACE_VERSION);
return (int)INTERFACE_VERSION;
besEND
besSUB_START
long *pL;
besMODULEPOINTER = besALLOC(sizeof(long));
if( besMODULEPOINTER == NULL )return 0;
pL = (long *)besMODULEPOINTER;
*pL = 0L;
printf("The function bootmodu was started.\n");
besEND
besSUB_FINISH
printf("The function finimodu was started.\n");
besEND
besFUNCTION(trial)
long *pL;
printf("Function trial was started...\n");
pL = (long *)besMODULEPOINTER;
(*pL)++;
besRETURNVALUE = besNEWMORTALLONG;
LONGVALUE(besRETURNVALUE) = *pL;
printf("Module directory is %s\n",besCONFIG("module"));
printf("dll extension is %s\n",besCONFIG("dll"));
printf("include directory is %s\n",besCONFIG("include"));
besEND
besCOMMAND(iff)
NODE nItem;
VARIABLE Op1;
long ConditionValue;
USE_CALLER_MORTALS;
/* evaluate the parameter */
nItem = besPARAMETERLIST;
if( ! nItem ){
RESULT = NULL;
RETURN;
}
Op1 = besEVALUATEEXPRESSION(CAR(nItem));
ASSERTOKE;
if( Op1 == NULL )ConditionValue = 0;
else{
Op1 = besCONVERT2LONG(Op1);
ConditionValue = LONGVALUE(Op1);
}
if( ! ConditionValue )
nItem = CDR(nItem);
if( ! nItem ){
RESULT = NULL;
RETURN;
}
nItem = CDR(nItem);
RESULT = besEVALUATEEXPRESSION(CAR(nItem));
ASSERTOKE;
RETURN;
besEND_COMMAND
#include <stdio.h>
As you can see there is a lot of code hidden behind the macros. You can not see versmodu, bootmodu or finimodu, because they are implemented using the macro besVERSION_NEGOTIATE, besSUB_START and bes_SUB_FINISH. These macros are provided in the header file `basext.h', along with other type definitions and include statements that are needed to compile a module. To have a deeper understanding feel free to have a look at the file `basext.c' containing the source for basext.h.
All the macros defined in the header file for the extensions start with the three letters bes. These stand for basic extension support.
The version negotiation function prototype and function start is created using the macro besVERSION_NEGOTIATE. This macro generates the function head with the parameters named Version, pszVariation and ppModuleInternal. The accepted version is returned using the macro INTERFACE_VERSION. The reason to use this macro is to ease maintainability.
The current version of the interface is 10 (or more). Later interfaces may probably support more callback interface functions, but it is unlikely that the interfaces become incompatible on the source level. When the module is recompiled in an environment that uses a newer interface it will automatically return the interface version that it really supports. If the interfaces become incompatible in source level the compilation phase will most probably fail.
The bootmodu function is created using the macro besSUB_START. In this example this allocates a state variable, which is a long. The memory allocation is performed using a callback function and with the aid of the macro besALLOC.
Here we can stop a bit and examine, how the callback functions work. The ScriptBasic interpreter has several functions that are available for the extensions. To access these functions the module should know the entry point (address) of the functions. To get the entry points ScriptBasic creates a table of the callback functions. A pointer to this table is passed as the first argument to each module function except the version negotiation function versmodu. In C syntax this table is a struct named SupportTable.
There are numerous functions that an extension can and should use to communicate with ScriptBasic. One of the most important functions is memory allocation. The field of the SupportTable named Alloc is initialized to point to the function alloc_Alloc defined in the file `myalloc.c'. This allocation function needs the size of the needed memory block and a pointer to a so-called memory segment. The memory segment pointer to be used is available via the SupportTable. The first member of the table is a pointer to another table containing the current interpreter execution environment. This environment is also a &code{struct} and contains the memory segment pointer.
This is a bit complicated and you can get confused. To ease coding use the macros available. These will hide all these nifty details. However know that these macros assume that you use them together. In other words besALLOC can only be used in a function when the function head was created using the macro besFUNCTION (or besSUB_START or besSUB_FINISH in case of bootmodu and finimodu). This is because the macros assume certain variable names in the arguments.
The function finimodu is created using the macro besSUB_FINISH. This function in this example does nothing but prints a test message. There is no need to release the memory allocated using besALLOC, because the memory is administered to belong to the segment of the interpreter execution environment and is released by the interpreter before the exits.
The real function of the module is named trial and is defined using the macro besFUNCTION. It gets the state variable via the module pointer. The module pointer is always passed to the module functions as the second argument. The functions can access it using the macro besMODULEPOINTER. Using this macro it looks like a local variable.
To return the counter value the function needs a basic variable. This variable should hold a long value and should be mortal. This is created using the macro besNEWMORTALLONG. The variable is actually a struct and the field containing the long value can be accessed using the macro LONGVALUE.
You can notice that this macro does not start with the letters bes. The reason is that this macro comes from a different header file `command.h'. This header file is included by `basext.h' and the definitions in that file are mainly for implementing internal commands linked statically. However some of the macros can be used for dynamic modules as well.
The function trial finally writes out some configuration data. On one hand this is another example of a callback function used via a macro named besCONFIG. But this is more important than that. This shows you that the modules can access any configuration data.
There is no need for any module to process separate configuration files. ScriptBasic reads the configuration file and stores each key and value pair it finds. It stores even those that do not mean anything for ScriptBasic itself, because they may be meaningful and needed by modules. The example module trial does not have its own data, therefore we print out the configuration data that ScriptBasic surely has.