bertrand@eiffel.UUCP (Bertrand Meyer) (03/11/89)
From article <2135@vlot.cs.vu.nl>, jos@cs.vu.nl (Jos Warmer): > Many window-system, written and callable from C, have the following > architecture. > > You initialise your application and install some callback-functions > and tell on which event they shoul be called. > > Then you give control to the window-system which takes over > everything and calls one of the installed C callback-functions when > an event happens. E.g. XtMainLoop in Xt toolkit, window_main_loop in SunView. > > Using this from eiffel poses the problem that the C window-system takes > over and needs to call eiffel-routines (not C), otherwise eiffel loses > control altogether. > > Is it possible in eiffel to make C functions call eiffel-routines. > I know it is in standalone C packages, but that's not what I need. This answer has two parts: immediate solution and more long-term reflection. I am cross-posting it to comp.lang.c++ because difference between the Eiffel approach and what I assume would be done in a C extension may be of interest to readers of this newsgroup. 1 - IMMEDIATE SOLUTION ---------------------- Three C functions are available for calling Eiffel routines from C. Actually the ability to call a routine on an object is not enough: you must also be able to create this object from C, and to access an attribute (not just a routine) of this object. The following three functions are available for this purpose: eif_create (class_name, arg1, arg2, ...) Create an Eiffel object of type class_name; arg1, arg2, ... are the arguments (at most eight) to the Create procedure of class_name, if any. The function returns a pointer to the object. eif_rout (object_ref, routine_name, arg1, arg2, ..) Apply the Eiffel routine routine_name to the Eiffel object referred to by object_ref, with arguments arg1, arg2, ... (at most eight). If the routine is a function, eif_rout will return its result. Reference object_ref must refer to an Eiffel object; it will normally have been obtained by eif_create. String routine_name must be the name of a routine in the class of which the object is an instance. eif_attr (object_ref, attr_name) Value of attribute attr_name for the Eiffel object referred to by object_ref. Reference object_ref must refer to an Eiffel object; it will normally have been obtained by eif_create. String attr_name must be the name of a routine in the class of which the object is an instance. These two functions will be part of release 2.2. The first two were actually present in release 2.1, but not documented. To install them right away, you need a couple of C files which we will gladly send to any Eiffel user who requests them. Just send a message to source@eiffel.com with subject header ``Access to Eiffel from C''. No message text is necessary. We'll mail back the files (one copy has been sent to Jos already). If you don't need the facilities right away, they will be part of 2.2 anyway. On a different but related issue: if you are using Eiffel with Suntools or Sunview, you may run into problems because the Sunview Notifier insists on handling all external events, including signals. This defeats the Eiffel exception handling mechanism. In version 2.2 special support is provided to solve this problem (by ensuring that the Notifier passes on any event which is not meant for it). 2 - THE REAL ISSUE ------------------ The above will be useful in practice for Eiffel users who need to interface with existing C packages of the type described by Jos. Typical examples are the Notifier under Suntools/Sunview, and similar facilities under X windows. This immediate usefulness should not, however, lead us to forget that the underlying problem is much better solved by object-oriented techniques. The general C trick that is used in the cases mentioned is the following: - Define a default way of handling a certain event. This is described by a certain function; more precisely, by a variable, say event_handler, whose value is a pointer to the appropriate function. - When the event occurs, call that function. - Enable a programmer to override the default handling by assigning to variable event_handler a pointer to a different function. This works but it is really a form of low-level hacking. In particular, no static check is possible to guarantee that all overriding functions will have the correct signature (type of arguments and results). The desired effect - providing a default behavior, and enabling programmers to explicitly override this default if they so desire - is precisely what is made possible in object-oriented programming by multiple inheritance, redefinition and dynamic binding. The Eiffel technique is straightforward: define the default behavior in a class, say BASE, where the various event handlers are routines. Any class that needs these facilities will inherit from BASE. (This is where multiple inheritance is essential: you may need to combine more than one default behaviors, for example one for a window system and one for a database). If you want to override the default behavior in a specific case, just redefine the corresponding routine in the appropriate class. Static typing ensures that the consistency of the new argument types with the original will be checked. Dynamic binding ensures that the event will be handled adequately (by the default routine or by the redefined version, depending on the context). This is exactly the way we implement the X interface in the Eiffel Graphics Library, so as to hide from programmers all the low-level C coding tricks. When interfacing with existing C packages, of course, programmers must live with the techniques enforced by these packages, in which case the answer given at the beginning of this message is the appropriate one. -- Bertrand Meyer bertrand@eiffel.com