FunctionSubs.md 22 KB

FUNCTION and SUB Procedures

The main difference between a FUNCTION and a SUB is that a FUNCTION returns a value to the FUNCTION call expression.

FUNCTION...END FUNCTION procedure


Syntax:
 
 FUNCTION TheName([Parameters][AS data type])[,AS data type]
 DIM ReturnValue
     [Statements]
 FUNCTION = ReturnValue
     [Statements]
 END FUNCTION

 Parameters:

  • TheName This is the name of the function. The value returned by the function can be indicated by using a data type indicator symbol at the end of TheName. For example, if the value returned is a string, the name of the function would be TheName$. If the function returned a double, its name would be TheName#.

    The data type of the value returned by the function also can be indicated by appending an AS data type phrase. For example, if the value returned is a string, the phrase would be AS STRING. If the function returned a double, the phrase would be AS DOUBLE.

  • ([Parameters][AS data type]) is zero or more comma separated variables used to pass values to the function. The data type of each parameter must be indicated. BCX uses this information to construct prototypes for the functions so the data type must be explicit for any variables except integers. For example, if a string is being passed, Parameters$ or Parameters AS CHAR would be used. If a DOUBLE is being passed, Parameters# or Parameters AS DOUBLE would be used.

    By default, string variables are passed by reference, while numeric variables are passed by value. To pass numeric values by reference instead of by value, see the BYREF qualifier.

  • The FUNCTION = ReturnValue statement causes an immediate exit from the function returning to the function caller the value contained in ReturnValue.

    While other BASIC dialects use TheName of the FUNCTION as the variable to return the value created by the function, BCX can use any user defined variable name as long as the variable is the correct data type.

    If an empty string is to be returned explicitly, use either

     
     FUNCTION = ""
    

    or

     
     RAW temp$
     temp$ = ""
     FUNCTION = temp$
    

    or else use

     
     FUNCTION = NUL$
    
  • ReturnValue is the return value of the function. Like all variables ReturnValue must be dimensioned before use. This can be done within the function or externally as a GLOBAL.

Example:

 
 DIM A!
 DIM X!
 DIM Y!

 X! = 2.2
 Y! = 3.1

 A! = Myfunc!(X!, Y!)
 PRINT A!

 FUNCTION Myfunc!(M!, P!)
  FUNCTION = (M! * P!)
 END FUNCTION

Remarks:

Note Well ! Do not modify a literal value or string used as an argument in the FUNCTION calling statement. For example, the attempt to modify "literal" in,

 
 DIM str1$

 str1$ = FOO$("literal")
 PRINT str1$

 FUNCTION FOO$(a$)
  a$ = "k"
  FUNCTION = a$
 END FUNCTION

will cause a crash when the program is run.

OVERLOADED or OPTIONAL FUNCTION procedures are NOT allowed in a user defined type structure.

BCX has support for functions in single line declaration/assignments, for example,

 
 DIM RAW RetVal = foo(x, y, z) AS INTEGER

BCX allows static SUB/FUNCTION to be coded as:

 
 PRIVATE FUNCTION MyFunction() AS INTEGER
 
 PRIVATE SUB MySub()

User defined string functions that rely on other string functions usually need to use an intermediate string.

Example:

 
 DIM A$

 A$ = Blurb$(65, 66, 67)
 PRINT A$

 FUNCTION Blurb$(A, B, C)
  DIM Z$
  Z$ = CHR$(A) & CHR$(B) & CHR$(C)
  FUNCTION = Z$
 END FUNCTION

FUNCTION declarations emit a TYPE that may be reused.

 
 DIM FUNCTION Foo$(a$)
 Any further declarations can be written as:
 DIM MyFunctionPointer AS Foo_TYPE

The following is an example which uses the emitted FUNCTION TYPE declaration as a member of a user defined type.

 
 DIM FUNCTION Foo$(a$)
  
 TYPE MyType                                 
   DYNAMIC A$[]
   DYNAMIC FN[] AS Foo_TYPE
   B AS INTEGER
 END TYPE
  
 DIM mt AS MyType
  
 REDIM mt.A$[2]
 REDIM mt.FN[3]
  
 mt.A$[0] = "Hello " 
 mt.A$[1] = "World"
 mt.FN[0] = LCASE$
 mt.FN[1] = MCASE$
 mt.FN[2] = UCASE$
 
 PRINT mt.FN$[0](mt.A$[0]), mt.FN$[0](mt.A$[1])
 PRINT mt.FN$[1](mt.A$[0]), mt.FN$[1](mt.A$[1])
 PRINT mt.FN$[2](mt.A$[0]), mt.FN$[2](mt.A$[1])

CALLBACK FUNCTION...END FUNCTION procedure

OVERLOADED or OPTIONAL FUNCTION procedures are NOT allowed in a User Defined TYPE structure.


Syntax 1:
 
 CALLBACK FUNCTION CBFuncName()
     [Statements]
 END FUNCTION

 Parameters:

  • The following C code is emitted by BCX from the above syntax

     
    
     LRESULT CALLBACK CBFuncName(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
     {
      [Statements];
    
      return DefWindowProc(hWnd, Msg, wParam, lParam);
    
  • CALLBACK FUNCTION syntax does not use any function parameters because BCX automatically emits the following :

    
     (HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
    
  • When using the automatically emitted function parameters hWnd, Msg, wParam, and lParam, it is important to remember that they must be written as case sensitive.
  • CALLBACK FUNCTION does not require a FUNCTION = ReturnValue statement because BCX automatically emits the following function return statement:

     
     return DefWindowProc,(hWnd,Msg,wParam,lParam);
    
  • Using

     
     CALLBACK FUNCTION CBFunc()
      FUNCTION = CallWindowProc OR DefWindowProc, DefMDIChildProc, AND DefFrameProc
     END FUNCTION
    

    is supported. Using anything other than the Win32API CallWindowProc or DefWindowProc, DefMDIChildProc, and DefFrameProc functions as a function return in the BCX CALLBACK FUNCTION is not supported.

  • WARNING ! Do not use CALLBACK FUNCTION with a resource file based dialog box function because the DefWindowProc function can not be used with a resource file based dialog box function. For a resource file based dialog box function, use Syntax 2 shown below.

Syntax 2:
 
 FUNCTION CBFuncName(hWnd AS HWND, _
                       Msg AS UINT, _
                  wParam AS WPARAM, _
                  lParam AS LPARAM) _
               AS data type CALLBACK
  [Statements]
 FUNCTION = ReturnValue
 END FUNCTION

 Parameters:

  • Any parameter names can be used but the parameter data types are fixed by the CALLBACK Win32API define.
  • The FUNCTION = ReturnValue statement causes an immediate exit from the function, returning to the caller the value contained in ReturnValue.
  • data type Specifies the data type of the value returned by the function. For example, in a CALLBACK FUNCTION which returns TRUE or FALSE either a BOOL data type or an INT_PTR Dataype would be used.
  • ReturnValue The value returned by the function. This value must be the same data type as specified in the FUNCTION statement. For example, a resource file based dialog box CALLBACK function should return TRUE or FALSE. Many CALLBACK functions use the DefDlgProc function, the DefWindowProc function or one of their variants to supply a return value.
    WARNING ! The DefDlgProc function or the DefWindowProc function must not be called by a dialog box callback procedure; doing so results in recursive execution.

If a return value is not required then the CALLBACK syntax can be expressed as for a SUB using VOID for the CALLBACK data type.


Syntax 3:
 
 FUNCTION CBFuncName(hWnd AS HWND, _
                       Msg AS UINT, _
                  wParam AS WPARAM, _
                  lParam AS LPARAM) _
                   AS VOID CALLBACK
   [Statements]
 END FUNCTION

 Parameters:

  • Any parameter names can be used but the parameter data types are fixed by the CALLBACK Win32API define.
  • VOID indicates that a return value is not expected to be made.

SUB ... END SUB procedure

OVERLOADED or OPTIONAL SUB procedures are NOT allowed in a User Defined TYPE structure.


Syntax 3:
 
 SUB SubName(Parameters)
  [Statements]
 END SUB

 Parameters:

  • SubName is the name of the subroutine. Do not use a data type with the SubName because an error will occur in translation. Subname does not need a data type because a SUB does not return a value.
  • ([Parameters][AS data type]) is zero(the use of Parameters is optional) or more comma separated variables used to pass values to the subroutine. The data type of each parameter must be indicated. BCX uses this information to construct prototypes for the subroutines so the data type must be explicit for any variables except integers. For example, if a string is being passed, Parameters$ or Parameters AS CHAR would be used. If a DOUBLE is being passed, Parameters# or Parameters AS DOUBLE would be used.

    By default, string variables are passed by reference, while numeric variables are passed by value. To pass numeric values by reference instead of by value, see the BYREF qualifier.

Note Well ! Do not modify a literal value or string used as an argument in the SUB calling statement. For example, the attempt to modify "literal" in,

 
 CALL FOO("literal")

 SUB FOO(a$)
  a$ = "k"
 END SUB

will cause a crash when the program is run.

CALL statement

Although subroutines can be invoked with or without the CALL keyword remember that if a SUB does not take arguments and parentheses are omitted, then CALL MUST be used. Following are the only valid methods of calling a SUB with no arguments:

CALL Foo()

or CALL Foo

or Foo()

Let's start with a small Example:

 
 CALL Greeting("Hello from the World of Programming!")

 Greeting("Hello from the World of Programming!")

 SUB Greeting(a$)
  PRINT a$
 END SUB

Values can be passed to subroutines. The data type(declarator) of the parameter passing the argument to the subroutine must be indicated. BCX uses this information to construct prototypes for the subroutines and functions so the data type must be explicit for anything except integers. For example, if a string is being passed SUB Passit(A$) would be used not SUB Passit(A). If a DOUBLE is being passed SUB Passit(A#) would be used.

 
 SetConsoleTitle("BCX Demonstration")
 
 COLOR 15, 1
 frame(15, 10, 65, 20)
 DIM a
 INPUT a
 COLOR 7, 0
 CLS
 
 SUB frame(x1, y1, x2, y2) ' while interesting, this is meant only as a
   DIM x                     ' sample SUB.  The run-time PANEL statement
   DIM y                     ' is much faster and more flexible
   FOR y = y1 TO y2
     FOR x = x1 TO x2
       LOCATE y, x, 0
       PRINT " ";
     NEXT
   NEXT
 END SUB

Note Well ! Do not modify a literal value or string used as an argument in the FUNCTION or SUB calling statement. For example, the attempt to modify "literal" in,

 
 CALL FOO("literal")

 SUB FOO(a$)
  a$ = "k"
 END SUB

will cause a crash when the program is run.

Recursive calls can be made from a subroutine. What this means is that the subroutine can be called from within itself. Here's an Example:

 
 Parse("1,2,3,4,5,666,777,88888,99999,101010101010101010")

 SUB Parse(A$)
  LOCAL Sep
  LOCAL B$
  Sep = INSTR(A$ , ",")
  IF Sep > 0 THEN
   B$ = LEFT$(A$, Sep - 1)
   PRINT B$
   A$ = MID$(A$, Sep + 1, 256)
   Parse(A$)  ' --- Recursive Call ---
  ELSE
     PRINT A$
  END IF
 END SUB

Subroutines can be prematurely exited by using the EXIT SUB statement. If you want to use values created in a Subroutine outside of it, then GLOBAL variables must hold the values.

 
 JumpOut()

 PRINT a$

 SUB JumpOut()
 GLOBAL a$
 a$ = "JumpOut"
  IF a$ = "JumpOut" THEN
   EXIT SUB
  END IF
  PRINT "This line is not executed."
 END SUB

Arguments and Parameters

Arguments are passed to procedures through parameters.

Passing Arguments to FUNCTION and SUB Procedures

Example 1: This example demonstrates passing a one-dimensional integer array to a function.

 
 DIM A_RAY[10] AS INTEGER
 
 Foo(&A_RAY[0], 3)
 
 PRINT A_RAY[3]
 
 SUB Foo(BYVAL A[], Index AS INTEGER)
  A [Index] = 12345
 END SUB

Example 2: This example demonstrates filling a global two-dimensional array in a function.

 
 GLOBAL DYNAMIC A[10][10] AS INTEGER
 GLOBAL a AS INTEGER
 GLOBAL b AS INTEGER
 
 CALL FILL(A,10,10)
 
 FOR a = 0 TO 9
  FOR b = 0 TO 9
   ? A[a][b];
  NEXT
  ?
 NEXT
 
 ! getchar();
 
 SUB FILL(B AS INTEGER PTR PTR, Dim1 AS INTEGER, Dim2 AS INTEGER)
  'The data type descriptor for the B parameter,
  'in the line above, requires one "PTR"
  'for EACH DIMENSION of the passed array.
  RAW a1
  RAW b1
  Dim1--
  Dim2--
  FOR a1 = 0 TO Dim1
    FOR b1 = 0 TO Dim2
      B[a1][b1] = a1 + b1
    NEXT
  NEXT
 END SUB

Example 3: treats the multi-dimensioned array as a one-dimensioned array inside the sub, and does a little simple maths to calculate the position of elements.

 
 DIM i, j
 DIM mat[3][3]
 
 FOR i = 0 TO 2
  FOR j = 0 TO 2
   mat[i][j] = i * 10 + j
  NEXT
 NEXT
 
 PRINT "Initialized data to: "
 FOR i = 0 TO 2
  FOR j = 0 TO 2
   PRINT mat[i][j]
  NEXT
 NEXT
 
 TRY2(mat[0], 3, 3)
 
 KEYPRESS
 
 SUB TRY2(Mymat[], n1 , n2)
  DIM k, h
 
  PRINT "Using simple maths: "
  FOR k = 0 TO n1-1
    FOR h = 0 TO n2-1
      PRINT Mymat[k * n2 + h]
    NEXT
  NEXT
 END SUB

Example 4: stores the array in a TYPE variable, and passes the address of the TYPE to the function.

 
 DIM i, j
  
 TYPE MYARRAY
   a[3][3]
 END TYPE
  
 DIM mat AS MYARRAY
  
 FOR i = 0 TO 2
   FOR j = 0 TO 2
     mat.a[i][j] = i*10+j
   NEXT
 NEXT
  
 PRINT "Initialized data to: "
 FOR i = 0 TO 2
   FOR j = 0 TO 2
     PRINT mat.a[i][j]
   NEXT
 NEXT
  
 TRY1(mat, 3, 3)
 getchar()
  
 SUB TRY1(Mymat AS MYARRAY, n1, n2)
   DIM k, h
  
   PRINT "Using a TYPE to hold the array: "
   FOR k = 0 TO n1 - 1
     FOR h = 0 TO n2 - 1
       PRINT Mymat.a[k][h]
     NEXT
   NEXT
 END SUB

Example 5: Here is an easy way to pass mutidimensional static arrays by enclosing the array in a TYPE.


 TYPE fltarray2D
   CA![100, 2]
 END TYPE
   
 GLOBAL myfloat AS fltarray2D
   
 CALL foo(&myfloat)
 PRINT myfloat.CA![1, 1]
   
 SUB foo(Myfloat AS fltarray2D PTR)
   Myfloat->CA![1, 1] = 5.33
 END SUB

Example 6: Using a DYNAMIC array.


 GLOBAL DYNAMIC CA![100, 2]
   
 CALL foo(CA)
 PRINT CA[1, 1]
   
 SUB foo(Myfloat AS FLOAT PTR PTR)
  'The data type descriptor for the Myfloat parameter,
  'in the line above, requires one "PTR"
  'for EACH DIMENSION of the passed array.
  Myfloat[1, 1] = 5.33
 END SUB

Example 7: The following simple example shows how to pass DYNAMIC multi-dimensional numeric arrays to a procedure by reference

 
 OPTION BASE 1
 GLOBAL DYNAMIC A[2, 2, 2] AS FLOAT
 LOCAL I, J, K
  
 A[1, 1, 1] = 1
 A[1, 1, 2] = 2
 A[1, 2, 1] = 3
 A[1, 2, 2] = 4
 A[2, 1, 1] = 5
 A[2, 1, 2] = 6
 A[2, 2, 1] = 7
 A[2, 2, 2] = 8
  
 FOR I = 1 TO 2
   FOR J = 1 TO 2
     FOR K = 1 TO 2
       PRINT "[", I, ",", J, ",", K, "] = ", XXX!(A, I, J, K)
     NEXT K
   NEXT J
 NEXT I
 KEYPRESS
 END PROGRAM
 
 FUNCTION XXX!(B AS FLOAT PTR PTR PTR, i, j, k)
  'The data type descriptor for the B parameter,
  'in the line above, requires one "PTR"
  'for EACH DIMENSION of the passed array.
  FUNCTION = 3.14 * B[i, j, k]
 END FUNCTION
 

Example 8: The following example shows how to pass TYPE and UNION structures to to a procedure

 
 TYPE STA
  DIM A$
  DIM B AS INTEGER
 END TYPE

 TYPE STB
  DIM A AS INTEGER
  DIM B AS INTEGER
 END TYPE

 ' union of all the structures
 UNION STUNION
  Atype AS STA
  Btype AS STB
 END UNION

 'the actual structure that is going to be used
 TYPE STALL
  DIM ID AS INTEGER 'tells what structure in the union is going to be used
  DIM UN AS STUNION
 END TYPE

 DIM st AS STALL

 st.ID = 1
 st.UN.Atype.A$ = "How is this?"

 CALL ExeSTX(&st)

 st.ID = 2
 st.UN.Btype.A = 2

 CALL ExeSTX(&st)

 getchar();

 SUB ExeSTX(s AS STALL PTR)
  SELECT CASE s->ID
    CASE 1
    ? s->UN.Atype.A$
    CASE 2
    ? s->UN.Btype.A
  END SELECT
 END SUB

BCX is able to correctly translate a parameter formed from a call to a FUNCTION or SUB as in this example.

 
 SUB WalkDir(szPath$, szSpec$, bRecurse, _
  WalkResult(szTPath$, szFPath$, bType) AS SUB)
 END SUB

OPTIONAL Parameters in Functions and Subroutines

An OPTIONAL parameter used in a FUNCTION or SUB declaration is defined with a default value. This value is used if the OPTIONAL parameter is omitted in the calling function or subroutine procedure. If a value is specified in the OPTIONAL parameter in the calling procedure, then that value is used instead of the default.

OPTIONAL parameters are not permitted in DLL functions.

Many BCX functions and subroutines have optional parameters already built in. For example, all the BCX GUI functions have optional window style and extended window style parameters which provide a default that is replaced when an OPTIONAL value is specified.


 Syntax 1:
 
 FUNCTION MyFunc% OPTIONAL(Normalparam$, Optionalparam$ = "BCX")
 Syntax 2:
 
 FUNCTION MyFunc% OPTIONAL(Normalparam$, Optionalparam AS data type = Value)

In the function or subroutine call, OPTIONAL parameters can be omitted from the right side of the parameter list.

The 'value' assigned to OPTIONALparameters must be a scalar value, that is a string literal or number, not a function or some other complex expression.

In the function definition, when using an empty string as an OPTIONAL parameter argument, a NULL POINTER is passed to the function or subroutine. The NULL POINTER is passed as the text string (null) not as an empty string.

Study the example below to see how this "C" limitation can be handled using BCX.

 
 Alert()
 Alert("Hello BCX Lovers!")
 Alert("Announcing OPTIONAL ARGUMENTS", "Very Cool!", 64)

 SUB Alert OPTIONAL(_Message$ = "" , _Title$ = "", Style = 0)
  LOCAL Message$, Title$
  Message$ = _Message$ & ""
  Title$   = _Title$   & ""
  IF TRIM$(Message$) = "(null)" THEN
   Message$ = "Alert ... Alert ...Alert!"
  END IF
  IF TRIM$(Title$)   = "(null)" THEN
   Title$   = "Generic Default Title"
  END IF
  IF Style = 0 THEN Style = MB_OK
  MSGBOX Message$, Title$, Style
 END SUB

$FSSTATIC directive

The $FSSTATIC directive causes the BCX translator to place the storage class specifier 'static' before function and subroutine definitions in the output C code.

Like many of the other BCX meta-statements, the $FSSTATIC command is like a toggle. The first instance of $FSSTATIC turns on the function and the second instance turns it off.

 
 $FSSTATIC
 SUB GoForward(iBrowser AS IWebBrowser2 PTR)

 END SUB
 $FSSTATIC

translates to


 static void GoForward(IWebBrowser2 *iBrowser)
 {
 }

and without $FSSTATIC, BCX translates

 
 SUB GoForward(iBrowser AS IWebBrowser2 PTR)

 END SUB

to

 

 void GoForward(IWebBrowser2 *iBrowser)
 {
 }