Fenestra User's Guide (Chapter 2) -- contents | previous | next


2 Library overview

This chapter introduces the overall structure of the library along with general considerations on its design. It is also recommended to read the appendix about the style guidelines (appendix (see section A)), as it may help understanding the Eiffel code, flat-short forms and the style of the library's class texts.

Installation notes and information on how to use the library with supported Eiffel compilers follow the general introduction.

2.1 A portable Win32 library

Fenestra is a general GUI library designed for the Win32 API. It follows the API functionality, and is aimed at providing Windows applications, which work like users of this platform usually expect the user interface to work.

On the other hand it is not a low-level library. No attempt was made to map every single construct from the API to a corresponding Eiffel feature. It is a high level library, to make GUI development easier for the Eiffel programmer. Therefore, applications should behave like Windows applications do, while not necessarily being programmed like Windows applications are in C or C++. For instance, the Eiffel developer will be able to use Windows menus, but will not see the menu IDs used when programming menus with the Win32 API in C.

The library is supported on all mainstream current Win32 platforms (that is, at the time of writing, Windows NT and Windows95). No attempt has been made to support Win32s, a cumbersome transition technology.

Portability

While this library is clearly a Win32 library, it does not mean it is necessary to give up cross-platform portability.

GUI portability is a complex issue, several approaches can be taken. The minimum common denominator approach may be the most popular (see for instance XVT or the Java GUI toolkit). Only features that are available on all platforms are supported, so that the available feature set is reduced, add-ons have to be reimplemented, and applications are likely to look slightly unfamiliar to users on most platforms.

Another solution is to completely reimplement the user interface code and only have the strict minimum platform dependent code, that is only the very basic windowing and graphics interface -- like the Win32 API's Graphics Device Interface (GDI) level. The look-and-feel of the target platforms can be emulated but the amount of work is considerable, and the imitation has to follow the trends of all the emulated platforms. An advantage of this approach is that it may then be possible to use any look-and-feel on any platform, such as a Windows aspect under Motif or conversely.

Finally, it is also possible to choose a `master' platform, use the native look-and-feel and feature set of this platform, and then port it to other platforms, implementing the original style of interfaces as closely as allowed by the target platforms. Given that the dominant GUI platform is currently Microsoft's Windows, it may be acceptable for ports to have a Windows-style interface -- especially as it seems more acceptable to have varying user interface standards on platforms such as Unix/X11 than under Windows.

This is, indirectly, the approach followed by Fenestra. While this library has only a Win32 implementation, the Win32 API has been ported by numerous third party vendors to most platforms -- there are several implementations available for Unix/X11 (with or without Motif) and the Macintosh for instance. This way, the portability effort is distributed between several companies, and gives the developer choice of portability toolkits and technologies. The main drawback is that this solution may appear inelegant on some platforms, because Win32 is a rather low-level interface that may be clumsy to emulate on other platforms, but this is balanced by the benefit of a native implementation for the mainstream platform.

2.2 Design principles

Fenestra is a compact, high-level library portable to various Eiffel systems. In addition to the style conventions (see appendix (see section A)), some design premises are introduced in this section, so that it is easier understand why and how some design choices have been made.

Eiffel portability

One of the primary aim of this library is to be usable with any conformant Eiffel compiler. This is not as easy a task as it should be, given the evolving nature of current Eiffel technology.

Portability implies using a working subset of the Eiffel language. Eiffel is a complex language and a few difficult or obscure constructs are better avoided in order to facilitate portability. It is especially important because Eiffel has no preprocessor -- generally rightly so -- so conditional compilation may not always be usable to circumvent non-portable constructs. When the few problem areas have been isolated, portability at the language level is normally quite good.

Another problem is library portability. This library targets compilers conforming to the Eiffel kernel library standard [els95] because kernel portability was often problematic with the previous generation of compilers.

Data structure library

A more significant problem related to the basic library issue is the data structure question. Because there is no established standard of data structure in the Eiffel environment, and the use of a proprietary solution is of course excluded for a portable library, this library comes with Pylon, a simple foundation library.

Pylon is a straightforward data structure and foundation library which introduces the main data structures and a small set of basic utility classes required by most applications. It supports the implementation of the GUI library and its communication with the client when kernel types are not adequate -- a relatively rare case. The GUI library does not impose this foundation library on its client, it is perfectly possible for client code to use a completely different data structure library. The fact that Pylon is very compact, means that its overhead should be minimal when using other data structure libraries concurrently.

A minimalist introduction is in this manual (see section (see section B)) and a more complete separate manual is available [pylon96]. Pylon's code can be freely distributed independently of the GUI library.

The consequence of using a companion foundation library is that it has all the needed features and its design is coherent with the main library. Hence there is a clear dependency on (1) the kernel and (2) Pylon.

Compact high-level design

In addition to the requirements above, the library was designed as a coherent, compact set of classes. Clean class interfaces and well-defined functionality were preferred to a direct mapping of all possible underlying API features. A feature had to make sense and be useful to be included.

The classes are designed for being convenient and practical to use and follow the object-oriented programming model at a high level -- the aim was not to have a 1:1 relation between any operating system `object' and an Eiffel object. Developers used to programming the Win32 API should not expect that a library object is a simple encapsulation of the Windows version, or that it behaves exactly like a trivial encapsulation would. When it was necessary to depart from a strict wrapping of some API features, or to achieve a coherent feature set, some extra logic was added to make it possible.

Necessity rule

The search for a clean interface means that when there is a choice the conservative one is usually made. For example, when both publicly exporting an attribute and keeping it private is sensible, it is generally kept private. This was done on the ground that it is always possible to extend the public interface in future versions, while removing functionality is always more problematic when the seamless evolution of earlier code is an issue -- even if Eiffel's obsolete keyword helps.

Unicity rule

The same principles lead to a unicity guideline: whenever it is reasonable, without constraining significantly the library user, only one way to do a given task should be provided. It is important for evolution and the consistency of client programs.

2.3 Design patterns

This section introduces some constructs which are used throughout the library. It is also used to discuss some particular design choices, which are better broadly understood before the details are reviewed in later chapters.

2.3.1 Event model

In a GUI application, many interface objects need to communicate with their clients. For instance, when a menu or a button is selected, it will need to inform its client. When the user interacts with the application, events are sent to the relevant components.

It is consequently necessary to have a scheme to allow the client objects in a GUI library to receive these events, or callbacks, from the library's interface objects. Fenestra provides two ways to do this, depending on the kind of objects and the sets of events which are associated with a given interface object.

Client inheritance

The first way to intercept events, is by using inheritance. Interface objects have a set of events procedures which can be redefined by descendant classes. These procedures are not necessarily Eiffel deferred procedures -- they are actually rarely so -- because many events need not be explicitly redefined in all cases. An effective heir may only need to specialise the behaviour of a few events, depending on what effect is desired, and leave the other events unprocessed or with their default behaviour.

As Eiffel allows descendants to redefine any methods of their ancestors, it is important to document how inheritance should be used when using inheritance for normal event processing -- maybe this could be named client inheritance, as opposed to normal inheritance used to enhance or reuse an object in a way not necessarily planned by the designer. In this library, all procedures which can be redefined during the normal use of the library for event processing are exported to PUBLIC_NONE. PUBLIC_NONE (formally introduced in appendix (see section A.2)) is an empty classification class which cannot have effective heirs. It is for all intents and purposes like NONE but is used to publicise event methods without exposing them to calls from the general public -- which would be excessive. It may happen, rarely, that an event procedure is exported for implementation purpose to other classes than PUBLIC_NONE. It is then necessary to maintain, or extend, the export conditions when redefining.

Events procedure are usually prefixed with when_, for instance, when_mouse_clicked for the event associated with a mouse button click in the client area of a window. User interface objects such as windows which have a relatively large set of events associated with them are the typical case when this kind of event handling is used. For instance, independent windows will generally inherit from a predefined type of window whose appropriate event procedures will be redefined.

2.3.2 Command objects

While this system makes sense for objects such as windows which are standing alone, it is less convenient when using simple objects whose use is mainly meaningful in the context of a larger scale construct. A typical case is a push button: it is possible to inherit from a button and redefine the event corresponding to a button selection, but it usually will not make sense in the context of the button alone. It will become meaningful in the wider context of the button's parent dialog box (or any other type of enclosing window).

Redirection commands

For that kind of situations, the library provides a simpler system of event redirection: small command classes. The GUI_COMMAND class is a simple class with a single deferred feature, execute. This class may be inherited by a larger scale class which need to receive the event. If as is often the case this class shall have more than one associated GUI_COMMAND, it cannot inherit directly from it as Eiffel's polymorphism would not allow more than one implementation of execute to be called. The event can be redirected through simple lightweight classes, for example descendants of BOOMERANG_COMMAND. This class is a simple extension of the command which associates a parent and a make feature to set that parent.

A typical redirection command class will be as follow:

class CMD_DO_SOMETHING
inherit BOOMERANG_COMMAND[PARENT]
creation make
feature execute is do parent.do_something end
end

Which can be used inside PARENT in, for instance, the case of a button:

 
!CMD_DO_SOMETHING!cmd.make(parent)
button.set_command(cmd)

-- later ...
do_something is

While it may look at first clumsy, this approach has several benefits. Descendants of BOOMERANG_COMMAND can be quickly written using the template or macro facility of a text editor, and, if the Eiffel environment allows it, put in the same source code file as the main class. The relation with the parent class is handled simply, while inheriting from the button class -- somewhat overkill and possibly inefficient just for redirecting an event -- would require adding separate features for this purpose, or doing some inconvenient redefinitions of internal attributes, or assignment attempts. An additional benefit is that several client objects can use the same command, or a command can be associated with other objects easily during development (or at execution time for that matter!).

Stand-alone command objects

The other common case where command classes are useful, is when the full implementation of an operation is implemented into heirs of the command class. At first, this case looks similar to inheriting directly from the original user interface object, say by implementing application functions in individual heirs of menu item objects. It is not nevertheless always wise to do that, because carrying the overhead of the user interface object may not be sensible.

In the classic example of the multi-level undo facility, each command class will contain the command, and enough context information to be able to reverse it. One instance of the command for each actual operation will then be put on a stack. If the command object had to inherit from a user interface object, multiple instances of this would end up duplicated and stored uselessly into the undo stack.

2.3.3 On inheritance

When looking at the library organisation, some noteworthy patterns may be encountered regarding the application of Eiffel's inheritance.

Generally, implementation inheritance is rarely used, as it seems to have more drawbacks than benefits: It overloads the namespace if not the public interface. Namespace pollution makes errors more common by increasing the chance that a feature name is misinterpreted for something else, or makes inheritance more difficult if there is a name conflict. If a class is used, inconsistently, through both implementation inheritance and composition, validity issues have to be considered.

It even happens that in some cases, what could have been done reasonably naturally with multiple inheritance, has been implemented with composition. For instance, controls do not inherit from CHILD_WINDOW, while all controls can be seen as a window from both a practical viewpoint and for the operating system. Not all controls are really plain child windows though, and it is rarely needed to access windows' functions directly for a control's client. Therefore, the implementation child window of a control is a self attribute.

Such an approach also has the benefits of limiting namespace pollution, and allows to have meaningful flat-short forms of the library's classes. In the example above, CHILD_WINDOW has already got one of the largest public interface in the library, if the numerous controls had to inherit this interface their own interface would became quite inelegant, for no good reason.

A drawback is that in some cases it may be meaningful to have a new class made through multiple inheritance of two related classes, while one of these inherited classes is designed to use the facilities of the other through composition. In this case it is still possible though to use inheritance thanks to Eiffel's flexibility. In our controls example, a custom control could inherit from the base control class and use inheritance of CHILD_WINDOW by redefining self, a deferred attribute in the base control, as follow:

self : CHILD_WINDOW is
    do
        Result := Current 
    end

A related pattern that can be seen in the controls cluster, is an abstract inheritance hierarchy shadowed by a concrete hierarchy. A lean hierarchy of deferred classes is used to describe the conceptual interface of some objects, with a few implementation of generic features, and one (or several) shadow hierarchy of practical concrete objects. The concrete objects inherit both from their abstract alter ego, and from a simpler concrete object they can build their implementation upon.

In the case of controls, it enables a clear separation between built-in controls provided by the operating system and custom controls written in Eiffel using the facilities of the library. Built-in controls have to be implemented differently and it wouldn't make sense for say an extended custom button to inherit from a built-in one. With the abstract scheme, both a button written in Eiffel and the standard operating system push button can inherit from the abstract concept of a push button, and, respectively, the concrete Eiffel control base class and the built-in control base class.

2.3.4 Dialog construction

Fenestra provides a simple and convenient system for the construction of dialog boxes or data entry forms, although it may at first appear slightly unusual.

The library does not make use of the operating system's dialog resources although bitmaps, icons, individual string can of course still be stored in the resource section of the executable file. A dialog box is simply an improved type of window in which controls -- control windows, also known as widgets -- will be dynamically created, normally before the dialog is displayed. Controls are generally child windows of their parent window, the dialog box.

Controls can be positioned inside the dialog box by using actual coordinates or device independent coordinates but will usually be positioned using controls' layout methods, which are introduced in the tutorial (chapter (see section 1)). Basically, a new control will be positioned relatively to the existing controls (for instance, a new control can be placed under or to the right of another control, and have the same of proportional width).

Programming is not drawing

Using dynamic layout has several advantages. An automated layout is more straightforward to design, extend and modify than having to change the position, adjust the alignment, using cumbersome WYSIWYG dialog editors. A dynamic dialog box can easily be customised at runtime: some options that need not be shown to some category of user can be conditionally shown and the dialog will be reformatted adequately. Portions of a dialog box can also be reused in other parts of the application or in any other program, like any piece of Eiffel code.

For this a reason, Fenestra does not provide a GUI interface builder. While a GUI builder may allow to build a skeleton rapidly without the need to understand what happens, it can have several drawbacks in a more long-term perspective.

Dynamic dialogs are designed using the same language as any other parts of the application, Eiffel. The dialog layout code is also the code which will be used to implement the functionality of the application: the object that is positioned is the object from which data will be retrieved. An interface builder will generate some interface code which may be clumsy or poorly understood, and still requires to add the real code. The reversibility of a system based on code generation do not always work seamlessly: code or interface changes may become difficult to synchronise.

The library approach also improves portability and maintenance. A Fenestra application is portable to all systems with an Eiffel compiler and an implementation of the Win32 API. There is no need to get a new version of an interface editor after a migration to a new platform. A dialog form and its content are the same Eiffel class, which can be easily maintained using source code maintenance tools or shared between various applications. A system based on either proprietary resources or windows resources could not necessarily fit well inside a revision maintenance system, specially for large applications.

2.3.5 On the multiple document interface

The current version of the library does not include support for MDI (Multiple Document Interface). The MDI concept -- several resizable child windows constrained inside a parent window -- made sense under older version of windows when there was no multitasking and launching several copies of the program was inefficient. Under Win32 platforms, multiple document application can efficiently have an instance of an application per document, and then automatically benefit from the operating system's multitasking capability.

The MDI model has also been used for user interfaces which have multiple view of some data. This was not the original purpose and indeed such application often prove clumsy to use, compared to the use of multiple stacked windows (panes), which can possibly be repositioned with a split-bar, inside a SDI (Single Document Interface) application. The library has extensive support for paned child windows in a program, including automatic layout and resizing.

It can also be noted that while Microsoft documents MDI support in Win32 to be there for legacy applications, while their application division uses it extensively...

In the context of the move towards document-centric computing, the move to SDI may also make more sense. While this is the current policy, following the unicity and necessity principles, this may be changed in the future and the inclusion of MDI, with warnings, is not completely excluded.

2.4 Library layout

The library is organised as a small set of clusters:

Windows
Basic window classes, menu system, clipboard, standard dialog boxes, access to the help facility, and related functionality.
Graphics
Graphic devices and graphic primitives to draw figures on these devices.
Controls
Dialog boxes, abstract and concrete controls.
System
Various interfaces to the operating system which are not part of the user interface -- such as access to the system Registry or data file access.
Internal
Classes used internally by the library -- no public classes.

The latter cluster, internal, contains no classes for public use. All its classes have no public features and should not be used directly. Conversely, all classes in the other clusters are public and documented.

Each cluster will be introduced in its own chapter. The explanations of this manual are presenting the capabilities and classes in context, showing how and when to use them. This manual should be used in addition to the classes' flat-short form that can be found in the Reference Manual, available as a Windows help file, or through the Eiffel environment browsing facility. The flat-short forms and the features' contracts are an essential part of the documentation, in line with the Eiffel philosophy.

In addition to its own cluster, the library depends on the following external facilities, as explained earlier:

  1. The standard kernel as defined in the Eiffel Kernel Library standard, Vintage 1995 [els95].
  2. Pylon, a foundation library. This cluster library is provided with the distribution, which also includes a separate manual, [pylon96]. It is introduced in this manual, in appendix (see section B).

The library also includes a small C runtime system (more details in appendix (see section D)) which must be linked with the compiled systems. It essentially includes wrapper functions to API calls, and callbacks to the Eiffel system to process the operating system's events.

2.5 Building systems

Building Eiffel systems (programs) requires a way to describe the system to the compiler. Each compiler has its own scheme, so each supported compiler will be covered after the general principles.

After the library has been installed, building a system generally involves writing a system description file or equivalent (section (see section 2.5.2) below), and possibly customising some compilation options.

The starting point of the program will usually be specified as the creator of the application's main window (see chapter (see section 3) for more details and alternative methods).

2.5.1 Installation

The library should of course be installed on the user's system, but this involves little more than copying the directory tree of the source distribution to the hard disk. To this end, the library comes with an installation program (for Intel/x86) which requires the user name, company and registration key to operate. It will then copy the files, serialised to the licensee name, to the user's hard disk. This need be done once, and the code can then be used on any platform or machine, in the limits of the licensing agreement.

It may make sense to precompile the library's clusters if the target compiler provides this possibility. See the compiler's documentation or the compiler specific sections below or the release notes.

The template configuration files introduced below, and the samples provided with the library distribution rely on the FENESTRA environment variable, which should be set to the root directory of your Fenestra installation. They also rely on the variable PYLON which points to the base directory of the foundation library's cluster -- %FENESTRA%\pylon for the default installation.

2.5.2 System description

Most Eiffel compilers require information on a per-project basis, allowing the compiler to know which classes and clusters are included in the system, and the external language interfaces for parts of the program not written in Eiffel, etc. This section describes the general information that has to be specified for any compiler, while the following sections give specific instructions for supported compilers.

In order to build a GUI application, the following points should be considered, in addition to the application-specific setup:

Many compilers expect all this information in a single file -- the `Lace' file or program description file. The information may have to be split between a library description and an application description, especially when advanced library facilities are available. Integrated Development Environments (IDE) may expect this information from an interactive user interface.

Clusters

The library requires including the single foundation library cluster, and the GUI clusters. There are five clusters in the GUI library, the public clusters, Windows, Graphics, Controls, System; and the cluster containing private implementation classes, Internal. They are in directories under the root directory of the library, to which an environment variable can point.

Class visibility

All callbacks are through the class NOTIFIER, which must be made visible (exported) to the low-level runtime system in all cases. In most cases this will be the only class that need be exported, unless some Eiffel types have to be accessed from the low-level runtime.

Linking the runtime

The result of compiling the low-level runtime system will produce an object or library file, typically fenr_<c-compiler>_<eiffel-compiler>.obj, which has to be linked with the application. The source of the runtime is provided for porting to unsupported systems or extension. More information on the low-level runtime is in appendix (see section D).

Linking the Win32 API

While the low-level runtime does not use the C-runtime library, it obviously makes calls to the operating system API. The low-level linker or Eiffel system may link the API library files automatically, but if it's not the case, the library files corresponding to the standard Win32 DLLs or libraries must be included.

The Win32 DLLs used by the library are:

Linking resources

In addition, a program may need external resources, such as icons, bitmaps, or strings. If these resources are required, see appendix (see section C) for information on how to use the resource compiler. This will result in an object file which must be added to the list of files to be linked with the system.

2.5.3 Low-level linking

Another important issue when describing a system, if the Eiffel compilers produces C, or C-like low-level object files, is the selection of the low-level linker options.

Executable types and entry point

First, it should be noted that Win32 distinguishes GUI programs (`graphics') from command line (`console') programs. This is set by the low-level linker at link time. Most Eiffel compilers currently default to producing command line programs. So, for GUI programs using the library, it may be required to change the C linker flags to set the `windows subsystem' flag. This is usually done by an option in one of the Eiffel compiler's configuration files, as the C linker is called by the Eiffel compiler's system.

Under Win32 operating systems, it is common for programs in C or C++ to start with the standard C main function for command line programs, and the WinMain function for GUI programs. It originates from older (16-bit) versions of Windows when extra parameters were needed for the startup function of GUI programs.

The WinMain function lost its raison d'être with Win32 but it is still traditionally used. That is, C linkers set it up as the default entry procedure to programs with the GUI flags. As an Eiffel compiler's runtime system will usually rely on main being the startup procedure of the Eiffel system -- the C code generated by the Eiffel compiler produces a main function -- it is necessary to reverse the default setting back to the main function. If the C linker allows specifying the name of the entry function, it is not as straightforward as expected as the real entrypoint to a C program will commonly be an internal function of the C runtime library, that will initialise it and then call main or WinMain. Check the relevant C compiler's manual.

Another concern is if the code produced by an Eiffel compiler does not make use of the C runtime library and may then need a direct entry into its own runtime system, be it main or another function.

Compiling debug builds as console applications

For convenience reasons, it may be useful to produce debug executable programs as if they were console program, while using the GUI library at the same time. These programs, command line programs from the operating system's viewpoint, will have a console (in addition to the other windows) that may be used for debug output from the Eiffel compiler (such as the result of assertion violations) or from the user's programs by using the kernel library' access to standard C input/output. Distributed binaries should not be linked as console programs (unless they really are console programs not making use of GUI facilities). Win32 GUI programs still have standard command line output and error streams, which are not displayed but may be redirected to a file.

Alternatively, it is normally possible to redirect the standard ouput (stdout) or standard error (stderr) port to a file or console, even for pure GUI applications.

Runtime library as a DLL

The GUI library's small C runtime library is provided as a statically linkable low-level object file. Although the runtime can be compiled as a DLL, it has not been deemed useful to include it in the distribution, because the size overhead of this static library is quite small when linked with a typical GUI application and this avoids adding an additional dependency on an external component.

2.5.4 Making programs with TowerEiffel

The library has been tested with TowerEiffel version 2.0, which is the first release of this compiler with a standard kernel -- a requirement for this library.

TowerEiffel uses `ace' files which are an extension of the `Lace' syntax described in [meyer92]. It requires to list all the library clusters in each configuration file, unless precompiled libraries are used, when only the application clusters have to be specified. See the compiler's documentation for more information on precompiled libraries.

Here is a sample `ace' file for a simple program:

-- Hello World demo Ace file

system hello

root
	HELLOWORLD: make

default
	link_flags_optimized("-subsystem:windows -entry:mainCRTStartup")
	assertion(all)
	collect(yes)
	static(yes)

cluster

	current: "."
		end

	-- System clusters

	kernel: "$EIFFEL/clusters/kernel" 
		end
  
	-- Pylon

	pylon: "$PYLON"
		end

	-- Fenestra clusters

	windows: "$FENESTRA/Windows"
		end

	graphics: "$FENESTRA/Graphics"
		end

	controls: "$FENESTRA/Controls"
		end

	system: "$FENESTRA/System"
		end

	internal: "$FENESTRA/Internal"
		visible(tecil)
			NOTIFIER
		end

external
--	shell: "rc -fo hello_rsc.obj hello.rc"

	object:	"hello_rsc.obj", 
		"kernel32.lib", "user32.lib", "gdi32.lib", 
		"advapi32.lib", "comdlg32.lib", "shell32.lib",
		"winmm.lib", "$FENESTRA/runtime/fenr_ms_twr.obj"

end -- helloworld

Eiffel options

After the usual system and root class statements, the assertion clause enables full assertions for debug builds, the collect directive enables the garbage collector which is otherwise disabled. A GUI program must be garbage collected.

Visibility

Using this compiler, the class NOTIFIER from the Windows cluster has to be made visible. The classes must be made visible with the tecil option (the clause is visible (tecil)) to indicate Tower's TECIL interface, as opposed to the default (but less efficient) cecil format.

Linking

As explained in section (see section 2.5.3), the compiler linking option should be changed for producing a GUI executable file. The clause link_flags_optimized in the example above gives the options for the Microsoft C compiler (Visual C++). The default linker options are to produce a console program, which can be useful for debug builds. Microsoft C users may want to add a link_flags_nooptimized("-incremental:yes") clause to enable the incremental low-level linker.

The linked objects are the operating system libraries, the library's runtime, and an object file containing the compiled icon resources. The shell clause gives an example of the rc command producing these compiled resources, it is commented out because the shell command is executed for every single build while icons are not changed that often.

The suffix for the Tower version of the low-level runtime library is twr.

Runtime DLLs

TowerEiffel 2.0 defaults to dynamic linking of its runtime DLL and the C-runtime with some compilers. The overhead for statically linking these libraries is quite small in the case of GUI programs, and it may be convenient to link statically applications made of a small number of executable programs to avoid distributing runtime DLLs.

2.5.5 Making programs with ISE Eiffel

The library was tested with ISE Eiffel 4.0. This compiler also takes a configuration file that specifies the various clusters and compilation options.

There are several things worth noting. First, the visible option only makes visible to the C runtime features that are exported to ANY, so the actual features that are exported by NOTIFIER have to be explicitly specified. This is not really a problem because only the necessary procedures are exported, without adding any unnecessary overhead.

Secondly, this compiler produces a low level makefile that does manage resource compiler scripts (.rc files). It tries to compile a file named <system-name>.rc, to which it appends its own resources (in a separate temporary file, it does not change user-defined files).

Here is an example Lace file for ISE Eiffel:

-- Generic ISE Eiffel Lace file

system app_name

root
	APP (current) : "make"

default
	precompiled ("$EIFFEL4\precomp\spec\$PLATFORM\wel");
	assertion(all)

cluster

	current: "."
		end;

	-- Pylon

	pylon: "$PYLON"
		include "p_system.ise"
		exclude "p_system.e"
		end;

	-- GUI clusters

	windows: "$FENESTRA/Windows"
		visible
			NOTIFIER
				export
					message, message_fonts,
					msg_control, msg_control_key
				end
		end;

	graphics: "$FENESTRA/Graphics"
		end;

	sys: "$FENESTRA/System"
		end;

	controls: "$FENESTRA/Controls"
		end;

	internal: "$FENESTRA/Internal"
		end

external
	-- ISE does 'automatically' compile <app_name>.rc and 
	-- links the resulting object file.

	object: "shell32.lib", "winmm.lib", "$FENESTRA/runtime/fenr_ms_ise.obj"

end -- system

Like for other compilers, this file assumes the environment variables PYLON and FENESTRA are set correctly.

The precompiled section in this example shows ISE's own proprietary GUI library, WEL. Of course, Fenestra only needs the standard kernel, so if it is available only the kernel cluster -- and eventually some of the `base' clusters it depends on -- really need to be included, whether they are precompiled or not. Including the whole of WEL was the only option available during the tests though.

The `pylon' cluster uses include and exclude clauses to handle the compiler-specific class from this cluster. Of course, this is not necessary if the file p_system.ise has been renamed to p_system.e during the installation.

The low-level object section includes system library files that are not automatically linked by the compiler in the version tested, in addition to the Fenestra low level runtime library .

2.5.6 Making programs with SIG Visual Eiffel

(At the time of writing the support for Visual Eiffel is still experimental and compatibility problems are to be expected. Visual Eiffel is a new and rapidly evolving compiler so problems may be solved by newer releases.)

This compiler uses an Eiffel System Description file (.esd file) which is an ordinary text file that can be used from the command line compiler. The supplied Integrated Development Environment provides a GUI interface to manage this file. This section describes the text-based .esd file, equivalent settings are available for users of the GUI environment.

As usual, the library clusters, the kernel and the foundation library's cluster must be included. The class NOTIFIER must be visible.

A generic .esd file looks like that:

- Fenestra .ESD based on template created by vec /cpt

system   app -- name of the executable module (possibly with path)

root     APP

creation  make -- name of the creation procedure

cluster -- list of all the clusters of a project (including libraries used)

	[current "."]
		end
	[ctl "$FENESTRA\controls"]
		end
	[win "$FENESTRA\windows"]
		end
	[sys "$FENESTRA\system"]
		end
	[gdi "$FENESTRA\graphics"]
		end
	[int "$FENESTRA\internal"]
		end
	[Pylon "$PYLON"]
		end
	[KERNEL]
		end

callback  -- list of callbacks ...

interface -- list of interface classes ...
	NOTIFIER -- receives callback from the window system.

makepath  -- list of directories where the make utility is searched for

link      -- list of object files to be linked into a project
	-- "rsc.obj" -- optional: compiled resources (icons, etc)
	"$FENESTRA\runtime\fenr_ms_sve.obj" -- runtime system
	"c:\msdev\lib\winmm.lib" -- Win32 libs not provided with VE, from MS C
	"c:\msdev\lib\wsock32.lib"
	"c:\msdev\lib\shell32.lib"

option -- system wide options

   -- output
   code               native
   target             exe windows
   run_time_in_dll    on

   -- debug
   debug              on -- source texts are being traced ...
   debug instructions on -- the debug instructions are generated

   -- linker
   linker            SIG -- use "Microsoft" if MS C is available for much faster link if RAM is scarce.
   map               off -- a map file is not created
   safe_import       off -- garbage collecting is not allowed inside callbacks
                  -- on  -- garbage collector can safely be used in callbacks

   -- assertions
   assertions        on  -- assertions are allowed to be executed
   require           on
   ensure            on
   class invariant   on
   check             on
   loop variant      on
   loop invariant    on

   -- optimization
   optimize fst         off -- dynamic binding optimization toggle
   fst_expansion_factor 2   -- binding factor
   optimize inline      off -- optimization of access to ARRAY and STRING ...
   optimize constants   on  -- access to constants is optimized
   optimize once        on  -- access to the once functions is optimized

   -- finalization
   finalize          off  -- finalization toggle
   optimize calls    off  -- deadcode elimination ...
   optimize dyn_type off  -- classes without !! applied to are not eliminated
   optimize layout   off  -- class layout optimization ...
   optimize leaves   off  -- 'shortcuts to 'leaves' ...
   optimize flat_fst off  -- 'flat out dynamic tables' optimization ...
end

Visual Eiffel is a native code compiler, so it does not require a C compiler. it nevertheless produces standard Win32 objects file which are compatible with the Microsoft linker among others.

This is an issue because, while it provides -- and automatically links -- system library files for the core Win32 API DLL, some of the system libraries used by Fenestra are missing and must be sourced elsewhere -- from the Visual C++ distribution or the official SDK for instance.

2.5.7 Making programs with SmallEiffel

SmallEiffel is a freeware compiler available from the Internet. It is at an early stage of its development and there are therefore some limitations to using it with this library.

The first issue is that it has no garbage collector. GUI programs need a garbage collector, and SmallEiffel can be used with collectors like the freeware Böhm/Weiser parallel collector which operates on any clean C program, so can be applied to the C programs produced by SmallEiffel. The drawback of this approach is that finalisation is not supported (feature dispose from the kernel class MEMORY), so there may be resource leaks which can be a problem, especially under Windows 95.

Instruction on how to use the Böhm collector with SmallEiffel and Microsoft C are available from http://www.nenie.org/'s web site.

The other issue is that, while innovative, the type inference system sometimes gets confused by complex programs like those using this library of Pylon. This can be avoided by adding artificially the missed types into the live type set. This can be done by code like that (provided it's reached by the type inference system, that is called from either cecil.se or indirectly via the creation procedure):

if false then -- never reach this code but compiler see's
    !!problem_type.make(Void)
end

The distribution includes in compilers/se a class solving the type problems encountered during testing.

A file cecil.se is also provided for use with the -cecil option exporting the appropriate routines for the runtime system. This must be specified on the command line, in addition to the required object files to link.

Example command lines and other files are provided in the distribution, and users are referred to this as a more detailed explanation in this manual would risk becoming rapidly out of date given that SmallEiffel is quickly evolving.

2.5.8 Supported C compilers

The runtime source code and compiled low-level library files are in the runtime directory of the distribution.

The low-level runtime library is provided with a compiled version of the runtime library for Microsoft C on the x86 platform, the compiler tag is ms, so the library file prefix is fenr_ms_ with a suffix added for each supported Eiffel compiler.

As indicated in appendix (see section D), the port to an unsupported C compiler is normally trivial: all that need be done is to adapt the makefile or simply compile the runtime module. Porting to a non-x86 platform for a C compiler supported on the X86 should involve nothing more than executing the corresponding makefile.

The Microsoft C compiler produces standard Win32 COFF files that may be accepted by other C compilers, therefore requiring no porting effort whatsoever.

More C compilers may be supported in the future.