ExpandCollapsePrev Next Index

+ 4.1 Special Magic

Whilst Felix is generally able to tell a lot about Felix code that you write, it does not understand the C code to which you bind types and functions. Therefore, some annotations are required so it can handle your C code correctly. Of course, we already know you have to specify the type. Here is some more magic:

+ 4.1.1 Plain Old Datatype

If you define a type which is roughly an ordinary old C datatype, you should add an annotation:

  pod type Int = "int";

This actually tells Felix that the data type does not have a useful C++ destructor, and so Felix does not have to schedule execution of a finaliser for the data type when the garbage collector reaps an object of that type. This effect propagates, for example a tuple of pods is also a pod. Felix does not know if a C++ destructor is trivial or not, so it will always ensure garbage collected values are finalised unless told otherwise.

It is always safe to leave out the pod annotation as it is only an optimisation. It is not safe to specify it if it isn't correct, as this may lead to the failure to invoke destructors, typically leading memory leaks.

+ 4.1.2 Incomplete type

You can define a type which is cannot be constructed. This is useful when you only want to deal with pointer to that type. For example:

  incomplete type Void = "void";
  incomplete type mutex_rep = "mutex_t";
  typedef mutex = &mutex_rep;

If you bind the actual type of an immobile object, that is, one which cannot be copied, you should specify that it is incomplete. Of course it is immobile, not incomplete, but the effect is the same: you cannot instantiate it. Felix requires all types to be copyable.

If you leave off this annotation where it is required and then accidentally try to do the wrong thing with it, such as copy a value of that type, Felix will not complain: but the C++ compiler will.

+ 4.1.3 _gc_pointer, _gc_type

The annotation shown here:

  _gc_pointer type fred = "fred*";

tells Felix that the type is a pointer which the garbage collector should trace. Make sure the type really is a pointer! Values of the pointed to type should be allocated by the Felix allocator on the Felix heap.

This annotation is rarely useful by itself, unless you're binding a system type. Here is how we bind Google's RE2 type, which is an immobile object:

  private type RE2_ = "::re2::RE2";
  _gc_pointer _gc_type RE2_ type RE2 = "::re2::RE2*";
  ctor RE2 : string -> RE2 = "new (*PTF gcp, @0, false) RE2($1)";

The first line binds the actual type used by Google. The second line says that in Felix we'll actually be working with pointers to a heap allocated object of that type. The third line shows how to write a constructor.

The _gc_type annotation tells Felix it must generate an RTTI (shape) record for the type RE2_ but also it tricks the compiler into using that type in the code where {@0} is written, instead of the type RE2. The {@0} annotation usually generates the shape of the function return type RE2: however in this case it is fooled into generate shape information for RE2_ instead, which in C is {::re2::RE2}.

The shape is required for the Felix allocation function underlying the special overload of C++ {operator new} used by Felix above, which ensure the garbage collector knows about the allocation.