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:
|
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 writtenas
:DIM
MyFunctionPointerAS
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
MyTypeDYNAMIC
A$[
]
DYNAMIC
FN[
]
AS
Foo_TYPE BAS
INTEGER
END
TYPE
DIM
mtAS
MyTypeREDIM
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$
[
0
]
(
mt.A$[
0
]
)
, mt.FN$[
0
]
(
mt.A$[
1
]
)
[
1
]
(
mt.A$[
0
]
)
, mt.FN$[
1
]
(
mt.A$[
1
]
)
[
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:
|
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:
|
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:
|
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:
|
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
aINPUT
aCOLOR
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=
y1TO
y2FOR
x=
x1TO
x2LOCATE
y, x,0
" "
;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, jTYPE
MYARRAY a[
3
]
[
3
]
END
TYPE
DIM
matAS
MYARRAYFOR
i=
0
TO
2
FOR
j=
0
TO
2
mat.a[
i]
[
j]
=
i*
10
+
jNEXT
NEXT
"Initialized data to: "
FOR
i=
0
TO
2
FOR
j=
0
TO
2
[
i]
[
j]
NEXT
NEXT
TRY1(
mat,3
,3
)
getchar(
)
SUB
TRY1(
MymatAS
MYARRAY, n1, n2)
DIM
k, h"Using a TYPE to hold the array: "
FOR
k=
0
TO
n1-
1
FOR
h=
0
TO
n2-
1
[
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
myfloatAS
fltarray2DCALL
foo(
&
myfloat)
[
1
,1
]
SUB
foo(
MyfloatAS
fltarray2DPTR
)
Myfloat-
>CA![
1
,1
]
=
5.33
END
SUB
Example 6:
Using a DYNAMIC
array.
GLOBAL
DYNAMIC
CA![
100
,2
]
CALL
foo(
CA)
[
1
,1
]
SUB
foo(
MyfloatAS
FLOATPTR
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
FLOATLOCAL
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
"["
, I,","
, J,","
, K,"] = "
, XXX!(
A, I, J, K)
NEXT
KNEXT
JNEXT
IKEYPRESS
END
PROGRAM
FUNCTION
XXX!(
BAS
FLOATPTR
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 OPTIONAL
parameters
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)
{
}