Back | Forward |
The different parts in this chapter are:
The generic call interface is part of the SAA package of REXX. It has an
equivalent meaning of the function RXFUNCADD
. It defines a
function or subroutine by use of the following call:
RXFUNCDEFINE
(REXXName, Library, LibraryFunction, DefinitionStem)
RXFUNCDEFINE
. Its value must be a
stems name. A period may or may not follow immediately after the value.
The stem must contain special values which describe the parameters of the
function or procedure. The structure is described below.The return value returns the defined values as for RXFUNCADD and a special value indicating errors in the structure.
Example:
stem = 'setlocale' err = RxFuncDefine('SetCountry', 'libc', 'setlocale', stem)
In this example the variables setlocale.0
and others
describe a function setlocale in the library libc.
The call itself is done by using another stem and the defined function name.
The return value of the created generic function is the empty string. The return value may be changed by a special parameter to reflect the true return value of the underlying function. A normal usage is shown in the following example:
stem = 'setlocale' call SETCOUNTRY stem
The stem itself must follow special requirements. It is a tree-based structure with the description of the parameters, the return value, and the calling convention. Thus, a description stem has the following elements:
Modified element names can be choosen.
cdecl
,
pascal
, etc depending on possible values for the current
machine and system.
Immediately following this value there are two optional tokens delimited
by at least one whitespace. These tokens are as function
and with parameters
. These tokens change the usage of the
generic function as shown below.
0
is
possible if no arguments exist.The interpreter will build a data structure for each parameter in the
background. The values of the parameter stem will be copied to the internal
data structure on call. The library function or procedure is then called.
After the return, each parameter and the return value is filled back from the
internal data structure to allow the modification of the parameters by the
library call. This only happens when indirect
or a return value
is used, of course.
The return value and the parameters of a function share the same syntax. Both variables are stems describing the structure and type of it. Each stem element describes a new part of the parameter.
A part contains the following elements:
Modified element names can be choosen.
indirect
followed by the basetype.Possible values for type vary and depend on the system and machine. The interpreter should support at least the values:
container
as a structure. The variable 0 contains
the number of elements described in the variables 1, ...
recursively.
A container can share its definition with another container or container
branch using the like
keyword immediately following the
container
keyword. After the like
keyword the
name of the branch must be given. No further information is needed, and the
variables 0, 1, ... are not examined. The branch
or stem is examined instead of the current structure. Therefore, the
branch or stem must fulfil the container requirements.
array
for repeating structures. 0 contains
the number of elements in the array. The variable 1 describes
the type of one array element recursively.integer8
, integer16
, integer32
,
integer64
for signed whole numbers with the corresponding bit
size.unsigned8
, unsigned16
, unsigned32
,
unsigned64
for unsigned whole numbers with the corresponding
bit size.float32
, float64
for floating point numbers with
the corresponding bit size.char
stringX
, X is a positive whole number
describing a terminated string of the maximum size X excluding
the terminator. The termination is the typical termination for strings
in the systems's current environment and may be either a length descriptor
or a termination character like 0 for ASCII systems. The interpreter is
responsible to provide the proper termination.as function
option.The complete structure is evaluated in depth-first order. This allows nesting structures. The content itself is aligned in depth-first order, too; indirected values are separated in position, though.
The memory allocation block of the folling example should be allocated as follows.
stem.type = container stem.0 = 3 stem.1 = indirect float64 stem.2 = indirect integer8 stem.2 = integer32will lead to the following allocation block (assuming a pointer size of 32 bit).
Byte 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 indirect | indirect | integer32 | float64 |integer8 to float | to integer| | |
The interpreter must respect alignments only for indirections. Parts of a container will be considered as correctly aligned in all cases, it is the user's responsibility to assure this. The following example explains this.
stem.type = container stem.0 = 3 stem.2 = integer8 stem.2 = indirect integer16 stem.1 = indirect float64will lead to the following allocation block (assuming a pointer size of 32 bit).
Byte 0| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 integer8| indirect | indirect | s|int16| float64 | to integer| to float | s| |
Spare bytes are marked by a "s
".
A function defined by RXFUNCDEFINE
will always
accept only one parameter if neither as function
nor with
parameters
are given in the calltype. The meaning of these
special values are explained some paragraphs further.
The single parameter to a stem is formed very similar to the definition stem.
The interpreter will reassign all changed values of every argument to the values after the function call. It is a tree-based structure with the values of the parameters and the return value. Thus, a call stem has the following elements:
Modified element names can be choosen.
The interpreter will set a variable 0 after each call to reflect the parameter count. This is done at the very end of operation and can be used as a flag for success.
The values of the parameter stem will be copied to the internal
data structure on call. The library function or procedure is then called.
After the return, each parameter and the return value is filled back from the
internal data structure to allow the modification of the parameters by the
library call. This only happens when indirect
or a return value
is used, of course.
If as function
is given in the
calltype, the return value of the the underlying function call is
passed back as the return value of the generic function call. It is the content
of the value variable of the return part.
The interpreter may or may not accept this value in case of a compound return
value. It depends on the interpreter's ability to return stems.
No error is thrown if the called object has no return value, e.g. is a
procedure.
The return part of the Call Stem isn't set if this parameter is given.
If with parameters
is given in the calltype, the
different parameters to the underlying function are passed directly to the
generic function call as normal parameters in the defined order. This parameter
implies as function
if the return definition isn't the
empty string. Thus, every parameter to the generic function
call is passed to the underlying function.
Missing parameters are treated as NULL-pointers in case of
indirect
values. Other interpretations are not possible. On
return, NULL-pointers lead to a drop of the value variable or to
a recursive drop of every value variable of the whole stem's branch
in case of arrays or containers. Other interpretations are not possible.
Parameters are passed in a modification allowing fashion. This is called pass by reference. A copy of the value is used if the value is a constant.
The interpreter may or may not accept this value. The number of parameters must not be more than 10.
The return value and the parameters of a function share the same syntax. Both variables are stems holding values for the function call. Each stem element describes a new part of the parameter.
A part contains the following elements:
Modified element names can be choosen.
value
after the index name.
Values may be omitted. This is allowed for indirect values only. A NULL value
is passed in this case. Consequently, each variable's value is
dropped if the called function sets the corresponding variable to NULL.
In case of an indirected CONTAINER
or ARRAY
however,
the value must be set to the elements of the container or array
to reflect that this object shall not be set to NULL. No further elements
of the container or array will be examinated otherwise; on return from the
called function, value will be set to the number of elements, but
not if the container or array is an indirection with an actual value of NULL.
The interpreter will signal a NOVALUE
condition in all cases of
missing values where missing values are not allowed. The user has to install or
deinstall an appropriate handler.
The interpreter will signal a SYNTAX
condition if structual parts
are missing or the wrong number or parameters are passed. The interpreter
will signal this condition if numbers can't be represented or strings are
too long. Other errors are passed back as an error string with one exception:
A function with a calling convention defined as as function
will always raise a SYNTAX
condition in case of errors.
The interpreter will not signal a LOSTDIGITS
condition if numbers
can't be converted without rounding the least significant digit. Instead, the
interpreter assumes an infinite precision when writing back values ignoring
the NUMERIC DIGITS
value. The user shall either call the
FORMAT
builtin function or has to add a zero. This allows the
reusage of intermediate values in cases where it is needed.
The REXX interpreter may allow the user to call the
operating system directly. The user must omit all the arguments to
RXFUNCDEFINE
except the internal function name and the stem. The
interpreter may require the user to use a special name as the internal
function name.
Moveover the user must use predefined values like register names to be passed to the kernel. The interpreter's documentation will explain what variables of which type must be used. Systems without a dynamic link interface should include the Direct Operating System Call feature.
Because of the tail expansion of names in stems it is
possible to select a special character as a prefix for tail names. This can
change a variable name from stem.return.type
to
stem.!return.!type
.
The change happens immediately after selection and affects both definition stems as call stems. The function for changing the prefix is:
GCIPREFIXCHAR
(Prefix)
An empty prefix or a prefix containg a blank or D2C(0) is treated same;
a prefix isn't used any longer in this case. Otherwise a character of
!?_#$@
must be used.
Please note that only the first three characters are definitely defined as valid by the ANSI standard, and some interpreters won't accept anything else.
The generic call interface is highly dangerous. The Direct Operating System
Call will be the most dangerous feature. So why shall this be implemented?
The detour of a separate wrapper library won't reduce the chances of a
program crash or a system crash. Indeed, using RXFUNCADD
is as
dangerous as using RXFUNCDEFINE
because one may always run in a
fault.
It is best programming practice to use RXFUNCDEFINE
only when
no other chance of the wanted functionality exists.
Back | Forward |