8.1 The requires clause
The requires clause has a number of options. Any number of requirements can be specified in a comma separated list.
requires header " "; requires body " ";
These are just literal floating insertions.
requires tagname;
These are floating insertions identified by name.
requires package "name";
The requirements are to be found in the file {config/name.fpc}. This not only specifies floating insertion of header files, but also linking of libraries and dependencies.
type mytype = "mytype*" requires finaliser "fred";
This specifies that the C function fred
is to be called
on a value by the garbage collector for finalisation.
instead of the default call which is
pointer->mytype::~mytype()
that is, a call to the destructor.
requires scanner "myscanner";
This annotation specifies a scanner for the object. It accepts a pointer to the object and reports any internal pointers to the collector. This allows you take data types like C++ {vector<T*>} and scan through all the elements of the vector using standard C++ iterators. (Note that at this time these scanners cannot be polymorphic! This is essential and should be implemented in a later version. The existing code is sufficient, though not efficient, in handling Judy arrays.)
At this time, the function must be declared like:
void *myscanner( collector_t *collector, gc_shape_t *shape, void *pp, unsigned long dyncount, int reclimit )
requires property "fred";
This annotation adds a string property to a symbol. These are primarily for internal use. These properties are currently recognized:
"needs_gc" // a function needs the garbage collector "needs_ptf" // a function needs a pointer to the thread frame "pure" // a function is pure and does not access global variables "generator" // a function is a generator and has side effects "virtual" // the function is a virtual member of a typeclass
The property needs_gc
implies the property needs_ptf
since the
pointer to the garbage collector is stored in the thread frame.
The thread frame is the "global store" of your program (but we use
a class object and not C static storage!) However needs_ptf
is a property
of a function whereas needs_gc
is a property of a type, which propagates
to a function if the function uses any of those types. The annotations
are required because Felix can't see into your C code.
An example of use from the library:
private body mkjudy = """ """ ; private body j1free = """ """ ; private type J1Array_ = "void*" requires scanner "::flx::gc::generic::Judy1_scanner", header ' ', finaliser ' ', j1free ; _gc_pointer _gc_type J1Array_ type J1Array = "void**" requires property "needs_gc"; gen _ctor_J1Array: 1 -> J1Array = "_mkjudy(FLX_POINTER_TO_THREAD_FRAME, &@0)" requires mkjudy, property "needs_gc" ;
The property needs_ptf
tells Felix a function must accept an extra argument
which is a pointer to the thread frame (and to pass it when the function
is called). Passing this pointer around is expensive. Many simple C like
functions do not require any data from the thread frame. By default therefore,
functions created by binding to C are considered not to require the thread frame
pointer. In turn, functions, including Felix functions, using them, may not
need the thread frame pointer. The requirement is forced on the function
if any of the functions it calls require it (since it has to have a copy
of the pointer to pass it to them).
A function with the pure
property is one which does not access
any variables outside itself. Accessing constants (that is, actually
unchanging values) is OK. Code which calls pure functions can safely be
moved about within the scope for which their arguments are invariant,
since the returned value is invariant in that scope since the function
only depends on its arguments. The pure
property may also be passed
on to the C++ compiler (gcc at least has such an annotation, although it
is not known if it is effectively used). Purity is primarily intended to
enable code motion optimisations.