| **Navigation:**  [[introduction.htm|Language Reference]] > 2 - Program Source Code Format > PROCEDURE Prototypes >====== Prototype Parameter Lists ====== | [[prototype syntax.htm|{{btn_prev_n.gif|Previous page}}]][[introduction.htm|{{btn_home_n.gif|Return to chapter overview}}]][[procedure return types.htm|{{btn_next_n.gif|Next page}}]] | | || **Topics in this help section:** [[prototype parameter lists.htm#generalsyntax|General Syntax]] [[prototype parameter lists.htm#valueparameter|Value Parameters]] [[prototype parameter lists.htm#variableparameters|Variable Parameters]] [[prototype parameter lists.htm#passingarrays|Passing Arrays]] [[prototype parameter lists.htm#pudt|Parameters of Unspecified Data Type]] [[prototype parameter lists.htm#entityparams|Entity Parameters]] [[prototype parameter lists.htm#procparms|Procedure Parameters]] [[prototype parameter lists.htm#passnamedgroups|Passing Named GROUPs, QUEUEs, and CLASSes]] __**General Syntax**__ **[CONST]** **[REF]** //type //[ //label //] <;**[CONST] [REF]**// type //[ //label //] //>// //type //[ //label //] //= default// {{blk2blue.jpg|blk2blue.jpg}} | **CONST** | An optional qualifier for the parameter which is valid only on a variable-parameter. This means that the parameter being passed by address may not be updated in the procedure. It is treated as if it were a constant value. | | **REF**{{newcnet.jpg|NewCNet.jpg}} | The** REF** keyword is added to Clarion# to provide compatibility with other .NET languages. It's used to mark a parameter as "passed by reference". The REF keyword can be used before the type name in the parameter declaration. | | //type// | The data type of the parameter. This may be a value-parameter, variable-parameter, array, unspecified data type, entity, procedure-parameter, or a named GROUP, QUEUE, or CLASS. | | //label// | An optional documentary label for the parameter. This label is not required and is placed in the prototype for documentation purposes only. | | //<; >// | Angle brackets indicate the parameter is omittable. The OMITTED procedure detects the omission. All parameter //types// can be omitted. | | //= default// | A //default// value indicates the numeric parameter is omittable, and if omitted, the //default// value is passed. The OMITTED procedure will not detect the omission--a value is passed. Valid only on simple numeric //types//. | 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:** [[map declare procedure prototypes .htm|MAP]] [[member identify member source file .htm|MEMBER]] [[module specify member source file .htm|MODULE]] [[procedure define a procedure .htm|PROCEDURE]] [[class object declaration .htm|CLASS]] [[ref parameter passed by reference .htm|REF]] **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:** ANY[[any any simple data type .htm|ANY (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:** [[map declare procedure prototypes .htm|MAP]] [[member identify member source file .htm|MEMBER]] [[module specify member source file .htm|MODULE]] [[procedure define a procedure .htm|PROCEDURE]] [[class object declaration .htm|CLASS]] **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:** [[map declare procedure prototypes .htm|MAP]] [[member identify member source file .htm|MEMBER]] [[module specify member source file .htm|MODULE]] [[procedure define a procedure .htm|PROCEDURE]] [[class object declaration .htm|CLASS]]