rick@tetrauk.UUCP (Rick Jones) (11/30/90)
Here is another suggestion/question. The project I am working on involves interfacing to databases and other external subsystems using C libraries. Calling C functions from Eiffel gives no problems, but what is sometimes required is to attach to an Eiffel object an instance of some C structure for use by the C routines. I am doing this in several places, an example being a message buffer managed by an Eiffel class where each object is an instance of a message. The first approach was to use standard malloc() to allocate space, and keep track of the address in Eiffel as an integer attribute. The problem comes with deallocation and garbage collection. It can of course be done with a dispose routine which calls free() to get rid of the space, but given the curent state of dispose (suspect), this is not very reliable. It also means that duplication of the object when required gets complex. After digging around in the STRING and ARRAY classes, I finally worked out (I think!) how the INDIRECT class is used. This enables a "special object" to be allocated, which is managed by the Eiffel object heap manager and thus subject to normal garbage collection, but whose contents are otherwise arbitrary. With this method, the special object can be used to hold any C structure, and its address can be passed to C functions when required. The following is some example code of how to do this, which may be useful to anyone else. I would also like some comment from ISE on this. First, have I got the mechanism right (it seems to work), or are there any pitfalls, especially relating to GC, that I've overlooked? Second, how permanent and supported a fixture is the INDIRECT class? Can this technique be considered portable and upward compatible? Use of INDIRECT 1. A class which needs to hold a special object must inherit from INDIRECT. This provides an attribute "area" which is the reference to the special object, and a routine "allocate" which creates a special object of a specified size and sets "area" to refer to it. If you don't want to call the special object "area" you can of course rename it. 2. The INDIRECT class is generic. The actual generic parameter given on inheritance should be any basic type, e.g. INTEGER. It doesn't make any difference to the operation what basic type is used (except probably DOUBLE). You must NOT use a class type, though, otherwise the garbage collector will think that the special object contains a list of object references and will inevitably crash the program. 3. The "allocate" routine takes as its argument the required size of the special object in Eiffel WORDS (not bytes). I find it best to use a C function to calculate this size and return it as an integer (example below). 4. When "area" is passed as a parameter to a C function, the value is the address of the object header, not the usable allocated space. The C macro "Access", defined in _eiffel.h, will give the address of the actual usable space. It is important that the C code does not corrupt the object header, nor of course write beyond the end of the allocated area. 5. The deep_clone feature can be used on an object which contains a reference to a special object, and will result in the special object being duplicated as part of the object hierarchy. Example: class SP_CLASS -- a class demonstrating special objects export inherit INDIRECT [INTEGER] feature space: INTEGER ; -- amount of space allocated -- (this may not need to be held explicitly) Create (param: INTEGER) is external get_space (param: INTEGER): INTEGER language "C" ; fill_space (area: like area, space: INTEGER): INTEGER language "C" ; do space := get_space (param) ; allocate (space) ; if fill_space (area, space) < 0 then -- fill_space failed end ; end ; do_something is -- routine supported by C function using the special object external do_space (area: like area, space: INTEGER): INTEGER language "C" ; do if do_space (area, space) < 0 then -- function failed end ; end ; ... end This example represents a scheme where the allocated space is variable, depending on a parameter passed to Create. If the space is constant (e.g. to hold a single C structure), then the space attribute is not needed, nor are the uses of "param" in the example. Supporting C code: ( brain.engage (alternate_syntax) :-) #include <_eiffel.h> /* this macro converts a size in bytes (resulting from sizeof, etc) to the equivalent space in Eiffel words, rounding up */ #define Esize(bytes) ((bytes + sizeof(DATUM) - 1) / sizeof(DATUM)) int get_space (param) int param ; { return Esize (function_which_returns_bytes (param)) ; /* for structures, use: return Esize (sizeof (struct (struct_tag))) ; */ } int fill_space (area, space) OBJPTR area ; int space ; { /* get the address of the data space in area, casting to appropriate type, where MY_TYPE is a typedef */ MY_TYPE *my_area = (MY_TYPE *)(Access(area)) ; /* size in bytes, converted from space in DATUMs */ int size = space * sizeof(DATUM) ; if (fill_function (my_area, size) <succeeds> ) return 0 ; else return -1 ; } int do_space (area, space) OBJPTR area ; int space ; { /* get the address of the data space in area, casting to appropriate type, where MY_TYPE is a typedef */ MY_TYPE *my_area = (MY_TYPE *)(Access(area)) ; /* size in bytes, converted from space in DATUMs */ int size = space * sizeof(DATUM) ; < do whatever with my_area > return <success>? 0 : -1 ; } Note that all C functions which manipulate the special object will be passed the address of area, and so will need the same first line as in fill_space() and do_space() above. Also if space is variable, the conversion to bytes will probably be needed since the byte is usually the basic unit of size in C. After that it's up to you! This code should be pretty portable, and makes no assumtions about the actual size of DATUM, although on current implementations it seems to be the same as a long int. See the accompanying posting on equivalence between Eiffel and C datatypes. -- Rick Jones Tetra Ltd. Maidenhead, Was it something important? Maybe not Berks, UK What was it you wanted? Tell me again I forgot rick@tetrauk.uucp -- Bob Dylan