CECIL interface overview
- Contents
- Eiffel basic types
- Protecting the Eiffel objects
- Other CECIL functions:
- Creating Eiffel objects from C: eif_create
- Protecting the objects returned by Eiffel functions.
- Getting the type id of an Eiffel type: eif_type_id
- Getting the type id of a generic type : eif_generic_type.
- Raising an eiffel panic: eif_panic.
- Releasing an Eiffel indirection pointer: eif_wean
- Getting the attribute from an Eiffel object: eif_attribute
- Getting the address of an Eiffel routine
- Enabling/Disabling the visible exception
- Creating an Eiffel string: eif_string
- Getting the return-type of an attribute: eif_attribute_type
- Getting the class name corresponding to a type id: eif_name
- Getting the type id of an Eiffel object: eif_type, eif_type_by_reference.
- Converting a C array into an Eiffel array: eif_make_from_c.
Eiffel basic types
The EIFFEL include files define types for every EIFFEL types:
- An Eiffel
INTEGER is an EIF_INTEGER, - An Eiffel
CHARACTER is an EIF_CHARACTER, - An Eiffel
REAL is an EIF_REAL, - An Eiffel
DOUBLE is an EIF_DOUBLE, - Eiffel references (any Eiffel objects that are not from a basic type) are EIF_REFERENCE (not protected, and can be moved),
- An Eiffel
POINTER is an EIF_POINTER, - An Eiffel
BOOLEAN is an EIF_BOOLEAN, - A Protected Eiffel object is an EIF_OBJECT (does not move, and should be accessed through eif_access. ).
Generally, you should use these types when implementing external C functions bound to be used from Eiffel or when you want to manipulate Eiffel objects from the C side. EIF_REFERENCE, EIF_OBJECT, EIF_POINTER all correspond in C to a (char *), but their semantic remains different in Eiffel.
c_foo (ptr: POINTER; obj: OBJECT): INTEGER
external
"C | %"your_file.h%""
alias
"foo"
end
In the C side, The C function foo
is defined as follow:
EIF_INTEGER foo (EIF_POINTER ptr, EIF_OBJECT obj)
{
/* some code */
}
In some cases, you may not be able to change the signature of a C function you want to use. In this case, you must describe its actual signature in the Eiffel code. On the C side, foo is already defined as below: int foo (void *arg1, char c, FILE *file)
{
/* some code */
}
To match the signature, you must declare it in Eiffel as: c_foo (arg1: POINTER; c: CHARACTER; file: POINTER): INTEGER
external
"C (void *, char, FILE *) : int | %""your_file.h%""
alias
"foo"
end
Not doing this would generally produce warnings during the C compilation, and it could crash with some C compilers.
To perform the conversion, here is the actual Eiffel types mapping to C types:
-
POINTER is compatible with any C pointer ( char *). -
INTEGER is a long. -
CHARACTER is an unsigned char. -
DOUBLE is a double. -
REAL is a float. -
BOOLEAN is an unsigned char (EIF_TRUE = '\01', EIF_FALSE = '\0') .
These are the current correspondences but they may change. The definition of each Eiffel type is located in $ISE_EIFFEL/bench/spec/$PLATFORM/include/eif_portable.h
.
More about EIF_OBJECT, EIF_REFERENCE, and basic expanded types
It is sometimes difficult to tell the difference between an EIF_OBJECT and an EIF_REFERENCE. An EIF_REFERENCE is an Eiffel reference. It corresponds to an Eiffel object in the Eiffel side.
An EIF_OBJECT is a safe and static indirection to an Eiffel reference. As the GC may move an Eiffel reference, this indirection is updated at every collection so that you do not need to know whether an Eiffel reference has moved or not. You must pass through this indirection to access the Eiffel reference (see
The basic expanded types are
Protecting the Eiffel objects
You can use the CECIL functions directly from a C program as well as from a C external called from Eiffel. If you encounter problems, they may be related to the handling of garbage collection. The programmer who uses CECIL has to be aware that all Eiffel objects can be moved or collected asynchronously by the Garbage Collector (GC). The cecil library provides the user with numerous macros and functions, which relieve the programmer from these kinds of low-level considerations (most of them are declared in $ISE_EIFFEL/bench/spec/$PLATFORM/include/eif_cecil. h).
Eiffel objects passed in a C external
An Eiffel object which is passed as an argument of a C external is automatically protected: the value passed to C is not a direct Eiffel reference. In fact, it is a temporary indirection pointer, which is only valid until the C external returned. This indirection pointer is to be used to access the direct reference of the Eiffel object with eif_access (eiffel_object) where eiffel_object is the argument (an EIF_OBJECT) of the C external which corresponds to the Eiffel object. Only Eiffel objects passed to C is automatically protected. This excludes basic expanded types, since they cannot be moved.
Accessing the direct reference to an Eiffel object: eif_access
# include "eif_hector. h"
EIF_REFERENCE eif_access (EIF_OBJECT object) /* Macro */
The GC moves the objects every time it runs a collection cycle. A collection cycle can only occur during an Eiffel call. This includes calls to Eiffel routines and calls to CECIL functions (other than eif_access). Thus, it may be unsafe to access a "raw" reference to an Eiffel object, (of type EIF_REFERENCE) "as is", since the latter can be obsolete after each collection. To avoid this, you must access a direct reference through a "protection", which is a safe, non-moving pointer (of type EIF_OBJECT). Call the macro eif_access as follows: eif_access (protection) , where protection is either a value returned by
Use eif_access to pass an Eiffel object to an Eiffel routine or to return the value of a C external. It is also unsafe to pass a direct Eiffel reference ( EIF_REFERENCE) to a C function which is not an Eiffel routine. Pass a protected indirection instead ( EIF_OBJECT). However, if you still intend to pass a direct reference, be very careful and make sure that you do not perform any Eiffel call after passing the reference to the C function and before reading it.
For example, in the following external:
c_foo (ptr: POINTER; obj: OBJECT): INTEGER
external
"C | %""your_file.h%""
alias
"foo"
end
the Eiffel run-time will protect obj, which can asynchronously move, and give a static and safe indirection to C.
Here is an example of how accessing obj: OBJECT:EIF_INTEGER foo (EIF_POINTER ptr, EIF_OBJECT obj);
{
/* Print the Eiffel object `obj', with the feature `print'
* from ANY. (do not forget to put `visible' the class
* ANY in the project configuration file.
*/
EIF_PROCEDURE ep;
EIF_TYPE_ID tid;
tid = eif_type_id ("ANY");
ep = eif_procedure ("print", tid);
(ep) (eif_access(obj),eif_access(obj));
}
Important rules when using eif_access:
- Never pre compute the value returned by eif_access.
Example:
Example:
Instead of the code above, it would have been dangerous to write: EIF_REFERENCE e_ref = eif_access (obj);
...
(ep) (e_ref, e_ref);
because e_ref is the direct reference to the Eiffel object when calling eif_access(). There is not guarantee that it will still be valid when the call to (ep) is done: meanwhile, e_ref may have been moved by the GC.
- Never use eif_access with encapsulated eiffel calls:
(eif_access in not an eiffel call) Example:
(ep) (eif_access (a), eif_string ("Hello world"));
/* eif_string is a macro returning a direct reference to an Eiffel string,
* which corresponds to the C string passed as its argument.
*/
Nothing guarantees that the direct reference returned by eif_access (a)
will be still valid when executing (ep): it may be obsolete after the Eiffel call eif_string ("Hello world"), which may invoke a collection cycle.
The correct code is
EIF_REFERENCE my_string;
/* some code */
my_string = eif_string ("Hello world");
(ep) (eif_access (a), my_string);
In this case, you do not need to protect my_string
since the GC is not likely to be triggered after the call to eif_string and before my_string
is given as argument in (ep). A collection is triggered only during Eiffel calls. If an Eiffel call had been performed, you would have had to use eif_protect (see paragraph 3. 2):
EIF_REFERENCE my_string;
EIF_OBJECT i_my_string;
/* some code */
my_string = eif_string ("Hello world");
i_my_string = eif_protect (my_string); /* Protect `my_string'. */
/* Some eiffel calls */
(ep) (eif_access (a), eif_access (i_my_string));
eif_wean (i_my_string); /* Release protection. */
Keeping a reference from C after an external call: eif_adopt
# include "eif_hector. h"
EIF_OBJECT eif_adopt (EIF_OBJECT object)
When passing Eiffel objects to C, you may need to keep a reference to them after the C external is called. Since the Eiffel run-time automatically will unprotect the Eiffel object references passed to a C external after execution, if one of the Eiffel objects is not referenced any longer from Eiffel, then the garbage collector will collect it because it is not aware that you may still need to reference this object from the C side.
Called within a C external, the function eif_adopt creates a user protection for the Eiffel object object passed to C ( object is a C external argument). This way, the GC cannot collect the Eiffel reference returned by eif_access(object) when the C external returned. It tells the GC to keep artificially a reference to this Eiffel reference from C. It returns the new indirection pointer (say returned_value) that must be used afterwards to access this direct Eiffel reference with eif_access (return_value). It is important to note that eif_adopt already takes an indirection pointer as unique argument. This is a temporary protection pointer: you can access the direct Eiffel reference with eif_access (object) only within the code of the C external. When the C external returns, eif_access (object) is NULL but eif_access (returned_value) remains valid until you release it with
Example:
In Eiffel:
c_foo (ptr: POINTER; obj: OBJECT): INTEGER
external
"C | %"your_file.h%""
alias
"foo"
end
c_display_and_release_obj
external
"C | %"your_file.h%""
alias
"display_and_release_obj"
end
On the C side:
EIF_OBJECT my_obj; /* Protection of the object of type OBJECT. */
EIF_INTEGER foo (EIF_POINTER ptr, EIF_OBJECT obj)
{
my_obj = eif_adopt (obj); /* Keeping a reference on it for
* for later use.
*/
/* some code */
}
void display_and_release_obj (void)
{
/* Display global object. */
EIF_PROCEDURE ep;
EIF_TYPE_ID tid;
tid = eif_type_id ("OBJECT");
ep = eif_procedure ("print", tid);
(ep) (eif_access(my_obj),eif_access(my_obj)); /* Print global object.*/
eif_wean (my_obj); /* Remove the protection on global object.*/
}
Between the call of c_foo
and c_display_obj
, the global object (eif_access (my_obj))
may not be referenced from Eiffel any longer. To prevent the GC from collecting it before the call to c_display_and_release_obj
, you must protect it with eif_adopt
in the C function foo
.
Other CECIL functions:
Creating Eiffel objects from C: eif_create
# include "eif_cecil. h"
EIF_OBJECT eif_create (EIF_TYPE_ID type_id)
All CECIL calls are not completed using C externals. It is possible to create and manipulate Eiffel objects from a C program and still reap benefits from the garbage collector and design by contract methodology provided by Eiffel (see also How to run a CECIL program ). This function does not call any creation procedure.
The CECIL function eif_create takes a type identifier type_id as argument (generally returned by
Example:
Creating an object of type "OBJECT":
#include eif_setup.h" /* for EIF_INITIALIZE and EIF_DISPOSE_ALL */
#include "eif_eiffel.h" /* for other exported routines from the Eiffel run-time */
main (int argc,char **argv,char **envp)
{
EIF_TYPE_ID tid;
EIF_OBJECT my_obj;
EIF_INITIALIZE(failure) /* Initialization of Eiffel run-time.
* This is to be done before any CECIL call.
*/
tid = eif_type_id ("OBJECT");
if (tid == EIF_NO_TYPE)
eif_panic ("No type id.");
my_obj = eif_create (tid); /* Create eiffel object, returns an indirection. */
/* some code */
eif_wean (my_obj); /* We do not need it any more. */
EIF_DISPOSE_ALL /* Reclaim memory allocated by Eiffel run-time. */
}
Protecting the objects returned by Eiffel functions.
# include "eif_hector. h"
EIF_OBJECT eif_protect (EIF_REFERENCE object)
This function is used to tell explicitly the GC that you want to keep a reference to an eiffel object from the C. It returns a static indirection pointer which is to be used afterwards to access the direct Eiffel reference object with eif_access (returned_value) where returned_value is the value returned by eif_protect . With this call, the GC artificially references object, so that it cannot collect it. It is unsafe to access directly (i.e without using eif_access) the Eiffel reference object, which may be obsolete after any collection cycle (the GC moves the objects). Ignore this rule, if you are sure that there is no Eiffel call after you pass the direct Eiffel reference to a C function and before you read it.
To release this artificial reference, call eif_wean (returned_value)
eif_protect is to be called on an EIF_REFERENCE returned by eif_attribute, eif_string, or the returned value of eif_reference_function.
See also eif_adopt, eif_create, eif_wean, eif_access.
Example: Assume that you want to access an attribute tab
of type
#include eif_setup.h"
#include "eif_eiffel.h"
main (int argc,char **argv,char **envp)
{
EIF_TYPE_ID tid;
EIF_OBJECT my_obj;
EIF_PROCEDURE emake; /* Creation procedure of "OBJECT". */
EIF_REFERENCE tab; /* direct reference to `tab' from "OBJECT". */
EIF_OBJECT i_tab; /* Protected indirection to `tab'. */
EIF_INITIALIZE(failure)
tid = eif_type_id ("OBJECT");
if (tid == EIF_NO_TYPE)
eif_panic ("No type Id.");
my_obj = eif_create (tid);
emake = eif_procedure ("make", tid); /* On the eiffel side: make is do ... end.. */
(emake) (eif_access (my_obj)); /* Call `make' on `eif_access(my_obj)'.*/
tab = eif_attribute( eif_access (my_obj), "tab", EIF_REFERENCE, NULL);
/* Return the attribute `tab' of type EIF_REFERENCE
* of the object `eif_access (my_obj)'.
*/
i_tab = eif_protect (tab); /* Here, protect `tab'. */
/* some code */
eif_wean (my_obj);
eif_wean (i_tab); /* We do not need it any more. */
EIF_DISPOSE_ALL /* Reclaim memory allocated by Eiffel run-time. */
}
Getting the type id of an Eiffel type: eif_type_id
# include "eif_gen_conf. h" # include "eif_gen_conf. h"
EIF_TYPE_ID eif_type_id (char *type_string)
Return the type identifier corresponding to the type described in type_string. If the type does not exist, is not visible or an instance of it is not declared in the root class, it returns EIF_NO_TYPE.
COMPATIBILITY:eif_type_id is equivalent to eif_type_by_name. Example: type_id of STD_FILES so as to call 'put_string'.
EIF_PROCEDURE p_put_string; /* 'put_string' from STD_FILES. */
EIF_TYPE_ID tid;
EIF_REFERENCE_FUNCTION fn_io; /* once function `io' from ANY (and STD_FILES by inheritance). */
EIF_REFERENCE o_io; /* Eiffel object `io' returned by once function*/
EIF_REFERENCE o_str; /* Eiffel string */
EIF_OBJECT i_io, i_str; /* safe indirection pointers to ``io' and Eiffel string. */
tid = eif_type_id ("STD_FILES");
if (tid == EIF_NO_TYPE)
eif_panic ("Type not in system.");
fn_io = eif_reference_function ("io", tid);
o_io = (fn_io) (root_obj); /* `root_obj' is the root object of the CECIL system
* automatically initialized in EIF_INITIALIZED
* if it does not exists */
i_io = eif_protect (o_io); /* Protect `io' . */
o_str = eif_string ("Hello World"); /* Create Eiffel string */
i_str = eif_protect (o_str); /* Protect Eiffel string. */
p_put_string = eif_procedure ("put_string", tid);
if (p_put_string == (EIF_PROCEDURE) 0) /* No routine found. */
eif_panic ("put_string not visible"); /* Raised if "visible exception" disabled. */
(p_put_string) (eif_access (i_io), eif_access (i_str));
eif_type_id is also used for returning the type identifier of generic types but you need to specify the generic parameter, otherwise it returns EIF_NO_TYPE.
Example:
EIF_PROCEDURE p_make; /* 'make' from ARRAY [INTEGER] . */
EIF_TYPE_ID tid;
tid = eif_type_id ("ARRAY[INTEGER]");
p_make = eif_procedure ("make", tid);
Getting the type id of a generic type : eif_generic_type.
Obsolete: see eif_type_id.
Raising an eiffel panic: eif_panic.
# include "eif_except. h"
void eif_panic(char *msg)
# include "eif_threads. h"
void eif_thr_panic (char *msg)
Raise an Eiffel panic with Eiffel exception trace with message msg. In MT mode, use eif_thr_panic instead.
Releasing an Eiffel indirection pointer: eif_wean
# include "eif_hector. h"
EIF_REFERENCE eif_wean(EIF_OBJECT object)
Tell the GC to remove the artificial reference to the nested Eiffel reference returned by eif_access (object). Then, the GC will be able to collect this nested object, as soon as it is not referenced from Eiffel any longer.
eif_wean (object) returns an Eiffel reference, which corresponds to eif_access (object). After a call to eif_wean (object), eif_access (object) is NULL, which does not mean that the nested Eiffel object is Void, but that the indirection pointer does not reference it any longer. It is possible to reuse object later on.Calling eif_wean (external_argument) where external_argument is an Eiffel object given by a C external can cause erratic behaviors. Indeed, external_argument is an indirection pointer, which is automatically deleted after the external call (not the nested Eiffel object), deleting it prematurely can corrupt the indirection pointers stack.
See also
In Eiffel: foo : STRING
external
"C | %"a file.h%""
end
In C:EIF_REFERENCE foo () {
EIF_REFERENCE str;
EIF_OBJECT i_str;
str = eif_string ("Hello world");
i_str = eif_protect(str);
/* Some operations on `i_str' */
return eif_wean (i_str); /* Returns direct reference to the Eiffel string.
* No need to keep an extra reference from the C. */
}
Getting the attribute from an Eiffel object: eif_attribute
# include "eif_cecil. h"
EIFFEL_TYPE eif_attribute (EIF_REFERENCE object, char *name, EIFFEL_TYPE, int *status)
Return the attribute of an Eiffel object.
The eif_attribute
macro returns the attribute of object of name, which is of type EIFFEL_TYPE.
EIFFEL_TYPE is the type of the Eiffel attribute. It can be: EIF_INTEGER, EIF_POINTER, EIF_CHARACTER, EIF_BOOLEAN, EIF_DOUBLE, EIF_REAL or EIF_REFERENCE.
If status is NULL then no status is set. Otherwise the status of the function is put in *status:
- status = EIF_NO_ATTRIBUTE => no attribute found.
- status = EIF_CECIL_OK => attribute found.
- status = EIF_CECIL_ERROR => an undefined error occurred, object may be invalid.
If the visible exception is enabled, then a visible exception is raised upon failure (EIF_NO_ATTRIBUTE, EIF_CECIL_ERROR).
RETURN VALUE:Upon failure, it returns (EIFFEL_TYPE) 0, otherwise, the attribute is returned. If the return value is not a basic type, you must protect it with eif_protect.
COMPATIBILITY:eif_attribute (object, name, type, NULL)
is equivalent to eif_field (object, name, type)
.
Getting the address of an Eiffel routine
#include "eif_cecil. h"
EIF_PROCEDURE eif_procedure (char *rout_name, EIF_TYPE_ID type_id)
EIF_REFERENCE_FUNCTION eif_reference_function (char *rout_name, EIF_TYPE_ID type_id)
EIF_INTEGER_FUNCTION eif_integer_function (char *rout_name, EIF_TYPE_ID type_id)
EIF_CHARACTER_FUNCTION eif_character_function (char *rout_name, EIF_TYPE_ID type_id)
EIF_REAL_FUNCTION eif_real_function (char *rout_name, EIF_TYPE_ID type_id)
EIF_DOUBLE_FUNCTION eif_double_function (char *rput_name, EIF_TYPE_ID type_id)
EIF_BIT_FUNCTION eif_bit_function (char *rout_name, EIF_TYPE_ID type_id)
EIF_BOOLEAN_FUNCTION eif_boolean_function (char *rout_name, EIF_TYPE_ID type_id)
EIF_POINTER_FUNCTION eif_pointer_function (char *rout_name, EIF_TYPE_ID type_id)
Return the address of the Eiffel routine by giving its name rout_name and the type id type_id of the class, in which it is declared. Returns a NULL pointer or raises a visible exception (if enabled) when there is no corresponding routine with name rout_name or the routine is not visible. The first argument of an Eiffel routine has to be the target of the Eiffel routine.
The Eiffel object returned by an Eiffel function must be protected afterwards with eif_protect
(this only applies for functions whose addresses are returned by eif_reference_function
, since the other function types returns basic types, which are not Eiffel objects).
Caution: Be sure that the Eiffel routine is not a C External. In this case, you should call directly the C external instead of its Eiffel wrapper.
Enabling/Disabling the visible exception
#include "eif_cecil. h"
void eif_enable_visible_exception ()
void eif_disable_visible_exception ()
Respectively, enable and disable the visible exception.
By default, the visible exception is disabled (since v4. 5).
Creating an Eiffel string: eif_string
#include "eif_plug. h"
EIF_REFERENCE eif_string (char *string) /* Macro */
Return the direct reference to an Eiffel string by giving the corresponding C string . The result of eif_string does not reference the C string passed as argument: it copies it, before creating the Eiffel string.
COMPATIBILITY:
eif_string ("ABC")
is equivalent to RTMS ("ABC")
and eif_make_string ("ABC", strlen ("ABC"))
.
Getting the return-type of an attribute: eif_attribute_type
#include "eif_cecil. h"
int eif_attribute_type (char *attr_name, EIF_TYPE_ID tid)
#define EIF_REFERENCE_TYPE 1
#define EIF_CHARACTER_TYPE 2
#define EIF_BOOLEAN_TYPE 3
#define EIF_INTEGER_TYPE 4
#define EIF_REAL_TYPE 5
#define EIF_DOUBLE_TYPE 6
#define EIF_EXPANDED_TYPE 7
#define EIF_BIT_TYPE 8
#define EIF_POINTER_TYPE 0
#define EIF_NO_TYPE (-1)
Return the type of the attribute described by its name attr_name and the type identifier tid of the class where it is defined. The return type is an int (see above for correspondences). In case of failure, EIF_NO_TYPE is returned - not such given attribute name, routine name instead of attribute name, or so on.
Example: Get the type of count from STRING
int i;
i = eif_attribute_type ("count", eif_type_id ("STRING");
printf ("type is %d\n"); /* Should be EIF_INTEGER_TYPE since it returns an Eiffel Integer */
Getting the class name corresponding to a type id: eif_name
#include "eif_cecil. h"
char *eif_name (EIF_TYPE_ID tid)
Return the corresponding name (C string) of the Eiffel class, given a type identifier type_id. If the type identifier is a generic type identifier, no generic parameter type is given. Return NULL if an invalid type identifier is given.
Example:
printf ("the class name with type id 1 is %s\n", eif_name (1); /* Should print "ANY" on most compiler versions */
COMPATIBILITY:
eif_name
is equivalent to eif_name_by_tid
.
Getting the type id of an Eiffel object: eif_type, eif_type_by_reference.
#include "eif_cecil. h"
EIF_TYPE_ID eif_type (EIF_OBJECT object)
EIF_TYPE_ID eif_type_by_reference (EIF_REFERENCE reference)
eif_type
returns the type identifier, given an indirection pointer to an Eiffel object.
eif_type_by_reference
returns the type identifier, given the direct reference to an Eiffel object reference.
COMPATIBILITY:eif_type
is equivalent to eif_type_by_object.eif_type (object)
is equivalent to eif_type_by_reference (eif_access (object))
.
Converting a C array into an Eiffel array: eif_make_from_c.
eif_make_from_c(eif_array, c_array, int nelts, int type) /* Macro */
(since 4. 5)
eif_array
is an EIF_REFERENCE
, c_array
is a C array of type (type *)
, `nelts is the number of elements in the array.