ExpandCollapsePrev Next Index

+ 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 "stuff";
  requires body "stuff";

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 =
      """
        void **_mkjudy(FLX_APAR_DECL ::flx::gc::generic::gc_shape_t *jptr_map){
          typedef void *voidp; // syntax
          void **m = new (*PTF gcp, *jptr_map, false) voidp; 
          *m=0;
          return m;
        }
      """
    ;
  
    private body j1free =
      """
        void _j1free(::flx::gc::generic::collector_t*,void *p) {
          //printf("Free J1Array %p\\n",p);
          JError_t je;
          Judy1FreeArray((void**)p, &je); 
        }
      """
    ;
    private type J1Array_ = "void*"
      requires 
        scanner "::flx::gc::generic::Judy1_scanner",
        header '#include "flx_judy_scanner.hpp"',
        finaliser '_j1free',
        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.