[comp.lang.eiffel] Problems with interfacing Eiffel and C

greitman@abel.ifistg.uucp (08/09/90)

I'm writing interface classes between Eiffel and Sun's XView-Toolkit
(part of Open Windows Version 2.0 Beta). 
I'm working with a SPARC Station 1 under the Operating System SunOS 
Version 4.0.3c.
There appeared some problems which are described in the following.


1. 
I have to pass the address of Eiffel features to "C". 
Here is my Eiffel feature with the appropriate "c"-call:

left_mouse_down_add_me(button: XV_BUTTON) is
     	external
       		c_left_mouse_down_add_me(button: XV_BUTTON): INTEGER language "C";
    	do
            	self := button;
           	dummy := c_left_mouse_down_add_me(@self);
                     	-- pass the address of self to the "C" function.
       	end; -- left_mouse_down_add_me


In "C" the variable containing this address has the type OBJPTR. 
Then in the "C" -file I have the definition:  static OBJPTR static_obj;
and in file c_left_mouse_down_add_me in this file the assignment:
static_obj = (OBJPTR) *obj;


/* The notify procedure should be added to the Eiffel event left_mouse_down.
   It should be possible to write the action which should be executed at the
   event entry in Eiffel.
int c_left_mouse_down_add_me(obj)
     int *obj;
{
  int result;
  Xv_object *handle;

  static_obj = (OBJPTR) *obj;

  handle = (Xv_object *)eif_attr((OBJPTR)static_obj, "handle");
  result = (int)xv_set(handle, 
                     PANEL_NOTIFY_PROC, left_mouse_down,
                     NULL);

  return(result);
}



The function eif_attr in file _eifc.c doesn't return a correct value at the 
assignment index = search(Objptr, a_name, 0);


int32 eif_attr (Objptr, a_name)
OBJPTR Objptr;
char *a_name;
{
        DATUM index;
        index = search (Objptr, a_name, 0);
        if (index) return ((int32)(*(Access (Objptr)+index-1)));
        else ATTR_ERROR;
}


Function search in file _eifc.c stops with segmentation fault 
after the position: 
if (!strcmp (a_name, Attr_names [dtype][i])) return i+1.
It looks like an initialization of Attr_names to "0" although 
it seems to be an array.
Before this position I have following values:
Objptr = (OBJPTR)0x6e2d0
a_name = (char *)0x58127 "handle"
flag = 0
(!Objptr ||!a_name) = 0
dtype = 4
Object_size[dtype] = 1048577
nb_attr = 3
!flag = 1
i = 0
Attr_names[4][0]] =I/O Error, Cannot read memory, address 0x4 out of bounds.
Attr_names[dtype][i] =I/O Error, Cannot read memory, address 0x4 out of bounds.

Attr_names is of type int and it is initialized with value "0".

I've got all these values running the debugger gdb on my root class.



static unsigned int search (Objptr, a_name, flag)
OBJPTR Objptr;
char *a_name;
int flag;
{
   extern int16 obj_nb;
   register int16 i;
        char *tmp;
        char field_name [825]; 
   int32 nb_attr, pos;
        int16 dtype;
        RESET;
   if (!Objptr || !a_name) return ((int32) 0);
   dtype = DType (Objptr);
        nb_attr = Object_size [dtype];
        if (!flag) {
                for (i = 0; i < nb_attr; i++) 
                        if (!strcmp (a_name, Attr_names [dtype][i])) return i+1;
        }
        else { 
                for (i =0; i < nb_attr; i++) {
                        strcpy (field_name, Attr_names [dtype][i]);
                        if (field_name [strlen (field_name)-1] == ' ') {
                                tmp = Strtok (field_name, " ");
                                for (pos=1; tmp != NULL; pos++) {
                                        if (!strcmp (a_name, tmp)) return ((i<<7) + pos + 1);
                                        tmp = Strtok (NULL, " "); 
                                }
                        }
                }
        }
 }



2.
Where do I find the definition of such types like OBJPTR, DATUM, DType, 
Attr_names, Attr_names[dtype][i], Strotok, etc ?



3.
If you compile the file _eifc.c with Debug-Option (-g) you get two warnings
(illegal combination of pointer and integer, op = ) in function search.

static unsigned int search (Objptr, a_name, flag)
OBJPTR Objptr;
char *a_name;
int flag;
{
   extern int16 obj_nb;
   register int16 i;
        char *tmp;
        char field_name [825]; 
   int32 nb_attr, pos;
        int16 dtype;
        RESET;
   if (!Objptr || !a_name) return ((int32) 0);
   dtype = DType (Objptr);
        nb_attr = Object_size [dtype];
        if (!flag) {
                for (i = 0; i < nb_attr; i++) 
                        if (!strcmp (a_name, Attr_names [dtype][i])) return i+1;
/* line 77 warning: illegal combination of pointer and integer, op = */
        }
        else { 
                for (i =0; i < nb_attr; i++) {
                        strcpy (field_name, Attr_names [dtype][i]);
                        if (field_name [strlen (field_name)-1] == ' ') {
                                tmp = Strtok (field_name, " ");
                                for (pos=1; tmp != NULL; pos++) {
                                        if (!strcmp (a_name, tmp)) return ((i<<7) + pos + 1);
                                        tmp = Strtok (NULL, " "); 
/* line 80 warning: illegal combination of pointer an dinteger, op = */
                                }
                        }
                }
        }
 }
 


4.
The documentation in the book "Eiffel: The Language Version2.2" about 
interfacing between Eiffel and "C" is absolutely insufficient. With only this
information it is nearly impossible to write such interface classes. 
But we are dependant on such self written classes because the Eiffel Graphics 
Library Classes are not sufficient for our needs. Furthermore some parts like 
menus don't work at all.



5.
Furtheron the graphical browser "GOOD" does not run at all.
We have tested "GOOD" using Sun Open Windows Version 1.0 (X11 R3) and
Open Windows Version 2.0 Beta (X11 R4) as well as MIT X11 R4. With all versions
of X Windows "GOOD" crashed with segmentation fault.
As "GOOD" is a very interesting tool especially to inspect the library classes 
delivered with the Eiffel system we hope that the next release of Eiffel will 
contain a running browser.



6.
Another question during writing our interface classes is, wheather there are
mechanisms to free data structures allacotated in "C" language parts based on
the Eiffel garbage collection mechanism. This is a major problem because
Eiffel has no facilities to write user defined destructor mechanisms.



I hope that someone out there in netland or maybe at ISE can help us to solve 
the problems stated above. 
My request to ISE: If this is a known bug please patch it immediately and
send us these patches.
Many thanks in advance. 

Best regards

Christiane Greitmann und Michael Ryba
Stuttgart University
Research Institut for Parallel and Distributed Supercomputers
Department of Integrated Systems Design

nosmo@eiffel.UUCP (Vince Kraemer) (08/14/90)

Drs. Greitmann and Ryba at University of Stuttgart, in their article
<3953@ifi.informatik.uni-stuttgart.de>, have stumbled across
a common problem in the documentation concerning interfacing Eiffel to C
via the eif_... routines. We are working to improve the documentation
by producing an addendum for "Eiffel: the Language" and our other
documentation. This addendum will be part of the documentation for the
2.3 release.

The following extracts from the addendum concern (1) Eiffel to C
interfacing (2) Passing objects to C and (3) Handling externally
allocated resources, including memory, in the presence of garbage
collection.  These are all issues that were raised in their article.

(1)

Dealing with Eiffel objects from C

Five routines are provided by the Eiffel run-time library to allow
external C routines, which are linked into an Eiffel based executable, to
manipulate Eiffel objects: eif_create, eif_rout, eif_attr, eif_rattr and
eif_battr.

The first routine, eif_create, is used to create an Eiffel object from
C.  Its return type is OBJPTR (an object pointer).  A call to eif_create
requires one argument: a lower case class name, which this object will
be a member of. If the create routine for the named class requires
arguments, these should be passed as additional arguments to eif_create,
in the same order as they are required for the Create routine of the
Eiffel class. 

Two important caveats apply to eif_create: 

* First, when you call eif_create, the class that you try to create must
  be part of the set of classes that make up a system.  Put simply, this
  means that you cannot eif_create an object of a class that is not used
  as a feature or entity in the Eiffel code for the system.  

* The second caveat applies to Eiffel code as well: you may not create
  an instance of a deferred class. 

The following code segment illustrates the use of eif_create to create
an Eiffel string object from C. (The ellipsis denote code which has been
deleted for brevity.)

#include "$EIFFEL/files/_eiffel.h" /* $EIFFEL should be replaced by the
				      path to the base of the Eiffel
				      installation tree. */

 ...

my_c_routine (..)
{
   OBJPTR obj;

 ...

   obj = eif_create ("string", 20); /* Class string needs length argument. */

 ...
}

Next, eif_rout provides access to Eiffel routines from C. Calling the
routine is actually a two step process: first you need to find the
correct routine to call and then you need to call it.  Eif_rout handles
the first task. The role of eif_rout is incorrectly documented in
"Eiffel: the Language". Eif_rout takes two arguments, an OBJPTR and the
name of a routine as a lower case string.  It returns a ROUT_PTR, which
is a pointer to a function returning a DATUM.  If the object's class
does not implement the routine named, a NULL pointer is returned.

Since a ROUT_PTR is a pointer to a function, you must dereference it to
call the corresponding function.  The first argument to the
dereferenced ROUT_PTR is an OBJPTR, which points at the target object,
which comes before the period in an Eiffel, as in a.f (...).  Any other
arguments required by the Eiffel routine should be passed in the order
specified in the Eiffel class text. Be sure that the OBJPTR passed as
the first argument points to an object of exactly the same type as the
OBJPTR used as the first argument to eif_rout.

An example of the use of eif_rout is given below:

/* assume that str1 and str2 are Eiffel objects (OBJPTR's) of class
   STRING either created by C or passed to C from Eiffel */

merge (str1, str2)
OBJPTR str1, str2;
{
   ROUT_PTR rout;

   rout = eif_rout (str1, "append");
   (*rout)(str1, str2);
}

Note: The result of dereferencing the ROUT_PTR with the wrong number of
arguments, an argument of the incorrect type or other possible abuses
is undefined.

To obtain the value of an attribute of an object, you can use
the functions eif_rattr, eif_battr and eif_attr, provided by the
run-time library. Each requires two arguments, an object instance (an
OBJPTR) and the name of the attribute to access (a lower case string).
The function eif_rattr returns a float, eif_battr returns an int that
can be interpreted as a boolean value and eif_attr returns an int32,
which can be used as an integer or a OBJPTR.

One thing to note about the use of these three routines: they are
applicable only to "true" attributes. Trying to access the value of
functions that are playing the role of attributes is an undefined operation.

(2)

Passing Eiffel objects to External routines

The easiest way to pass objects from Eiffel to external C routines is by
naming the objects you wish to pass as part of the call.  For example,
if you want to pass the object referred to by the variable `my_obj' to
an external routine `external_rout', write the call from Eiffel in the
following fashion: 

external_rout (my_obj);

The C function, external_rout, would be declared as follows:

void external_rout (obj)
OBJPTR obj;

You can also use the "@" operator to perform this type of passing, but
it is a bit more tedious.

(Note: The "@" operator was originally intended for passing the address
of routines only.  In its original implementation, the application of the
"@" to some non-routines, like local variables, was an unundetected error.
This has been fixed in the 2.3 release so you may use "@" to pass the
address of objects, known through both local entities and attributes, as
well as routines).

(3)

Handling external resources in the presence of garbage collection.

It is often necessary to access external resources from Eiffel systems,
such as windowing systems, data-bases, etc.  While it is often easy to
determine when to allocate resources from Eiffel, it is more difficult
to determine when to release these resources.  The simple solution is to
allow an object to release the resources when the object is guaranteed
to be finished with them: when it is collected as garbage.

To facilitate this, the class MEMORY, which is an interface to the
garbage collector, defines the empty routine `dispose'.  Classes which
require explicit actions to be performed before they are collected
should inherit from this class and redefine `dispose'.  This is very
applicable to situations where resources need to be de-allocated as the
corresponding Eiffel objects are being disposed of.

Consider the following code:

class X_WINDOW export

inherit
	...
	MEMORY redefine dispose;
	...
feature

	window_ref: INTEGER;

	Create (parent: X_WINDOW) is
			-- Create an X_window object and allocate its
			-- corresponding X server entity.
		external
			create_x_window: INTEGER
				name "XCreateSimpleWindow"
				language "C"
		do
			window_ref := create_x_window
					(display.ref, parent.window_ref,
					 0, 0, 100, 200, 1, black,
					 white);
			...
		end; -- Create

	dispose is
			-- De-allocate the window on the X server
		external
			destroy_x_window
				name "XDestroyWindow"
				language "C"
		do
			destroy_x_window (display.ref, window_ref);
		end; -- dispose

	...
end -- Class X_WINDOW

---------------------- End of Addendum extracts -----------------------

From <3953@ifi.informatik.uni-stuttgart.de>:
>2.
>Where do I find the definition of such types like OBJPTR, DATUM, DType, 
>Attr_names, Attr_names[dtype][i], Strotok, etc ?

The definition of types, variables and other entities used in the Eiffel
run-time are in Eiffel/files/_eiffel.h and its parent ".h" files.

>5.
>Furtheron the graphical browser "GOOD" does not run at all.
>We have tested "GOOD" using Sun Open Windows Version 1.0 (X11 R3) and
>Open Windows Version 2.0 Beta (X11 R4) as well as MIT X11 R4. With all versions
>of X Windows "GOOD" crashed with segmentation fault.

We were aware that there were problems running GOOD on the Sun X11/NeWS
merged server that is the basis for OpenWindows.  We have just recently
gotten OW 1.0.1 and OW 2.0 in house.  We have found the problems with
GOOD that were exercising invalid assumptions about colors, fonts, etc.
and causing GOOD to fail due to the graphics library.  These areas have
been addressed and are part of the next release.

We have not had problems with GOOD (2.2B) on the MIT server. If others
have had problems like this, we are appreciate hearing more about it,
through our customer support e-mail (eiffel@eiffel.com) or via phone
(805) 685-1006 or fax (805) 685-6869.

Vince Kraemer
ISE Tech. Support
(technical correspondence to: eiffel@eiffel.com)