PROCEDURES
Updated: Aug 31, 2009

Declare, Subroutines & Functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Declare statements allow a one-pass compiler to develop a list of user symbols
to process source code and provide information about external modules such as
system and other .dlls and linkable .obj modules. 

For procedures defined in your source code:

  DECLARE SUB | FUNCTION name [(param1 As type[, param2 As type[, etc]])] _
    [As type]

For procedures defined in external .dll or .obj modules:

  DECLARE SUB | FUNCTION name LIB "libname" [ALIAS "aliasname"] _
    [(param1 As type[, param2 As type[, etc]])] [As type]

The syntax above may look complicated, but hold your breath.  Items in [.] are
optional.  DECLARE FUNCTION requires "As type" at the end.  ALIAS is used only
when LIB is defined and is optional.  "_" continues a statement on the next
line.

Let's start with simple examples:


DEClARE SUB
~~~~~~~~~~~
  Declare SUB MySub
  'code
  Call MySub
  'code

  SUB MySub 'here we define what we do in MySub
  'code
  IF {condition} THEN Exit Sub
  'more code
  END SUB

Above, MySub is declared and defined.  In the SUB code, one can EXIT SUB
at any time.

In the DECLARE statements, type is the variable type.  Please see "Dimension".
The symbols used for param1, param2, etc are dimensioned automatically, as
shown in compiler Symbol Table output.


DECLARE FUNCTION
~~~~~~~~~~~~~~~~
Now a simple FUNCTION example:

  DEFREAL10 x,y,z
  Declare FUNCTION MyFun As LONG  
  'code
  x=100: y=2: z = MyFun 
  'code

  FUNCTION MyFun As LONG
  'MyFun can access dimensioned variables and does not need arguments
  RESULT = -1 'if we want to set a default result
  'code
  IF x > 0 AND y > 0 THEN RESULT = x / y ELSE Exit FUNCTION
  'MyFun returns either -1 or x/y
  'more code
  END FUNCTION

In the FUNCTION code, you can also assign the result to the name of the
function.  Above, MyFun = -1 would be the same as RESULT = -1.

Some compilers do not flag missing result statements, but instead "invent" a
return value of zero.  Such compilers are a case study in both insecure and
inefficient computing.

In HotBasic, absence of a defined function result as described above is assumed
to be a source code error, so you don't waste time debugging a program that does
not work because of a missing result statement.

If your SUB or FUNCTION has arguments, you just add them in (... ).

A simple SUB or FUNCTION naming convention implements PROPERTY SET and PROPERTY
GET as described in "Variables and Types".


==Calling Windows API functions (hotapi.bas):

What if the SUB or FUNCTION is not defined in your own code?  API calls are
illustrated in hotapi.bas, and may be as simple as:

  Declare FUNCTION GetLastError LIB "kernel32" ALIAS "GetLastError" () _
    As LONG
  Declare FUNCTION Beep LIB "kernel32" (BYVAL freq As LONG,_
    BYVAL duration As LONG) As LONG

  DIM rval As LONG
  'code which might cause an error condition
  rval=GetLastError  
  rval=Beep(400,2000)

"BYVAL" is not necessary and may be omitted.  The LIB is kernel32.dll and the
ALIAS may be defined or omitted.  ALIAS allows you to refer to the procedure
with one name in your code, but HotBasic will substitute the ALIAS name, if
present, at compile time.

All API function arguments are 32-bit quantities.  However, if the declared
argument is "As STRING", "As <UDT_Name>", or "As VARIANT", HotBasic
automatically posts the argument as a pointer.  

In addition, "As DOUBLE" and "As INT64" may also be used, if the API specifies
a DOUBLE or INT64 argument.

"BYREF" in the argument list of a DECLARE ... LIB statement provokes a Warning
compile ErrorLevel and is otherwise ignored.  Indeed, it is not needed for
STRING, VARIANT or UDT arguments, since a pointer automatically is used.  For
other pointer arguments, use "As DWORD" in the DECLARE statement and the
pointer as the argument in the CALL statement (Please see @ and VARPTR in
Numeric Functions.)


==Calling Procedures in a Non-Windows .DLL (hotcall.bas and hotdll.dll):

hotcall.bas shows that a HotBasic executable can use funtions in a .DLL
compiled by HotBasic or obtained elsewhere.  The required Declare statements
are identical in form.  HotBasic provides two run-time errors:

(1) LIB .dll file not found and (2) function name not found in LIB .dll.


==Calling Procedures in .OBJ Modules (hotlink.bas and hotobj.obj):

How do we link an .obj module compiled by HotBasic or obtained elsewhere?
For each procedure in the external module, we have a DECLARE like above with
one crucial difference: the full name of the file is specified.

  Declare SUB DoNeatThing LIB "neatthings.obj" [(args)] 
  'code
  DoNeatThing[(args)] 'or Call DoNeatThing[(args)] calls the subroutine.

or use a Declare FUNCTION as above, but with LIB "neathings.obj".  The .obj
file is included in the executable produced.

If .obj modules are stored in a .lib file, then, in the example above,
"neatthings.obj" would be changed to "neatthings.lib".  Run this .bat program
from your HotBasic directory to make a .lib file from all .obj modules (from
$APPTYPE OBJ) in a directory:

=====HBlib.bat
Echo Usage:   HBlib Obj_File_Path Lib_Name
Echo Example: HBlib c:\hot\trig trigfunc
bin\link -lib "%1\*.obj" "/out:%2.lib"
=====HBlib.bat

Above, all the .obj files in c:\hot\trig are placed in trigfunc.lib in your
HotBasic directory.  Copy trigfunc.lib to your project directory to link its
modules with your program.

If the SUB has arguments, you may use (..) or not, but no space before (.

  DoNeatThing(MyValue1, MyValue2, etc)
  'or DoNeatThing MyValue1, MyValue2, etc

Note: If the external module is not stdcall (Windows compatible), you may have
to DECLARE it without arguments and use PUSH to place the arguments on the
stack in the correct order before calling the SUB or FUNCTION.  Please see
Statements > Advanced Techniques.


==CDECL calling convention often used in C language software

Merely change the DECLARE keyword to CDECL to use CDECL calling conventions.

CDECL FUNCTION xyz LIB "abc.lib" ... etc

where abc.lib contains procedures compiled for CDECL calling conventions --
which are the same as STD (stdcall), except arguments are not cleaned from
the stack. 

CDECL FUNCTION xyz STD (...args...) As LONG

where CDECL may be used to prepare procedures used for CDECL callbacks and for
$APPTYPE DLL or OBJ which may be used in a C language environment.

Usage is simple:  just use CDECL in place of the DECLARE keyword.  CDECL has
an effect only in declaration statements with LIB or STD keywords.


==Summary where CDECL or DECLARE may be used in the examples

For procedures in $APPTYPE DLL or OBJ or for certain callback procedures in
your main program, the STD keyword may be used and HotBasic will manage
argument retrieval from the stack.  STD is positioned where LIB might otherwise
appear in a DECLARE statement -- after the procedure name and before any
arguments.  Please see example in hotstd.bas in HotThing.

In $APPTYPE DLL or OBJ, only DECLARE ... STD procedures may be called by code
in another module ("public").  That is, other DECLARE and GOSUB procedures are
"private".

For $APPTYPE DLL, only procedures with DECLARE ... STD statements are exported.

All DECLARE statements for STD procedures will have arguments equivalent to
4-byte values, exactly as in Microsoft API function definitions.  That is,
a STRING, VARIANT or UDT is always a pointer (DWORD), numbers are always
INTEGER, DWORD, etc.  However, DOUBLE and INT64 arguments are posted on the
stack as two DWORDs.

When the procedure is called, a BYTE, WORD or SHORT value may be used as an
argument, HotBasic will convert these to the type specified in the DECLARE or
CDECL statement for the procedure.

STD can be used for callback procedures.  Example:

  DECLARE SUB MyWndProc STD (hwin as DWORD, _
    pmsg as DWORD, wpar as DWORD, lpar as DWORD)

and the RETVAL keyword is used to return a result in the CPU eax register.

Callback procedures may also be declared as FUNCTION; however, DECLARE SUB and
use of RETVAL on exit is more efficient.


GOSUB
~~~~~
With GOSUB, no DECLARE is needed (please see HotBasic Tips) and any LABEL may
start, or be an alternate entry point into, a subroutine.

  GOSUB ThisSub: GOSUB ThatSub
  'code

  ThisSub:  'this is just a LABEL
  'code
  RETURN

  ThatSub:
  'code A
  ThatSubEntry2:
  'code B
  RETURN
  
Notice that GOSUB ThatSub runs code A and B, while using the alternate entry
point -- GOSUB ThatSubEntry2, runs code B only.

If you place all your GOSUB (and similer GUI event) procedures after the END
statement, your program flow will not "fall into" this code.

After all source code is processed, HotBasic checks if all GOTO, GOSUB and
event procedure LABELS exist.

A line LABEL may help describe what a SUB does:  GOSUB ComputeIncome.  But it
could be an integer number, too, as in older versions of Basic (example in
hottest.bas).


==SUMMARY

Code for a SUB/FUNCTION in your main program module or in an external module
(a linked .obj or .lib file, a .dll, .ocx, .ax, etc) may be viewed as Public
or Private.  Generally, only Public procedures may be called from another
module and therefore arguments must be placed on the stack.

Within any module, code may call either its Public or Private procedures.
[Please see HotBasic Tips for more detail on when and how the stack is used
for arguments.]

(1) Private:  All variable types and UDT's can be used as arguments in
SUB/FUNCTION DECLARE and CALL statements.  Arguments are not placed on the
stack -- an inefficient practice that makes most other compilers obsolete.

For FUNCTION return values, all variable types may likewise be used (except
VARIANT and UDT).  GOSUB procedures are all Private.

(2) Public:  DECLARE statements contain the LIB or STD keyword.

DECLARE arguments are 4-byte variables, "As STRING", "As VARIANT",
"As <UDT_Name>" or "As DOUBLE".  If an argument is STRING, VARIANT or UDT,
its pointer is automatically used.

Usually, the result of a Public FUNCTION is a 32-bit value (e.g., As LONG).
However, HotBasic will autodetect the result of a LIB or STD FUNCTION declared
"As STRING" and assume the return value is a pointer which is delivered to the
calling code as a STRING data type.

Note: a STD callback procedure acts like the Public category above.  Though it
may be located in your main program, it is called by an external module --
usually the OS.


+ Penthouse (registered) version

Copyright 2003-2009 James J Keene PhD
Original Publication: Oct 8, 2003
