Theory: Eiffel from C using the C-Eiffel Call-In Library

As with most problems in software engineering, the answer lies in choosing the right sort of indirection, and the first step to calling Eiffel from Python lies in calling Eiffel from C. Since the Python-to-C interface is well-documented (allowing for extension modules to be written in C for run-time speed), calling Eiffel from C is the first step.

The SmallEiffel compiler was written to accommodate this. While not as simple to use as the external keyword that allows Eiffel code to call C functions (and was used extensively in the exposure of the Python/C API through SWIG), SmallEiffel's C-Eiffel Call-In Library (CECIL) does allow the intrepid applications developer to expose some or all of the functions of Eiffel to the C world. To expose functionality, the developer simply lists the functions she wishes to export in a form suitable for CECIL: the exported function name, the class the function is from, and the name of the feature as it appears in the class. In fact, the SWIG-generated Eiffel classes were already using CECIL, producing the following file:

Example 11-1. The file produced by SWIG

- The name of the include C file : 
-- The features you want to call from C :
string_from_external		STRING			from_external
string_to_external		STRING			to_external
character_array_to_external	ARRAY[CHARACTER]	to_external
integer_array_to_external	ARRAY[INTEGER]		to_external
real_array_to_external		ARRAY[REAL]		to_external
double_array_to_external	ARRAY[DOUBLE]		to_external

Note the format of the file; the first line contains the name of the C header file which will be generated (here,; the following lines contain an exported feature name on the left, followed by the name of the class and then the name of the feature from that class. For example, the STRING.from_external feature appears in the string.e file as

Example 11-2. The STRING.from_external feature

   from_external(p: POINTER) is
         -- Internal `storage' is set using `p' (may be dangerous because
         -- the external C string `p' is not duplicated).
         -- Assume `p' has a null character at the end in order to 
         -- compute the Eiffel `count'. This extra null character
         -- is not part of the Eiffel STRING.
         -- Also consider `from_external_copy' to choose the most appropriate. 
            storage := storage.from_pointer(p);
            count := 0;
            storage.item(count) = '%U'
            count := count + 1;
         capacity := count + 1;
         capacity = count + 1;
         p = to_external

The SmallEiffel compile command, executed using the -cecil parameter, generates the following stub in eiffel-glue.h (the header file specified in the file):

Example 11-3. The generated stub in eiffel-glue.h for the STRING.from_external function

typedef void* T8
void string_from_external(void* C,T8 a1);

The C parameter above, a void pointer, represents an opaque pointer to the desired Eiffel object. In many ways, this looks and feels similar to the Python/C API, where many features were represented by C functions which took a pointer to the Python object as the first argument. Any Eiffel feature of any class may be exposed simply by including the appropriate line in (it should be noted that the SmallEiffel compiler will not generate code for a feature unless an object of the required class is built somewhere in the application, which can result in cryptic errors if one is not careful--in fact, it is not a bad idea to include a "dummy" object creation somewhere to ensure the required code is generated, which is the approach taken by SWIG).

And with that, and the generation of a header file, our Eiffel class features are laid bare for use through C. How, then, to expose them to Python?