
Having looked at some customers code for interfacing Eiffel to C and creating callbacks from C to Eiffel, I frequently found that things were not done properly, resulting in potential memory corruption or crashes.
First, I like to point out the CECIL documentation which contains everything you need to know about interfacing C with Eiffel. It might be overwhelming so below is a simplified tutorial.
Typing
At the C level, Eiffel objects are known under 2 possible types:
EIF_REFERENCE: a direct reference to an Eiffel objectEIF_OBJECT: an indirect (or protected) reference to an Eiffel object
The reason for having 2 types lies in the garbage collector (GC) which moves objects around at runtime. As a consequence, a direct reference is not always valid, meaning that if you use an EIF_REFERENCE variable, then it might points to the previous location of the object. This is why we have EIF_OBJECT, those variable are automatically updated by the GC. To access the Eiffel objects, one has to use the eif_access
Callbacks
The corresponding C signature of an Eiffel routine is the same as the Eiffel routine except that there is an extra first argument for the target object of the call. In other words, the Eiffel code
a.f (i)
is equivalent to the C code
f(a, i)
. The types used for the declaration of the C routine are simply the Eiffel types prefixed by `EIF_' for the basic types (INTEGER, NATURAL, POINTER, ....) and EIF_REFERENCE for all the other types. Therefore for the following Eiffel routine:
has the following C signature:
void my_routine (EIF_REFERENCE Current, EIF_INTEGER i, EIF_REFERENCE s)
.
Passing an Eiffel routine to a C external
I'm describing here an alternative to the CECIL way of doing things which is much lighter and simpler to use in my opinion. This is the technique used in all the Eiffel Software libraries where a callback is needed.
Basically if you want to pass the function pointer of the Eiffel routine my_routine above, then you simply have to wrap the following C routine:
void set_callback_function (void (*) (EIF_REFERENCE, EIF_INTEGER, EIF_REFERENCE));
set_callback_function (a_routine: POINTER) is external "C inline useĀ %"my_c_header.h%"" alias "set_callback_function ($a_routine);" end
set_callback_function ($my_routine)
Comments
When you eif_protect()
When you eif_protect() something in C, is this considered a reachable reference to the GC? i.e. if the Eiffel side of code loses reachability to an object that was passed to C and eif_protect'ed, will it ever be collected?
It won't be collected until
It won't be collected until eif_wean() is called.