Isis passes 5 arguments to C functions when they are called:Value *my_func(Script *script, char *proc_name, int call_data, int argc, Value **argv);
The rest of these macros check the types of individual arguments. The
first 7 check the primitive types. checkNumber() checks if
the argument is either an integer or a real number. The rest check if
the argument is a homogeneous list (contains values of only one type).
If the type is incorrect, the macro prints an error message and the
entire function returns NullValue.
The type-checking macros expect the argument number (starting from 0)
and a string name for the argument to be used in error messages. If
the argument doesn't have a name, just pass NULL.
For example, if your function expects exactly two arguments, an
integer "loop parameter" and a string, then the beginning of your
function could look like this:
Here is a small example:
To free a value is to say that you are deleting one reference to it.
Isis keeps an internal reference count and releases the memory used by
a value only when there are no more references to it.
If you are not returning or storing a value that you created inside
your function, or if you delete any other global reference to a value,
call freeValue() on it. You will very rarely need to
do this since usually you will only create values if you intend to
return them. Never call freeValue() on arguments to your
function. Here is an example using the newListva() function
discussed earlier.
referValue() returns the same value passed to it.
You can also return NullValue, which is a special pre-defined
Isis value that represents Null in the interpreter.
NullValue functions just like any other Isis value, except
that you never have to worry about creating or freeing it. Please
note that you should NEVER return C's null pointer, NULL,
from any Isis function. This will result in annoying error messages.
Use NullValue instead:
Checking argument types
The very first thing to do in the function is make sure the expected
number of arguments was received. After that, the next thing to do is
check that each of those arguments is of the expected type. Isis
provides several efficient macros for this purpose:
The checkCount() macros check if exactly, at
least, or at most the specified number of arguments have
been received. If the count is wrong, the macro prints an error
message and entire function returns NullValue. The macro
will also print a "usage" message if you pass a string for the
usage argument. That string will be printed starting on the
line after the error message, and it may contain newline characters in
order to print on more than one line. If you don't want a usage
message to be printed, pass NULL.
Since the above macros automatically print error messages and return
NullValue if there is a problem, they are not appropriate for
all circumstances. In that case, you can also use the following
macros to directly check the type of an Isis value:
Each of these macros returns 1 or 0. You can check the type of an
argument by passing an item from the argv[] array to these
macros. For example:
Another way to check types is to use the typeof() macro:
typeof() returns an integer that corresponds to one of the
types defined by the following constants:
typeof() is best used in an switch statement, like this:
Extracting values
Once you have established that the arguments are of the correct type,
you are ready to actually use them to do something! There are several
macros and functions that extract the actual values from the
Value structure used by the arguments. Always use the macro
that corresponds to the type of value being extracted, and only after
that type has been verified using the macros shown above.
These first seven macros extract values of the seven basic Isis types.
Take notice of the return types: Integers and characters are returned
as their standard C counterparts. Real numbers are returned as type
real which is currently typedef'd as
double. Booleans are returned simply as integers (0 or 1).
Procedures are returned as a pointer to a Procedure structure
which you are not meant to access directly--it will be used in calls
to other helper functions. Addresses are returned, appropriately, as
generic pointers (void *). Lists are returned as an array of
pointers to Value structures (which should not be modified).
These 2 functions are used to extract an integer or a real number from
a value that could be either (determined using the isNumber()
macro).
These macros only operate on lists. The first returns the number of
items in a list, and the other returns the specified item in the list.
These functions work on values previously determined to be
homogeneous lists. Each automatically extracts the values and
puts them into an array that you have allocated. maxlen
specifies the array size. If the number of items in the list is
larger than the size of the array, the overflow is not extracted.
getString() also adds a null terminating character
'\0'
at the end of the extracted string. All of these
macros return the total number of items extracted.
Creating new values
The following functions create new Isis values, suitable for returning
from your function:
These above six functions simply create new basic Isis values.
These functions simply create homogeneous lists of basic Isis values.
Each function has 2 versions--one accepts an array and the other
accepts a variable number of arguments. Use whichever is most
convenient. In any case, be sure to pass the list's length as the
first argument.
This function allows you to create a heterogeneous list of only the
six basic Isis values (not lists). It accepts a variable number of
arguments. Pass the length of the list first. Each item in the list
is specified as a pair of arguments---the item's type and the item's
value. For specifying the types, use the same type identifiers from
before: IsisInt IsisReal IsisBool IsisChar IsisAddr IsisProc.
Here is an example:
In Isis this would appear as:
These final 2 functions create a list from Isis values that already
exists:
One takes an array of the values, and the other accepts a variable
number of arguments each of which is a value. You are responsible for
properly disposing of these values after calling the function (because
the functions will create their own references to the values). Please
read the important rules and examples below for more information...
Isis employs an efficient reference count system for handling
values and controlling deallocation. In order to maintain the
integrity of this system, you must follow these simple rules at all times:
Freeing values
The value returned in the above example will show up like this:
Referring to previously-created values
If you are returning or storing a value that was not created inside
your function, or any other time you create a new reference to a
value, call referValue() on it. This means if you want to
return or retain a value that was passed as an argument, you must call
referValue() on it before the function completes. When you
create new values inside your function, they are automatically
initialized with one reference, so unless you create additional
references, you do not need to call referValue() on them at
any time.
Returning values
Every Isis C function must return a pointer to a Value.
Values created with any of the creation primitives from before are
suitable for returning directly from your function. However, as
discussed before, if you want to return a value that was passed to the
function as an argument, you must call referValue() on it
since you are creating a new reference to it.
A Full example
The following is an example of an Isis C function that accepts an
integer and a string as arguments, prints the string the specified
number of times, and returns the original string. Usually an
operation as simple as this would be written directly in Isis, but
the individual pieces of this example may be useful to you as you
write your own C functions.
Other useful routines for Isis C functions:
This function check if two values are equal. Returns 1 or 0.
Checks if the value is a true value (as defined in the Isis reference
manual).
Prints a text representation of the value to the given file. All
values except for procedures are printed in a form that could be
re-read and evaluated by the interpreter at a later time.
If you pass one of the specifiers IsisNull IsisInt IsisReal
IsisBool IsisChar IsisAddr IsisProc IsisList for the type, this
function checks if all the elements are of the given type. Result is
nonzero if so, 0 otherwise.
If you pass 0 for the type, the function simply check if all the
elements in the list are of the same type (not any particular type).
If so, that type is returned, and 0 otherwise.
This creates a new Isis procedure that refers to a C function. You
pass the actual C function, a "name" for the procedure that will be
passed to it as proc_name and an integer that will be passed
to it as call_data. A pointer to the new procedure is
returned. You could then use the newProc() macro to create a
new procedure value.
Any time you create or destroy and external reference to an Isis
Procedure, use these routines in a similar manner to their
counterparts for Isis values. Note that procedures (Procedure
*) and procedure values (Value *) are two
different things. You should only to use these routines in the
extrememly rare case that you want to return or retain a
Procedure instead of a procedure Value.
Both of these functions apply an Isis procedure (possibly extracted
using the getProc() macro) to a set of values. In both, you
must specify the number of arguments you are passing as argc.
The first accepts the a variable number of arguments each of which is
a value, and the second accepts an array of the values instead. Use
whichever one is most convenient. You are responsible for properly
disposing of the argument values after the procedure exits. The
return value is the result of the call. For example, here is the
implementation of the Isis apply primitive:
Binding values into the top-level environment
All of these functions bind Isis values into the top level environment
of the interpreter. They work similarly to the new*() functions
described earlier, except that you must pass a string name for the
variable in Isis that will be bound to the value of interest.
bindValue() binds a value of any type, and as always you
should properly dispose of the value after calling this function.
bindCfunc() is simply a short-cut to calling
newCfunc() and bindProc() in sequence. These
functions will be heavily used when you are ready to add your
functions into the environment of your personal Isis interpreter.
Details are in the section on binding C into Isis.