Important note: this document is regularly updated (corrections, examples,
bug report, suggestions from customers). Please make sure that you have
downloaded the latest version.
This document provides an overview of the C-Eiffel Call-In Library
(CECIL) as defined in Eiffel: The Language (ETL). The first section adresses
how to compile and run a CECIL program. The second part contains a more
precise description of the Eiffel types, the protection mechanism as well
as how to write and use C externals.
CECIL , designed by ISE, is the C library that permits C and C++ applications
(as well as applications written in other languages) to take advantage
of almost all Eiffel facilities: create Eiffel objects, apply features
to them. The basics of CECIL are described in chapter 24 of the reference
book on Eiffel, Eiffel: The Language, which covers interfaces between Eiffel
and other languages. Important material can also be found in the ISE manual
Eiffel: The Environment and on ISE's FTP server. In particular, the FTP
server contains a complete example which you are strongly encouraged to
download if you plan to make serious use of CECIL. You can find the examples
in
(This is a zip of the entire example directory. You can also retrieve individual
files from that directory by looking at ftp://ftp.eiffel.com/pub/examples/cecil
and ignoring cecil.zip.)
The updated examples for the ISE compiler 4.5 are in:
The CECIL documentation, man pages and examples are part of the standard delivery with the ISE Compiler 4.5 and higher.
The present document complement the descriptions of Eiffel: The Language.
Note that CECIL has been revised and improved since that book was published,
so the explanations below have precedence over those of the book.
This document is intended for both Windows and Unix users. Only a few
of the sections, clearly marked, are platform-specific.
Index
Introduction
Index
How to run a CECIL program
How to run a CECIL program
1 - Using CECIL
To access Eiffel mechanisms from C or other external languages:
For step 1, note that through CECIL you can use an Eiffel system
compiled in any of the Eiffel compilation modes:
-
Finalized C code.
-
Workbench (melted/frozen) code, usually for development purposes.
In this case you must copy the <system name>.melted file ( where
<system
name> is the name of your system)
located in EIFGEN\W_code
to the directory where you intend to execute your C application from. Note
that each time you melt the Eiffel system, the <system name>.melted
file is updated.
CAVEAT: in the second case (workbench mode), it is not possible to call
through the CECIL interface any routine that has been melted in the last
compilation; this would raise the run-time exception
$ applied to melted routine
The solution is simply to refreeze the system.
Back to index
2 - Compiling your Eiffel system for
CECIL
It is very simple to "Cecilize" your Eiffel system, that is to say
make its features available from the outside through CECIL. You will compile
it as you normally would - either freeze it or finalize it. The only supplementary
precaution is to protect from the dead-code remover any Eiffel features
that are not called from the Eiffel system's root, and might thus be dead-code-removed
by the optimization mechanism.
To do this, include the appropriate features and classes in the visible
clause of the Ace file, as in
system
system_name
root
...
default
...
cluster
...
your_cluster: "..."
adapt
...
visible
CLASS1;
CLASS2
creation
"other_make"
export
"other_make", "feat1", "feat2"
end
end
... Other clusters ...
...
end
Here all features of CLASS1 are available to the external software;
for CLASS2, only other_make (for creation) and feat1
and feat2 (for normal call) are available. For the full set of visible
options, see appendix D of Eiffel: The Language. If you omit
the clause export , only the features
with no export clause in the Eiffel code, will be available.
The
creation procedure of a visible class is always available
See also visible classes
Back to index
3 -Building a CECIL archive
The ISE-EiffelBench 4.x compiler produces both C code and
a "Makefile". The Makefile compiles and link s that C code in a subdirectory
one level below the EIFGEN directory of a project. For frozen/melted code,
the Makefile is located in the W_code subdirectory. For finalized code,
it is in the F_code subdirectory.
To produce a CECIL library, you must: open a shell (unix) or the MS-DOS
prompt (Windows), go to the subdirectory that contains the Makefile, and
then type:
make cecil (on unix)
nmake cecil (on windows, with VC++)
On version 4.4 and later, the CECIL library is automatically generated, you do not
need to call `nmake cecil' any longer. This will be done when freezing or finalizing.
This generates a CECIL archive whose name derived from the name
<system name> of the Eiffel system, as follows:
lib<system name>.a (on unix)
lib<system name>.lib (on Windows)
For example, the corresponding archive for an Eiffel system called
"test", would be called either "libtest.a" (Unix) or libtest.lib
(Windows.)
You can build a CECIL archive with either the "finalized" C code or
"frozen" C code. In the latter case you must copy the "<system name>.melted" file
located in $/EIFGEN/W_code/<system name>.melted
to the directory from where you intend
to execute your C application . Each time you melt the Eiffel system, the
<system name>.melted file is updated.
Note: Calling
melted routines through the CECIL interface is not supported in the current
version - calling a melted routine raises the exception "$ applied
to melted routine".
Back to index
4 - Using a CECIL archive
4.1 - Linking the CECIL archive into a program
The CECIL archive already incorporates the Eiffel 4.x run-time. To
use the functions provided in the CECIL archive, simply write the C programs
according to the CECIL specifications of ETL, and then include the cecil
archive in the linking line of your C application. This line looks like:
on Unix/linux:
ld -o [name of your CECIL executable] [your C object files and archives] lib<system
name>.a -lm
on Windows, with MS VC++:
link [your link flags] -OUT:[name of your cecil executable]
[your C object files and archives] lib<system
name>.lib [other Windows libraries]
Note:linking with "-lm" is required since the Eiffel 4 run-time uses
the standard math libraries.You may need to link with other libraries (for
example, on linux: with "-lbsd", in MT mode with "-lpthread" (posix threads)
or "-lthread" (solaris)) .
On Windows, you can reuse the .lnk file, which is automatically generated when you freeze or finalize your system: go to the appropriate directory (W_code or
F_code) and locate the file:
To have a Cecilized version (that is to say, a
version usable from external software ) you should:
- Copy system_name.lnk into a new file cecil.lnk in the same directory.
- To use the current MS-DOS shell as the default console add the directive "console_application (yes) in your Ace file.
- and replace the line:
by
assuming that the main.obj object file is in the project
directory; update the above path if it is in another directory.
Then, link with:
link @cecil.lnk
(in the appropriate directory W_code or F_code)
Notes for compiling
CECIL C files:
The CECIL library is built automatically, which is unfortunately not
the case of the corresponding object files of the cecil program you
wrote.
The C flags to use are usually the same as the ones needed during the
compilation of the generated C-code plus those, which are relevant to your
own C-code.
Typically, you will compile your flags as below:
gcc -c -O -I$EIFFEL5/bench/spec/$PLATFORM/include -I<SOME_INCLUDE_PATH>
-D<SOME_FLAGS> your_file.c
(if your are compiling with gcc on a Solaris).
or
cl -c -nologo -Ox -I<INSTALLATION_DIR>\bench\spec\windows\include
-I<SOME_INCLUDE_PATH> -D<SOME_FLAGS> your_file.c
(if you are using VC on Windows. <INSTALLATION_DIR> is the
installation directory of your Eiffel delivery: C:\Eiffel4, for
example).
For instance, if you want to use the multithreaded facilities of Eiffel,
you should define the EIFFEL MT flags.
gcc -c -O -I$EIFFEL5/bench/spec/$PLATFORM/include -DEIF_THREADS -DSOLARIS_THREADS
-D_REENTRANT your_file.c
or
cl -c -nologo -DEIF_THREADS -MT -Ox -I<INSTALLATION_DIR>\bench\spec\windows\include
your_file.c
You can specify a Makefile in your Ace file, so that your
C files will be compiled automatically after the Eiffel compilation and
before the final linking. Just add at the end of your Ace file in the external
clause:
external:
make: "$PATH_TO_MAKEFILE/your_makefile";
This makefile will be run from the $/EIFGEN/W_code or $/EIFGEN/F_code
directory. You should not give to the CECIL executable the same name as
your system, because it will be replaced by the Eiffel executable when
you run another compilation.
Back to index
4.2 - Initializing the Eiffel 4 run-time
Even though the main thread of control resides in the "C side" of the
program, the Eiffel 4 run-time must be intialized correctly before it case
use CECIL facilities to communicate with the Eiffel world.
In the C file containing the "main" C function, you must add the following
line to include the header file "eif_setup.h" provided with this example:
#include "eif_setup.h" /* Macros EIF_INITIALIZE and EIF_DISPOSE_ALL
*/
#include "eif_eiffel.h" /* Exported functions of the Eiffel run-time
*/
Your "main" function must have the three standard arguments of the C
"main" function" "argc", "argv" and "envp" and include the following macros
that are defined in "eif_setup.h":
main(int argc, char **argv, char **envp)
/* Please, respect this
signature: `argc', `argv' and `envp' are used
* in EIF_INITIALIZE.
*/
{
/* declarations of variables */
EIF_INITIALIZE(failure)
/* Initialize the Eiffel run-time. */
/* ...
* body of your "main" function
*
... */
EIF_DISPOSE_ALL
/* Reclaim the memory allocated by the Eiffel
* run-time. */
}
See the cecil examples on ftp://ftp.eiffel.com/examples/cecil.
Note that the above mentioned macros must imperatively be in the body
of the "main" function for the Eiffel 4 exception handling mechanism to
work correctly.
You also need to add the Eiffel 4 run time directory to the list of
directories in which the C compiler searches for include files. You can
do so by using the "-I" option of your C compiler.
See also Linking the CECIL archive into a program.
Back to index
5 - Restrictions
You can only link one CECIL library into your C applications at the
time.
Back to index
6 - Notes
Even though external object files and archives are correctly specified
in the "object" clause of the Ace file, you will need to explicitely link
them to your C application.
Back to index
Overview of the CECIL Interface
1 - 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, which is 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,
An Protected Eiffel objects is an EIF_OBJECT
(do 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.
Example:
Calling C external `foo' from Eiffel, which takes a pointer and an
eiffel object of type OBJECT as arguments and returns an INTEGER.
c_foo (ptr: POINTER; obj: OBJECT): INTEGER is
external
"C | %"your_file.h%""
alias
"foo"
end
In the C side, The C function `foo' is defined as below:
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 described 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 is
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 correspondances but they may change.The definition
of each Eiffel types is located in $EIFFEL5/bench/spec/$PLATFORM/include/eif_portable.h.
Back to index
1.1 - 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.
eif_attribute, eif_reference_function
, eif_string,
eif_wean all return an EIF_REFERENCE. An
EIF_REFERENCE can be used "as is" by the Eiffel run-time. eif_attribute, eif_xx_function take EIF_REFERENCE as arguments, never EIF_OBJECT.
The return value of a C external is to be an EIF_REFERENCE, if it
is not a basic
expanded type. To protect an EIF_REFERENCE, use eif_protect.
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 eif_access). Not doing it is completely unsafe since an
Eiffel reference may be obsolete after a collection. eif_create, eif_adopt, and
eif_protect returns an EIF_OBJECT.
The argument of a C external (on the C side) , which is not a basic expanded
type, is also an EIF_OBJECT .
The Eiffel run-time temporarily protects the Eiffel objects that are passed
to a C external
, that is why the signature of a C external has no EIF_REFERENCE in it,
but EIF_OBJECT instead. After the C external call, the run-time
unprotects the Eiffel object. If you intend to use in the C side
an EIF_OBJECT
given by a C external afterwards, you must protect it with
eif_adopt. To unprotect an EIF_OBJECT
, which is not a C external argument, use eif_wean.
The basic expanded types are INTEGER, REAL, DOUBLE, CHARACTER, BOOLEAN,
POINTER. They are passed to C externals by values. There is no need
to protect an INTEGER, REAL, DOUBLE, CHARACTER, or a BOOLEAN.
When the POINTER is a
pointer to an Eiffel object (ex:
$my_object
), then the direct Eiffel reference is passed to C, with no protection and this reference may move. Use eif_protect to manually protect it.
To unprotect it, call eif_wean
Back to index
2 - Protecting the Eiffel objects
You can use the CECIL functions directly from a C program as
well as in 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 numorous macros and functions,
which relieves the programmer from these kinds of low-level considerations
(most of them are declared in $EIFFEL5/bench/spec/$PLATFORM/include/eif_cecil.h).
Back to index
2.1 - 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 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 INTEGER, CHARACTER, POINTER, DOUBLE, BOOLEAN or REAL, which are basic
expanded types, since they cannot move.
Example
Back to index
2.2 - 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 Eiffel call.
This includes: calls to Eiffel routines, 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 eif_create, eif_adopt, eif_protect or an Eiffel object, which is an argument of a C external.
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 is
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 GENERAL.
(do not forget to put `visible' the class
* GENERAL in the
Ace.ace file.
*/
EIF_PROCEDURE ep;
EIF_TYPE_ID tid;
tid = eif_type_id ("GENERAL");
ep = eif_procedure ("print",
tid);
(ep) (eif_access(obj),eif_access(obj));
}
NB: The first argument of (ep) is the target of the function
(the eiffel object to which you want to apply the Eiffel feature (ep))
and the second argument corresponds to the first argument of `print'.
Note that any Eiffel objects could have been the 1st argument of
(ep)
since all of them inherit from GENERAL.
Important rules when using
eif_access:
-
Never precompute the value returned by eif_access.
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.
*/
See also eif_protect.
Back to index
2.3 - 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 unprotects
the Eiffel objects 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 returned,
eif_access (object)
is NULL but
eif_access (returned_value)
remains
valid until you release it with `eif_wean'.
See also eif_access,
eif_protect,
eif_create,
More about Eiffel types.
Example:
In Eiffel:
c_foo (ptr: POINTER; obj: OBJECT): INTEGER is
external
"C | %"your_file.h%""
alias
"foo"
end
c_display_and_release_obj is
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'.
Back to index
3 - Other CECIL functions:
3.1 - 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 external. 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 eif_type_id).
It returns a static indirection pointer which is to be used afterwards
to access the newly created Eiffel object
with
eif_access (returned_value)
where returned_value is the value returned by eif_create .
This means that when creating
an eiffel object from C, the eiffel object is automatically protected:
there is no need to call eif_adopt or
eif_protect on
it. This function does not call any creation procedure. To do so, you need
to explicitly call it with eif_procedure.
The garbage collector will not collect the newly created object until you
call eif_wean on it.
See also More about Eiffel types.
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. */
}
Note: `eif_create' does not call any creation procedure. It just
allocates
memory and initializes an object.
Back to index
3.2 - Protecting the objects returned by Eiffel functions.
# include "eif_hector.h"
EIF_OBJECT eif_protect (EIF_REFERENCE
object)
This function is used to tell explicitely 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 articifial 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. More about Eiffel types.
Example: Assume that you
want to access an attribute `tab' of type ARRAY [INTEGER] in
the class OBJECT.
#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. */
}
Note: Although you must protect Eiffel references returned by eif_attribute.
You must not protect attributes of basic types - they are not Eiffel references
and not supposed to move.
Back to index
3.3 - 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)
Returns the type identifier corresponding to the
type described in type_string. If the type does not exists , is
not visible or an instance of it is not declared in the root class (see
visible
classes), 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 GENERAL (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);
See also eif_procedure,
eif_xx_function
Back to index
3.4 - Getting the type id of a generic
type : eif_generic_type.
Obsolete: see eif_type_id
3.5 - 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.
Example.
Back to index
3.6 - Releasing an Eiffel indirection pointer: eif_wean
# include "eif_hector.h"
EIF_REFERENCE eif_wean(EIF_OBJECT
object)
Tells the GC to remove the artificial reference to the nested
Eiffel reference returned by eif_access (object). Then, the GC will be able collect this nested object,
as soon as it is not referenced from Eiffel any longer.
Note that object must be previously created with eif_adopt,
eif_protect or eif_create.
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 prematuraly can corrupt the indirection pointers stack.
See also eif_access.
Example: C external returning
an Eiffel string.
In Eiffel:
foo : STRING is
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. */
}
Back to index
3.7 - 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 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 occured, 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 types, you must protect it with
eif_protect
COMPATIBILITY:
eif_attribute (object, name, type, NULL) is equivalent to eif_field (object, name, type)
NOTE:
you cannot access a constant attribute, or the result of a function
(once or not) with eif_attribute.
OTHER:
EIF_BOOLEAN attribute_exists (EIF_REFERENCE object, char *name) returns
EIF_TRUE or EIF_FALSE depending if the attribute exists or not, is visible or not.
See also eif_procedure, eif_xx_function.
Example
Back to index
3.8 - 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)
Returns 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, which address is returned by `eif_reference_function'
since the other function types returns basic types, which are not
Eiffel objects).
NOTES:
The address returned by these functions must be called between
parenthesis.
Be sure that the Eiffel routine is not a C External. In this case, you must call directly the C External instead of its Eiffel wrapper.
Example
See also Declaring
routines taking real as argument, Cast
of the Eiffel routines in C, Bad macros in eif_cecil.h.
Back to index
3.9 - Enabling/Disabling the
visible exception
#include "eif_cecil.h"
void eif_enable_visible_exception
()
void eif_disable_visible_exception
()
Respectively,
enables and disables the visible exception. See visible
exception
By default, the visible exception is disabled (since v4.5).
Back to index
3.10 - Creating an Eiffel string: eif_string
#include "eif_plug.h"
EIF_REFERENCE eif_string (char
*string) /* Macro */
Returns the direct
reference to an Eiffel string by giving the corresponding C string string
.
The result of eif_string does not reference the C string passed as argument: it copies it, before creating the Eiffel string.
NOTE:
The return value must be protected with eif_protect for later use. The C string must be manually freed by the
user, if it has been dynamically allocated.
COMPATIBILITY:
eif_string ("ABC") is equivalent to RTMS ("ABC") and eif_make_string ("ABC", strlen ("ABC")).
Example
Back to index
3.11 - 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)
Returns the type of the attribute described
by its name attr_name and the type identifier of the class where
it is defined tid. The return type is an int (see above for correspondances).
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 */
OTHER:
*(EIFFEL_TYPE *) eif_attribute_safe (EIF_REFERENCE object, char *name, int type_int, int *status) can be used for debugging or type checking. It returns the attribute reference of name
name from the object object of type type_int. status contains the status of the function call: it can be EIF_CECIL_OK, EIF_CECIL_ERROR, EIF_NO_ATTRIBUTE, or EIF_WRONG_TYPE (type_int does not match with real type of object).
See also:
Back to index
3.12 - Getting the class name corresponding to a
type id: eif_name
#include "eif_cecil.h"
char *eif_name (EIF_TYPE_ID tid)
Returns 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. Returns
NULL if an invalid type idientifer is given.
Example:
printf ("the class name with type id 1 is %s\n",
eif_name (1); /* Should print "PLATFORM" on most
compiler versions*/
COMPATIBILITY:
eif_name is equivalent to eif_name_by_tid
See also eif_type,
eif_type_id
Back to index
3.13 - 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)).
See also eif_name, eif_type_id
Back to index
3.14 - 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 */ eif_make_from_c(eif_array, c_array, int nelts, int type) /* Macro */
(since 4.5)
Not on ISE Eiffel compilers prior to 4.5. However, its definition is available at
ftp://ftp.eiffel.com/pub/examples/cecil/cecil/C2array/array-opt
. eif_array is an EIF_REFERENCE, c_array is a C array of type (type *), nelts is the number of elements in the array.
Back to index
4 - Restrictions in CECIL
4.1 - Declaring routines
taking real as argument
The EIF_PROCECURE, EIF_XX_FUNCTION types match to most of the procedures and functions.
However,
if you want to use an Eiffel routine taking a real as one of its arguments,
you must be very careful. You need to define explicitely a typedef that
corresponds to the exact signature of the routine.
Example: call to put (r: REAL ; index: INTEGER) from ARRAY
[REAL])
typedef void (*EIF_PROCEDURE_REAL_INTEGER)(EIF_REFERENCE, EIF_REAL, EIF_INTEGER);
EIF_PROC_REAL_INTEGER eput;
/* some code */
eput = (EIF_PROCEDURE_REAL_INTEGER) eif_procedure ("put", eif_type_id ("ARRAY
[REAL]"));
Back to index
4.2 - Cast of the Eiffel routines in
C.
There are nine typedefs defined for the Eiffel routines.
EIF_PROCEDURE: Eiffel procedure
EIF_INTEGER_FUNCTION: Eiffel functions returning an Eiffel Integer
EIF_BOOLEAN_FUNCTIONL: Eiffel functions returning an Eiffel
Boolean
EIF_CHARACTER_FUNCTION Eiffel functions returning an Eiffel
Character
EIF_REAL_FUNCTION: Eiffel functions returning an Eiffel real
EIF_DOUBLE_FUNCTION Eiffel functions returning an Eiffel double
EIF_REFERENCE_FUNCTION: Eiffel functions returning an Eiffel
references (e.g an Eiffel object, which is not an instance of a basic type)
EIF_POINTER_FUNCTION: Eiffel functions returning an Eiffel pointer
EIF_BIT_FUNCTION: Eiffel functions returning an Eiffel bit.
These typedefs do not perform the type checking of the arguments.
The typedefs only cast the result of the Eiffel routine (void if it is
an Eiffel procedure). For more information on the previous definitions,
see their definitions in $EIFFEL5/bench/spec/$PLATFORM/include/eif_cecil.h.
CECIL can not generate relevant typedefs for every type of routine that
is exported to C through CECIL. Consequently, you must define manually
the typedefs to properly cast the pointer type to the Eiffel routine. This
way, during compilation, a warning or an error is raised if arguments with
incorrect types are passed to the Eiffel routine.
Example:
Let the Eiffel function `foo' :
foo (c: CHARACTER; a: ARRAY [INTEGER]): POINTER is
It is declared in C:
typedef EIF_POINTER (*EIF_FOO_TYPE)(EIF_REFERENCE /* Current object
*/,
The first argument of an Eiffel routine is always the current object -
the target of the call. This object is either the one, which precedes the
".foo(..)" if it is a nested call, or Current if
it is called without the "."
NOTE:
Do not forget to call all the Eiffel routines from C within parenthesis.
Otherwise, they will return the address of the Eiffel routine, instead of
performing an action.
You cannot use the Eiffel routine types for the routines, which are Eiffel External routines. The cast would be incorrect since these routines do not take an
EIF_REFERENCE as first argument, but the actual first argument of the Eiffel signature. It is recommended to use directly the C function instead.
Back to index
4.3 - Visible classes
You must declare in your Ace file all the Eiffel types you want to
use in CECIL as visible. See also ETL appendice D.
- Using a non-generic type, declare:
dummy: SIMPLE_TYPE
in your root class.
a_cluster: "A_PATH"
visible
SIMPLE_TYPE
end
end;
in your Ace file.
- Using Generic types , declare for example:
dummy1: GEN_TYPE [INTEGER]
dummy2: GEN_TYPE [OTHER_TYPE]
in the root class.
and
an_other_cluster: "AN_OTHER_PATH"
visible
GEN_TYPE
-- Do not specify any generic parameter.
end
end;
in your Ace file.
This is necessary to tell the compiler to generate the C code corresponding
to GEN_TYPE[INTEGER] or GEN_TYPE[OTHER_TYPE] and make it available to CECIL.
Otherwise, eif_type_id will returns EIF_NO_TYPE,
and eif_procedure/eif_function_xx will
return a NULL pointer or raise a visible exception.
Visible exception:
If you try to access a feature, which is not exported in the visible
clause of an Ace file (through its class or an export clause in the visible
clause), then a visible exception is raised. This exception can also be
triggered when you mistyped the name of a class (routine renamed, attribute
instead of a routine, or so on) , when the routine is not exported (see
visible
features), or when you forgot to declare an instance of the class that
implements the routine (see above).
You can disable this exception by calling eif_disable_visible_exception.
You
can also enable it with eif_enable_visible_exception.
If you try to call eif_procedure
or eif_xx_function
to get the pointer on a non visible routine, they will return a NULL pointer.
Back to index
4.4 - Visible features
Only the Eiffel features, with no export clause (even if it is {ANY}),
and which are declared in a visible class, are available (visible) to CECIL.
The creation procedure of a visible class is automatically exported to CECIL (unless there is
an export clause in the Ace file, which does not specify to export it)
Exporting features to CECIL,
overriding the export clause in the Ace file:
You can specify the CECIL routines you want to export
to CECIL in your Ace file by adding a custom export clause in your Ace
file. This overrides all the export clauses specified in the Eiffel code.
When there is an export clause in a visible clause, all the feature of
the class are not visible by default, even its creation procedure.
Example: export the feature foo from class A which has
an export clause.
In Eiffel:
feature {B,C} -- Only exported
to classes B and C
foo is
do
...
end
In the Ace file:
....
visible
A
export
"make", -- creation procedure
"foo"
end
end
...
In this case, only foo and make are exported to CECIL.
All other features from class A are no longer available. See also
ETL Part E, appendice D and bug in export clause.
Back to index
5 - Bugs
5.1 - Bug in export clause
Be very careful when enumerating the Eiffel features that you want
to export in the visible clause. The compiler does not check the validity
of the feature names. If you put a wrong name (typo, or if you are
not taking care of the renaming in Eiffel, for example). All features
of the class will be invisible from CECIL.
The name of the features to export should be put between "
" to avoid any confusion with Lace keywords.
By default, the creation procedure(s) of a visible class is/are exported and
visible in CECIL. However, if the creation procedure is defined in a parent and
has an export clause in the Eiffel code, you must explicitely
export it to CECIL in the Ace file.
Back to index
5.2 - Bad macros in eif_cecil.h
Back to index
|