Navigation: Language Reference > 2 - Program Source Code Format > PROCEDURE Prototypes >====== Prototype Parameter Lists ====== | |
Topics in this help section:
Parameters of Unspecified Data Type
Passing Named GROUPs, QUEUEs, and CLASSes
General Syntax
[CONST] [REF] type [ label ]
<;[CONST] [REF] type [ label ] >
type [ label ] = default
The parameter list in a PROCEDURE prototype is a comma-delimited list of the data types to pass to the PROCEDURE. The entire parameter list is enclosed in the parentheses following the PROCEDURE keyword (or the name). Each parameter's type may be followed by a space then a valid Clarion label for the parameter (which is ignored by the compiler and only documents the purpose of the parameter). Each numeric value-parameter (passed by value) may also include an assignment of a constant value to the type (or the documentary label, if present) that defines the default value to pass if the parameter is omitted.
Any parameter that may be omitted when the PROCEDURE is called must be included in the prototype's parameter list and enclosed in angle brackets ( <; > ) unless a default value is defined for the parameter. The OMITTED procedure allows you to test for unpassed parameters at runtime (except those parameters which have a default value).
Example:
MAP
MODULE('Test')
MyProc1 PROCEDURE(LONG) !LONG value-parameter
MyProc2 PROCEDURE(<;LONG>) !Omittable LONG value-parameter
MyProc3 PROCEDURE(LONG=23) !Passes 23 if omitted
MyProc4 PROCEDURE(LONG Count, REAL Sum) !LONG passing a Count and REAL passing a Sum
MyProc5 PROCEDURE(LONG Count=1, REAL Sum=0) !Count defaults to 1 and Sum to 0
END
END
See Also:
Value-parameters
Value-parameters are “passed by value.” A copy of the variable passed in the parameter list of the “calling” PROCEDURE is used in the “called” PROCEDURE. The “called” PROCEDURE cannot change the value of the variable passed to it by the “caller.” Simple assignment data conversion rules apply; Value-parameters actually passed are converted to the data type in the PROCEDURE prototype. Valid value-parameters are:
BYTE | SHORT | USHORT | LONG | ULONG | SREAL | REAL | DATE | TIME | STRING |
Example:
MAP
MODULE('Test')
MyProc1 PROCEDURE(LONG) !LONG value-parameter
MyProc2 PROCEDURE(<;LONG>) !Omittable LONG value-parameter
MyProc3 PROCEDURE(LONG=23) !Passes 23 if omitted
MyProc4 PROCEDURE(LONG Count, REAL Sum) !LONG passing a Count and REAL passing a Sum
MyProc5 PROCEDURE(LONG Count=1, REAL Sum=0) !Count defaults to 1 and Sum to 0
END
MODULE('Party3.Obj')
Func48 PROCEDURE(REAL),REAL,PASCAL !PASCAL calling convention
Func49 PROCEDURE(SREAL),REAL,C,NAME('_func49')!C convention and external function name
END
END
Variable-parameters
Variable-parameters are “passed by address.” A variable passed by address has only one memory address. Changing the value of the variable in the “called” PROCEDURE also changes its value in the “caller.” Variable-parameters are listed by data type with a leading asterisk (*) in the PROCEDURE prototype in the MAP. Valid variable-parameters are:
*BYTE | *SHORT | *USHORT | *LONG | *ULONG | *SREAL | *REAL | *BFLOAT4 | *BFLOAT8 |
*DECIMAL | *PDECIMAL | *DATE | *TIME | *STRING | *PSTRING | *CSTRING | *GROUP | *USTRING |
*BSTRING | *INT64 | *UINT64 | *VARIANT |
Example:
MAP
MODULE('Test')
MyProc2 PROCEDURE(<;*LONG>) !Omittable LONG variable-parameter
MyFunc1 PROCEDURE(*SREAL),REAL,C !SREAL variable-parameter, REAL return, C call conv
MyProc6 PROCEDURE(CONST *CSTRING Value) !Value retains a constant value in procedure
END
MODULE('Party3.Obj')
Func4 PROCEDURE(*CSTRING),REAL,C,RAW !Pass CSTRING address-only to C function
Func47 PROCEDURE(*CSTRING),CSTRING,C,RAW !Returns pointer to a CSTRING
END
END
Passing Arrays
To pass an entire array as a parameter, the prototype must declare the array's data type as a Variable-parameter (“passed by address”) with an empty subscript list. If the array has more than one dimension, commas (as position holders) must indicate the number of dimensions in the array. The calling statement must pass the entire array to the PROCEDURE, not just one element.
Example:
MAP
MainProc PROCEDURE
AddCount PROCEDURE(*LONG[,] Total,*LONG[,] Current) !Passing two 2-dimensional arrays
END
CODE
MainProc !Call first procedure
MainProc PROCEDURE
TotalCount LONG,DIM(10,10)
CurrentCnt LONG,DIM(10,10)
CODE
AddCount(TotalCount,CurrentCnt) !Call the procedure passing the arrays
AddCount PROCEDURE(*LONG[,] Total,*LONG[,] Current) !Procedure expects two arrays
CODE
LOOP I# = 1 TO MAXIMUM(Total,1) !Loop through first subscript
LOOP J# = 1 TO MAXIMUM(Total,2) !Loop through second subscript
Total[I#,J#] += Current[I#,J#] !increment TotalCount from CurrentCnt
END
END
CLEAR(Current) !Clear CurrentCnt array
Parameters of Unspecified Data Type
You can write general purpose procedures which perform operations on passed parameters where the exact data type of the parameter may vary from one call to the next by using untyped value-parameters and untyped variable-parameters. These are polymorphic parameters; they may become any other simple data type depending upon the data type passed to the procedure.
Untyped value-parameters are represented in the prototype with a question mark (?). When the procedure executes, the parameter is dynamically typed and acts as a data object of the base type (LONG, DECIMAL, STRING, or REAL) of the passed variable, or the base type of whatever it was last assigned. This means that the “assumed” data type of the parameter can change within the PROCEDURE, allowing it to be treated as any data type.
An untyped value-parameter is “passed by value” to the PROCEDURE and its assumed data type is handled by Clarion's automatic Data Conversion Rules. Data types which may be passed as untyped value-parameters:
BYTE | SHORT | USHORT | LONG | ULONG | SREAL |
REAL | BFLOAT4 | BFLOAT8 | DECIMAL | PDECIMAL | DATE |
TIME | STRING | PSTRING | CSTRING | USTRING | BSTRING |
GROUP(treated as a STRING) | Untyped value-parameter(?) | Untyped Variable-parameter (*?) |
The RAW attribute is valid for use if the untyped value-parameter (?) is being passed to external library functions written in other languages than Clarion. This converts the data to a LONG then passes the data as a C/C++ “void *” parameter (which eliminates “type inconsistency” warnings).
Untyped variable-parameters are represented in the PROCEDURE prototype with an asterisk and a question mark (*?). Within the procedure, the parameter acts as a data object of the type of the variable passed in at runtime. This means the data type of the parameter is fixed during the execution of the PROCEDURE.
An untyped variable-parameter is “passed by address” to the PROCEDURE. Therefore, any changes made to the passed parameter within the PROCEDURE are made directly to the variable which was passed in. This allows you to write polymorphic procedures.
Within a procedure which receives an untyped variable-parameter, it is not safe to make any assumptions about the data type coming in. The danger of making assumptions is the possiblity of assigning an out-of-range value which the variable's actual data type cannot handle. If this happens, the result may be disastrously different from that expected. See Also: ANYANY (any simple data type)
Data types which may be passed as untyped variable-parameters:
BYTE | SHORT | USHORT | LONG | ULONG | SREAL | REAL | BFLOAT4 |
BFLOAT8 | DECIMAL | PDECIMAL | DATE | TIME | STRING | PSTRING | CSTRING |
Untyped variable-parameter (*?) | USTRING | BSTRING |
The RAW attribute is valid for use if the untyped variable-parameter (*?) is being passed to external library functions written in other languages than Clarion. This has the same effect as passing a C or C++ “void *” parameter.
Arrays may not be passed as either kind of untyped parameter.
Example:
PROGRAM
MAP
Proc1 PROCEDURE(?) !Untyped value-parameter
Proc2 PROCEDURE(*?) !Untyped variable-parameter
Proc3 PROCEDURE(*?) !Untyped variable-parameter (set to crash)
Max PROCEDURE(?,?),? !Procedure returning Untyped value-parameter
END
GlobalVar1 BYTE(3) !BYTE initialized to 3
GlobalVar2 DECIMAL(8,2,3)
GlobalVar3 DECIMAL(8,1,3)
MaxInteger LONG
MaxString STRING(255)
MaxFloat REAL
CODE
Proc1(GlobalVar1) !Pass in a BYTE, value is 3
Proc2(GlobalVar2) !Pass it a DECIMAL(8,2), value is 3.00 - it prints 3.33
Proc2(GlobalVar3) !Pass it a DECIMAL(8,1), value is 3.0 - it prints 3.3
Proc3(GlobalVar1) !Pass it a BYTE and watch it crash
MaxInteger = Max(1,5) !Max procedure returns the 5
MaxString = Max('Z','A') !Max procedure returns the 'Z'
MaxFloat = Max(1.3,1.25) !Max procedure returns the 1.3
Proc1 PROCEDURE(? ValueParm)
CODE !ValueParm starts at 3 and is a LONG
ValueParm = ValueParm & ValueParm !Now Contains '33' and is a STRING
ValueParm = ValueParm / 10 !Now Contains 3.3 and is a REAL
Proc2 PROCEDURE(*? VariableParm)
CODE
VariableParm = 10 / 3 !Assign 3.33333333… to passed variable
Proc3 PROCEDURE(*? VariableParm)
CODE
LOOP
IF VariableParm >= 256 THEN BREAK. !If passed a BYTE, BREAK will never happen
VariableParm += 10
END
Max PROCEDURE(Val1,Val2) !Find the larger of two passed values
CODE
IF Val1 > Val2 !Check first value against second
RETURN(Val1) ! return first, if largest
ELSE !otherwise
RETURN(Val2) ! return the second
END
See Also:
Entity-parameters
Entity-parameters pass the name of a data structure to the “called” PROCEDURE. Passing the entity allows the “called” PROCEDURE to use those Clarion commands that require the label of the structure as a parameter. Entity-parameters are listed by entity type in the PROCEDURE prototype in the MAP. Entity-parameters are always “passed by address.” Valid entity-parameters are:
FILE | VIEW | KEY | INDEX | QUEUE | WINDOW | REPORT | BLOB |
A REPORT can be passed as the parameter to a procedure prototyped to receive a WINDOW, since internally they use the same passing structure.
Example:
MAP
MODULE('Test')
MyFunc2 PROCEDURE(FILE),STRING !FILE entity-parameter, returning a STRING
ProcType PROCEDURE(FILE),TYPE !Procedure-parameter type definition
MyFunc4 PROCEDURE(FILE),STRING,PROC !May be called as a procedure without warnings
MyProc6 PROCEDURE(FILE),PRIVATE !May only be called by other procs in TEST.CLW
END
END
Procedure-parameters
Procedure-parameters pass the name of another PROCEDURE to the “called” PROCEDURE. Procedure-parameters are listed by the name of a prototype of the same type in the PROCEDURE prototype in the MAP (which may or may not have the TYPE attribute). When called in executable code, the “called” PROCEDURE must be passed the name of a PROCEDURE whose prototype is exactly the same as the procedure named in the “called” procedure's prototype.
Each parameter in the list may be followed by a valid Clarion label which is completely ignored by the compiler. This label is used only to document the parameter to make the prototype more readable, or to duplicate the PROCEDURE definition statement. Each passed parameter's definition may also include the assignment of a constant value to the data type (or the documentary label, if present) that defines the default value to pass if the parameter is omitted.
Example:
MAP
MODULE('Test')
ProcType PROCEDURE(FILE),TYPE !Procedure-parameter type definition
MyFunc3 PROCEDURE(ProcType),STRING !ProcType procedure-parameter, returning a STRING,
END !must be passed a procedure that takes a FILE
END !as a parameter
Passing Named GROUPs, QUEUEs, and CLASSes
Passing a GROUP as a Variable-Parameter, or a QUEUE as an Entity-Parameter, to a PROCEDURE does not allow you to reference the component fields within the structure in the receiving PROCEDURE . You can alternatively pass a “named” GROUP or QUEUE to achieve this. You may also name a CLASS in the same manner to allow the receiving procedure to access the public data members and methods of the CLASS.
To reference the component fields within the structure, place the label of a GROUP, QUEUE, or CLASS structure in the receiving PROCEDURE's prototype parameter list as the data type for the parameter. This passes the parameter “by address” and allows the receiving procedure to reference the component fields of the structure (and the public methods of a CLASS pass in this manner).
The data actually passed as the parameter must always have a similar structure (defined with the same data types) for its component fields. The GROUP or QUEUE actually passed can be a “superset” of the named parameter, as long as the first fields in the “superset” group are the same as the GROUP or QUEUE named in the prototype. The actually passed CLASS object can also be a derived class of the CLASS named in the prototype. The “extra” fields in the passed GROUP, QUEUE, or CLASS are not available for use in the receiving procedure.
The GROUP, QUEUE, or CLASS named in the parameter list does not need to have the TYPE attribute, and does not have to be declared before the procedure's prototype, but it must be declared before the PROCEDURE that will receive the parameter is called. This is the only instance in the Clarion language where the compiler allows such a “forward reference.”
Use Field Qualification syntax to reference the members of the passed group in the receiving procedure (LocalName.MemberName). The member fields of the structure are referenced by the labels given them in the group named as the data type in the prototype–not the labels of the fields in the structure actually passed in. This allows the receiving procedure to be completely generic, regardless of what actual data structure is passed to it.
Example:
PROGRAM
MAP
MyProc PROCEDURE
AddQue PROCEDURE(PassGroup PassedGroup, NameQue PassedQue)
END !AddQue receives a GROUP defined like PassGroup and
! a QUEUE defined like NameQue
PassGroup GROUP,TYPE !Type definition – no memory allocated
F1 STRING(20) ! GROUP with 2 STRING(20) fields
F2 STRING(20)
END
NameGroup GROUP !Name group
First STRING(20) !first name
Last STRING(20) !last name
Company STRING(30) !This extra field is not available to the receiving
END !procedure (AddQue) since PassGroup only has two fields
NameQue QUEUE,TYPE !Name Queue, Type definition – no memory allocated
First STRING(20)
Last STRING(20)
END
CODE
MyProc
MyProc PROCEDURE
LocalQue NameQue !Local Name Queue, declared exactly the same as NameQue
CODE
NameGroup.First = 'Fred'
NameGroup.Last = 'Flintstone'
AddQue(NameGroup,LocalQue) !Pass NameGroup and LocalQue to AddQue procedure
NameGroup.First = 'Barney'
NameGroup.Last = 'Rubble'
AddQue(NameGroup,LocalQue)
NameGroup.First = 'George'
NameGroup.Last = 'O''Jungle'
AddQue(NameGroup,LocalQue)
LOOP X# = 1 TO RECORDS(LocalQue) !Look at what's in the LocalQue now
GET(LocalQue,X#)
MESSAGE(CLIP(LocalQue.First) & ' ' & LocalQue.Last)
END
AddQue PROCEDURE(PassGroup PassedGroup, NameQue PassedQue)
CODE
PassedQue.First = PassedGroup.F1 !Effectively: LocalQue.First = NameGroup.First
PassedQue.Last = PassedGroup.F2 !Effectively: LocalQue.Last = NameGroup.Last
ADD(PassedQue) !Add an entry into the PassedQue (LocalQue)
ASSERT(NOT ERRORCODE())
See Also: