Subroutines |
Top Previous Next |
Subroutines are sections of programs that need to be executed numerous times. While you could use GOTO to jump into and out of a section of code, it would be very hard to manage. Subroutines can be called from anywhere in the program and return execution from where they were called. Functions, commands, statements, API, procedure, and handlers are all common names for a subroutine. The distinction of the name depends on the usage. All subroutines must begin with the SUB statement and end with the ENDSUB statement. The subroutine can appear anywhere in your source files and do not affect the path of execution until you call or GOSUB to them. Subroutines do not need to be declared if they are only used in the source file they are defined in. You must declare and external if you wish to use a subroutine located in another source file, this will be covered shortly. In the SUB statement list any parameters that will be passed to the subroutine and also whether or not your subroutine returns a value. Here is a short example subroutine SUB MyFirstSub( num as INT), INT The variables defined in your subroutine, or defined in the SUB statement are called local variables. A local variable is only accessible while your subroutine is being executed. Once control is returned to the code that called the subroutine the local variables do not exist. All local variables are stored in a special area of memory called the stack. The stack size is set by the 'Advanced' tab on the Executable Options dialog or the Project Options dialog. The default stack commit size is set to 32K and indicates how large the size of the local variables in any one subroutine can be. If you exceed this size the compiler will generate a warning message so you can adjust as needed. The RETURN statement can be omitted from the subroutine if the subroutine does not return a value. EBASIC will insert the return statement when it encounters the ENDSUB statement. SUB MySecondSub( num as INT) Calling the subroutine Once a subroutine has been defined you can call it either by just using its name, or by using the GOSUB command. GOSUB is included for backwards compatibility with other versions of BASIC. 'call our function If a subroutine does not have any parameters use an empty parenthesis '( )' to call it. Or alternatively use GOSUB AnotherSub( )
Passing parameters The above example shows passing a value to a subroutine. Variables can be passed by either value or reference depending on the type of variable and whether or not the BYREF keyword is used. When a variable is passed by reference the subroutine is allowed to make changes to the contents of the passed variable. When a variable is passed by value a copy of the contents of the variable is sent to the subroutine and the original variable will remain unchanged. Emergence BASIC defaults to passing by value for all numeric types and passing by reference for strings, UDT's, etc. The following table lists the defaults:
Numeric types may be passed by reference by using the BYREF keyword or by using a POINTER parameter SUB NewSub(a as UINT BYREF, b as FLOAT BYREF) Passing Arrays Arrays require special handling when passing them to a subroutine. The subroutine needs to know the dimensions of the array at compile time. For single dimensioned arrays it is not necessary to supply a dimension and an empty bracket set will suffice '[ ]'. All arrays are passed by reference so anything done to the array in the subroutine will modify the array passed to it. DEF myarray[10,50] as INT Optional Parameters Optional parameters at the end of the parameter list may be specified using the OPT keyword. A default value may be specified if the parameter is not included in the calling list. If a default parameter is not included then either 0 or a NULL string will be passed to fill in the optional parameter. PrintIt("This string is centered",0,1)
Declaring subroutines You must use the DECLARE statement to use a subroutine defined in another source file in project mode, or if you wish to specify the C calling convention. The C calling convention is normally used when interfacing with C or other external libraries that require it. To use a subroutine located in a different source file use the EXTERN keyword with DECLARE: DECLARE EXTERN OtherFilesSub(c as CHAR, b as INT), STRING To use the cdecl calling convention in either a local or external declare specify the CDECL keyword DECLARE CDECL MySub(c as CHAR, b as INT),STRING You can also use the DECLARE statement for subroutines located in the same file, but it is not necessary with Emergence BASIC. When the DECLARE statement is used locally it will override the parameter names specified in the SUB statement so be sure to name them the same to avoid confusion DECLARE AFunction(bb as DOUBLE),DOUBLE Global Subroutines To allow other source modules to use a subroutine it must be declared as global by using the GLOBAL keyword GLOBAL SUB _gMySub(bb as FLOAT),FLOAT All global subroutines must be uniquely named or linking your executable will fail with a duplicate definition. When debugging programs only subroutines declared as global will display in the context window when a breakpoint is encountered. You will still get the correct file name and line number, just the name of the subroutine will default to the last global name in the path of execution. Indirectly calling subroutines Emergence BASIC supports indirectly calling subroutines through function pointers. A function pointer is a UINT variable that contains the address of a subroutine. The subroutine can be local or global. A common use for this is to create an array of subroutines, that all accept identical parameters, to be called by an index. The DECLARE statement needs to be used to set up a parameter template. DECLARE fnTemplate(param as UINT),INT You can use any valid name you wish for the template. After a subroutine is defined you can get the address of that subroutine using the & operator. SUB Addition(param1 as FLOAT, param2 as FLOAT), FLOAT Call the subroutine indirectly by using the ! operator, supplying the template name, variable containing the subroutine address, and any parameters PRINT !<fnTemplate>fnArray[0](1.0, 3.0) The DECLARE statement is only used as a template and will not look for a matching SUB when used as the parameter template for indirectly calling a subroutine. For a complete example see sample program: indirect_functions.eba
Subroutines with a variable number of arguments Emergence BASIC supports subroutines with a variable number of arguments. To specify a variable argument use the ellipses (...) after the first static parameter in the subroutines parameter list. One real parameter is required. Each additional argument sent to the subroutine can be accessed using a pointer. SUB AddItUp(num as INT, ... ), FLOAT In order to access the unknown number of arguments use the VA_START function to get a pointer to the first variable argument. VA_START requires the name of the last actual parameter. DEF pParam as POINTER Access the variable using any dereferencing operator flTemp = #<FLOAT>pParam Use pointer math to retrieve successive arguments pParam+=4 The variable arguments are passed without regard to type and size information. It is up to the programmer to determine the size of each argument sent to the subroutine. A common methods is to use an index variable or formatting string. For example The USING function uses a variable number or arguments whose type and size depends on a formatting string. All numeric variable types are passed by value. To keep inline with industry standards all floating point types are converted to a DOUBLE before being passed. Strings and UDT's are passed by reference. Variable sizes are listed in the Variables topic. References to strings and UDT's take up four bytes. For a complete example see the sample file variable_arguments.eba included with the distribution. |