mirror of
https://github.com/EiffelSoftware/eiffel-org.git
synced 2025-12-07 15:22:31 +01:00
Added doc for 21.11
git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@2328 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
[[Property:title|Platform specifics]]
|
||||
[[Property:description|Eiffel's facilities, tools, and libraries that are targeted to specific operation systems]]
|
||||
[[Property:weight|12]]
|
||||
[[Property:uuid|fe9cb24b-3f0f-cb79-8ef0-27c34c851dfe]]
|
||||
Although the majority of Eiffel is completely platform independent, Eiffel does provide solutions for some development needs that are strictly related to the capabilities of specific operating systems. This book documents Eiffel's facilities, tools, and libraries that are targeted to specific operation systems.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
[[Property:title|EiffelCOM Class Reference]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|476f2bc7-9afa-125e-0b68-3e4fc30e97f4]]
|
||||
==View the [[ref:libraries/com/reference/index|EiffelCOM Class Reference]] ==
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[[Property:title|EiffelCOM Interface Content]]
|
||||
[[Property:weight|-2]]
|
||||
[[Property:uuid|a6543000-6009-970e-8a68-a6f3b18c1fc6]]
|
||||
The EiffelCOM library includes the following clusters:
|
||||
* A [[ref:libraries/com/reference/index|Constants]] cluster provides standard COM constants and enumerations.
|
||||
* A [[Interfaces|Interfaces]] cluster includes wrappers for standard interfaces that the EiffelCOM wizard does not generate and deferred classes: [[ref:libraries/com/reference/ecom_interface_chart|ECOM_INTERFACE]] , [[ref:libraries/com/reference/ecom_queriable_chart|ECOM_QUERIABLE]] , and [[ref:libraries/com/reference/ecom_stub_chart|ECOM_STUB]] .
|
||||
* A [[Structures|Structures]] cluster includes wrappers for COM structures and additional data structures.
|
||||
* A [[ref:libraries/com/reference/index|Support]] cluster provides access to helper classes.
|
||||
|
||||
{{seealso|<br/>
|
||||
[[COM and Eiffel]] <br/>
|
||||
[[EiffelCOM Wizard|EiffelCOM wizard]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
[[Property:title|Interfaces]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|7f65c561-cac3-6870-5d1c-6f73beeed872]]
|
||||
COM interfaces have several facets. First, an interface is a deferred class ("abstract class" in C++ terms). This means that an interface is a specification of a type. Second, an interface pointer represents a COM object, which is callable by a client application. An object can expose several interfaces, or represent several types.
|
||||
<!--break-->
|
||||
==ECOM_INTERFACE==
|
||||
|
||||
For each interface listed in a type library, the EiffelCOM wizard generates a deferred class and two effective classes: a proxy of an interface pointer, or a client side class, and a stub of an interface pointer, or a server side class. The deferred interface class inherits from [[ref:libraries/com/reference/ecom_interface_chart|<eiffel>ECOM_INTERFACE</eiffel>]] and has one deferred feature for each interface function. Both effective classes, or implemented interfaces, inherit from the deferred class and implement its functions. The functions of the interface proxy calls the underlying C layer, which in turn calls the COM component while the functions of the interface stub implement the component functionality.
|
||||
|
||||
[[ref:libraries/com/reference/ecom_interface_chart|ECOM_INTERFACE]] holds a pointer to the underlying COM interface.
|
||||
|
||||
==ECOM_QUERIABLE==
|
||||
|
||||
Different languages handle type coercion or type narrowing in different ways. C uses type cast; C++ introduces several type casting mechanisms; Eiffel uses [[ET: Inheritance#Object test|object test]], etc. Every COM interface exposes the <eiffel>QueryInterface</eiffel> function which allows clients to query the COM component for a pointer to another interface. Querying a component for an interface pointer is analogous to using an object test in Eiffel. To accomplish this, EiffelCOM introduces a library class [[ref:libraries/com/reference/ecom_queriable_chart|ECOM_QUERIABLE]], which has the creation routine
|
||||
<code>
|
||||
make_from_other (other: ECOM_INTERFACE)</code>
|
||||
|
||||
which queries a COM component internally. Every interface proxy class inherits from [[ref:libraries/com/reference/ecom_queriable_chart|ECOM_QUERIABLE]]. An important difference between this mechanism and using an object test is that if the creation routine fails to initialize a new [[ref:libraries/com/reference/ecom_queriable_chart|ECOM_QUERIABLE]] object, an exception will be raised, whereas, the <eiffel>attached</eiffel> syntax of the object test will merely produce a <code>False</code> result in the case in which no object of the desired type is available.
|
||||
|
||||
==ECOM_STUB==
|
||||
|
||||
[[ref:libraries/com/reference/ecom_stub_chart|ECOM_STUB]] inherits from [[ref:libraries/com/reference/ecom_interface_chart|ECOM_INTERFACE]], and exposes the feature [[ref:libraries/com/reference/ecom_stub_chart|<code>create_item</code>]] which allows creating the underlying COM object.
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
[[Property:title|Structures]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|fc2071f0-213c-cc88-4914-833b598ceb83]]
|
||||
The [[ref:libraries/com/reference/index|Structures]] cluster includes wrappers for data structures useful when using the COM technology.
|
||||
|
||||
==ECOM_ARRAY==
|
||||
|
||||
ECOM_ARRAY is a multidimensional, resizable array. It is converted to SAFEARRAY at the COM runtime level. Most languages only support SAFEARRAYs of OLE automation types.
|
||||
|
||||
==ECOM_EXCEPTION==
|
||||
|
||||
ECOM_EXCEPTION provides support for triggering and catching exceptions. According to the COM specification, every feature of a COM interface should return a status code of type HRESULT. HRESULT is a 32 bit integer. The COM specification defines possible exception codes and corresponding human-readable descriptions.
|
||||
|
||||
The status code is mapped by the EiffelCOM runtime to Eiffel exceptions. To raise COM-specific exceptions, the class ECOM_EXCEPTION provides the feature <eiffel>trigger</eiffel>:
|
||||
<code>
|
||||
trigger (code: INTEGER)
|
||||
-- Raise exception with code `code'.
|
||||
-- See class ECOM_EXCEPTION_CODES for possible values.
|
||||
</code>
|
||||
|
||||
The class also has several features that help analyzing exceptions and error codes received from the COM runtime.
|
||||
<code>
|
||||
hresult: INTEGER
|
||||
-- Original HRESULT.
|
||||
require
|
||||
applicable: is_developer_exception
|
||||
|
||||
hresult_code: INTEGER
|
||||
-- Status code.
|
||||
require
|
||||
applicable: is_developer_exception
|
||||
|
||||
hresult_facility: INTEGER
|
||||
-- Facility code.
|
||||
require
|
||||
applicable: is_developer_exception
|
||||
|
||||
hresult_message: STRING
|
||||
-- Error message.
|
||||
require
|
||||
applicable: is_developer_exception
|
||||
</code>
|
||||
|
||||
Every call to COM should be wrapped into a <code> rescue </code> clause as follows:
|
||||
<code>
|
||||
some_feature
|
||||
local
|
||||
retried: BOOLEAN
|
||||
...
|
||||
do
|
||||
if not retried then
|
||||
-- Call to COM.
|
||||
end
|
||||
rescue
|
||||
if is_developer_exception then
|
||||
-- Compare `hresult' to some predefined value
|
||||
-- and handle various cases,
|
||||
-- or display a dialog box with an error message
|
||||
-- `hresult_message'.
|
||||
end
|
||||
retried := True
|
||||
retry
|
||||
end
|
||||
</code>
|
||||
|
||||
==ECOM_STRUCTURE==
|
||||
|
||||
ECOM_STRUCTURE is a deferred class which inherits from WEL_STRUCTURE. All wrappers of COM structures inherit from ECOM_STRUCTURE.
|
||||
|
||||
==ECOM_VARIANT==
|
||||
|
||||
ECOM_VARIANT is a wrapper of the VARIANT structure. VARIANT is the Visual Basic equivalent to Eiffel's <eiffel>ANY</eiffel>. In Visual Basic all variable types conform to VARIANT. Since the semantics and runtime architecture of Visual Basic are different from those of Eiffel, there is no simple way to map VARIANT to ANY. Instead, EiffelCOM provides an Eiffel wrapper around the VARIANT structure.
|
||||
|
||||
This wrapper exposes the feature
|
||||
<code>
|
||||
variable_type: INTEGER
|
||||
-- Variable type. See class ECOM_VAR_TYPE for possible values.
|
||||
</code>
|
||||
|
||||
which specifies the actual type of the variant. The class has multiple features for typed access and setting of variables.
|
||||
|
||||
==ECOM_VARIANT_ACCESS==
|
||||
|
||||
<eiffel>ECOM_VARIANT_ACCESS</eiffel> provides the feature:
|
||||
<code>
|
||||
missing: ECOM_VARIANT
|
||||
-- Value representing the default value of a COM optional argument.
|
||||
-- Equivalent to an omitted VB argument, or C++ vtMissing, or .NET System.Reflection.Missing.
|
||||
</code>
|
||||
|
||||
Many COM Automation servers have routines that take arguments with default values. This is very common, for example, in Microsoft Office applications such as Excel and Word. In Visual Basic, such arguments are optional and, when omitted, the server uses the default value. In languages that cannot omit arguments, a special VARIANT value representing the omitted argument must be passed. EiffelCOM applications can pass missing for this purpose.
|
||||
|
||||
For example, the following adds a new workbook to Excel. The default argument tells Excel to decide for itself how many worksheets to put in the new workbook:
|
||||
<code>
|
||||
class
|
||||
MY_EXCEL_APPLICATION
|
||||
|
||||
inherit
|
||||
APPLICATION_PROXY
|
||||
|
||||
|
||||
ECOM_VARIANT_ACCESS
|
||||
|
||||
feature
|
||||
|
||||
add_workbook
|
||||
local
|
||||
workbook: WORKBOOK_PROXY
|
||||
do
|
||||
workbook := workbooks.add (missing, 0)
|
||||
end
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[[Property:title|EiffelCOM Library]]
|
||||
[[Property:weight|8]]
|
||||
[[Property:uuid|b5c66d71-515f-3c73-9c67-c50e4a8d6685]]
|
||||
Type: Library <br/>
|
||||
Platform: Windows <br/>
|
||||
|
||||
==EiffelCOM Library==
|
||||
|
||||
The EiffelCOM library provides support classes for the code generated by the EiffelCOM wizard. It has wrappings for standard COM data types such as VARIANT, SAFEARRAY, CURRENCY, DECIMAL, IUnknown, IDispatch, etc.
|
||||
|
||||
{{seealso|<br/>
|
||||
[[EiffelCOM Wizard|EiffelCOM wizard]] }}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
[[Property:title|Access Type]]
|
||||
[[Property:weight|5]]
|
||||
[[Property:uuid|0d049617-9737-7cc8-810a-9f6f9ca603ec]]
|
||||
Regardless of its location, a COM components can be accessed either through OLE '''Automation''' or '''directly''' through the interface's virtual table.
|
||||
|
||||
==Automation==
|
||||
|
||||
Accessing a COM component through Automation means using a well-known interface called '''IDispatch''' to access to a group of methods and properties. This interface includes the method <eiffel>invoke</eiffel> that allows calling a method, setting or getting a property on the Automation server.
|
||||
|
||||
One advantage of this approach is that the interface has a known virtual table layout. As a result, Windows can include a built-in marshaler for that interface (See [[Deeper into COM]] for information on marshalers). The supported types (known as Automation types) and their Eiffel equivalents are listed in the following table:
|
||||
|
||||
{|
|
||||
|-
|
||||
|
|
||||
COM Type
|
||||
|
||||
|
|
||||
Eiffel equivalent
|
||||
|
||||
|
|
||||
Description
|
||||
|
||||
|-
|
||||
|
|
||||
VARIANT_BOOL
|
||||
|
||||
|
|
||||
BOOLEAN
|
||||
|
||||
|
|
||||
Standard boolean
|
||||
|
||||
|-
|
||||
|
|
||||
unsigned char
|
||||
|
||||
|
|
||||
CHARACTER
|
||||
|
||||
|
|
||||
Standard character
|
||||
|
||||
|-
|
||||
|
|
||||
double
|
||||
|
||||
|
|
||||
DOUBLE
|
||||
|
||||
|
|
||||
Standard double
|
||||
|
||||
|-
|
||||
|
|
||||
float
|
||||
|
||||
|
|
||||
REAL
|
||||
|
||||
|
|
||||
4-byte real value
|
||||
|
||||
|-
|
||||
|
|
||||
int
|
||||
|
||||
|
|
||||
INTEGER
|
||||
|
||||
|
|
||||
Standard integer
|
||||
|
||||
|-
|
||||
|
|
||||
long
|
||||
|
||||
|
|
||||
INTEGER
|
||||
|
||||
|
|
||||
Standard integer
|
||||
|
||||
|-
|
||||
|
|
||||
short
|
||||
|
||||
|
|
||||
INTEGER
|
||||
|
||||
|
|
||||
2-byte integer
|
||||
|
||||
|-
|
||||
|
|
||||
BSTR
|
||||
|
||||
|
|
||||
STRING
|
||||
|
||||
|
|
||||
Standard string
|
||||
|
||||
|-
|
||||
|
|
||||
CURRENCY
|
||||
|
||||
|
|
||||
ECOM_CURRENCY
|
||||
|
||||
|
|
||||
Currency value
|
||||
|
||||
|-
|
||||
|
|
||||
DATE
|
||||
|
||||
|
|
||||
DATE_TIME
|
||||
|
||||
|
|
||||
Standard date
|
||||
|
||||
|-
|
||||
|
|
||||
SCODE
|
||||
|
||||
|
|
||||
INTEGER
|
||||
|
||||
|
|
||||
Return status
|
||||
|
||||
|-
|
||||
|
|
||||
Interface IDispatch *
|
||||
|
||||
|
|
||||
ECOM_INTERFACE
|
||||
|
||||
|
|
||||
Automation interface
|
||||
|
||||
|-
|
||||
|
|
||||
Interface IUnknown *
|
||||
|
||||
|
|
||||
ECOM_INTERFACE
|
||||
|
||||
|
|
||||
Generic interface
|
||||
|
||||
|-
|
||||
|
|
||||
dispinterface
|
||||
|
||||
|
|
||||
ECOM_INTERFACE
|
||||
|
||||
|
|
||||
Automation interface
|
||||
|
||||
|-
|
||||
|
|
||||
Coclass Typename
|
||||
|
||||
|
|
||||
TYPE_NAME
|
||||
|
||||
|
|
||||
Component main class
|
||||
|
||||
|-
|
||||
|
|
||||
SAFEARRAY(TypeName)
|
||||
|
||||
|
|
||||
ECOM_ARRAY [TypeName]
|
||||
|
||||
|
|
||||
Array
|
||||
|
||||
|-
|
||||
|
|
||||
TypeName*
|
||||
|
||||
|
|
||||
CELL [TypeName]
|
||||
|
||||
|
|
||||
Pointer to type
|
||||
|
||||
|-
|
||||
| VARIANT
|
||||
| ECOM_VARIANT
|
||||
| Variant value
|
||||
|-
|
||||
| enum
|
||||
| INTEGER
|
||||
| Enumeration
|
||||
|-
|
||||
|
|
||||
Decimal
|
||||
|
||||
|
|
||||
ECOM_DECIMAL
|
||||
|
||||
|
|
||||
Decimal value
|
||||
|
||||
|}
|
||||
|
||||
Another advantage is a more dynamic discovery of the methods and properties of a component at runtime. Indeed the IDispatch interface also includes methods to determine whether or not a method or property is available and, in the case that it is available, get its identifier. This process is called late binding and allows component to discover at runtime the availability of functionality on other components.
|
||||
|
||||
This approach has also a lot of drawbacks. First, late binding is not an efficient way of calling a function on an interface. This is because first its identifier must be requested, and then the function called. That's two round trips which can be expensive in a distributed environment. Second, since the marshaler is built-in, it has to know in advance all the possible types that a function can accept to be able to marshal the corresponding data. Consequently, there is a limitation on the number of types that one can use in signatures of functions on an Automation compatible interface. The set of available types is called '''Variant''' and cover most of the standard types. It does not allow however the passing of complex user defined data types.
|
||||
|
||||
For these reasons Automation is mostly used in scripting environments (where speed is not an important factor) to accomplish simple tasks.
|
||||
|
||||
==Direct Access==
|
||||
|
||||
Direct interface access is the preferred way to access remote servers where speed becomes a concern and data types are specific to the application. The first interface pointer on the component is obtained through the class object (see [[Coclass|Class Object]] ). Other interfaces on the component are obtained by calling the '''QueryInterface''' function.
|
||||
|
||||
Because information on any interface cannot be accessed dynamically, the description of the interfaces must be provided to tools that need to handle the components such as the EiffelCOM wizard. The official way of describing components and interfaces is through Interface Definition Language (IDL). Once an IDL file has been written to describe a component it can be compiled with MIDL (the Microsoft IDL compiler) to generate both a type library and the code for the marshaler specific to that interface.
|
||||
|
||||
==EiffelCOM==
|
||||
|
||||
One idea behind EiffelCOM is that the details of how a component is accessed should be of no concern to an EiffelCOM programmer. Of course the programmer should be able to choose which kind of access he or she wants to use, but this choice should have no impact on the design of the Eiffel system itself. For that reason, the Eiffel code generated by the wizard follows the same architecture independently of the choice made for interface access and marshaling. The differences in access are handled in the EiffelCOM runtime library where the actual calls to the components are implemented.
|
||||
|
||||
|
||||
{{seealso|<br/>
|
||||
[[EiffelCOM Library|EiffelCOM library]] <br/>
|
||||
[[EiffelCOM: Introduction|Introduction]] <br/>
|
||||
[[COM Concepts|COM Concepts]] <br/>
|
||||
[[COM Interfaces|COM Interfaces]] <br/>
|
||||
[[Coclass|Coclasses]] <br/>
|
||||
[[The Component Location|Component Location]] <br/>
|
||||
[[Deeper into COM|Deeper into COM]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
[[Property:title|Coclass]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:uuid|7f91f9ce-e042-1d48-9d01-f9b794269ec4]]
|
||||
We saw earlier that [[COM Interfaces]] can be perceived as views of a component. This conceptual representation actually maps the implementation of an EiffelCOM component. This is because the coclass inherits from the interfaces and implements their [[uuid:b8c10baa-4f50-adfe-a6f8-9cb56a8f1917#Deferred feature|deferred features]]. Indeed, interfaces are [[uuid:b8c10baa-4f50-adfe-a6f8-9cb56a8f1917#Deferred class|deferred classes]] with all features deferred that are accessible by [[uuid:b8c10baa-4f50-adfe-a6f8-9cb56a8f1917#Client|clients]]. The coclass is an Eiffel class that inherits from these interfaces and implements all the features. This design is not specific to Eiffel though, and can be found in other languages as well. The coclass defines the behavior of the interface functions.<br/>
|
||||
|
||||
==Class Object==
|
||||
Also, we have seen that interfaces are accessed through interface pointers. But how does a client obtain one of these?
|
||||
The answer lies in the class object. The name of this module should really be coclass factory since its goal is to spawn instances of the coclass on request. Class objects are accessed by COM whenever a client requests a new instance of the associated component. COM loads the class object and asks it to provide the interface pointer requested by the client.
|
||||
|
||||
The way a class object is loaded in memory (this process is called activation) depends on the location of the component (See [[The Component Location|Location]] for a description of the possible locations of a component). If the component is an in-process server, then the class object is called directly through the functions exported from the DLL. If the component is an out-of-process server, then it provides COM with a pointer to the class object. In both cases, once the component is loaded, COM has access to the class object and can call it, should a client request a new instance of a component.
|
||||
|
||||
[[Image:com-1|Component Creation]]
|
||||
|
||||
The code for the class object is generated by the EiffelCOM wizard so that Eiffel programmers will not have to worry about it.
|
||||
|
||||
{{seealso|
|
||||
[[EiffelCOM Wizard|EiffelCOM wizard]] <br/>
|
||||
[[EiffelCOM Library|EiffelCOM library]] <br/>
|
||||
[[EiffelCOM: Introduction|Introduction]] <br/>
|
||||
[[COM Concepts|COM Concepts]] <br/>
|
||||
[[COM Interfaces|COM Interfaces]] <br/>
|
||||
[[The Component Location|Component Location]] <br/>
|
||||
[[Access Type|Access Type]] <br/>
|
||||
[[Deeper into COM|Deeper into COM]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
[[Property:title|COM Concepts]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|9cb19fc1-3d26-5752-6232-ea23f2668c32]]
|
||||
This page defines some concepts that are fundamental to the entire technology of COM.
|
||||
<!--break-->
|
||||
==The COM Standard==
|
||||
COM is a standard that describes how a binary component can communicate with the outside world.
|
||||
|
||||
==Interfaces==
|
||||
The component communicates through well-defined interfaces. Each interface is a specification of a group of properties and methods. It is important that the interface does not contain the implementation of the properties and methods, only their specifications (signatures).
|
||||
|
||||
==Coclasses==
|
||||
The actual implementation lies in the [[Coclass|'''coclass''']]. There can be different implementations of the same interface in different coclasses.
|
||||
|
||||
==Class Objects or Class Factories==
|
||||
Finally, each coclass can be instantiated using a class object or class factory.
|
||||
|
||||
|
||||
These notions will be discussed further in the upcoming sections.
|
||||
|
||||
{{seealso|<br/>
|
||||
[[EiffelCOM Library| EiffelCOM library]] <br/>
|
||||
[[EiffelCOM: Introduction|Introduction]] <br/>
|
||||
[[COM Interfaces|COM Interfaces]] <br/>
|
||||
[[Coclass|Coclasses]] <br/>
|
||||
[[The Component Location| Component Location]] <br/>
|
||||
[[Access Type|Access Type]] <br/>
|
||||
[[Deeper into COM|Deeper into COM]] }}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
[[Property:title|COM Interfaces]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|1dbc3fb2-cf0b-b129-1102-ba01e26d8f74]]
|
||||
Interfaces are at the heart of any COM component. Interfaces are described in the definition file of a component. They consist of a group of semantically related functions that can be accessed by the clients of the component. Although they are a specification, they also have a physical representation. A client can request a pointer on an interface and access the component functions through that pointer. Interfaces are the only possible way to access functions from a component. They enforce information hiding by ensuring that clients can access only the component's public functions.
|
||||
|
||||
Interfaces also define the type of a component. Each interface corresponds to a specific view of the component. In a way, this can be compared to polymorphism in the object-oriented world. Whenever an interface from a component is requested, only the functions defined on that interface are accessible, as if the component were polymorphically cast into an object of that type.
|
||||
|
||||
The COM specification requires that any interface provide access to all interfaces on the same component. All interfaces should include a specific function called '''QueryInterface''' that will provide a pointer on any other interface of the component. Interfaces are identified with a globally unique identifier (GUID) guaranteed to be unique in time and space. Since this function has to be on every interface, it has been abstracted into a specific interface called '''IUnknown''' from which all other interfaces must inherit.
|
||||
|
||||
The two other functions exposed by '''IUnknown''' are '''AddRef''' and '''Release'''. These functions should be called when a client gets a reference to an interface and when it discards that reference, respectively. These two functions define the life-cycle of a component at runtime by implementing reference counting—a common way of determining when a component can be removed from memory: when the reference count reaches zero, this signifies that there are no more clients are using that component. You might worry that this business of reference counting will the source of problems such as memory leaks. You would be right, should you choose a low-level language in which to implement your components, since it requires strict discipline on the part of the programmers. Fortunately, you will never have to implement or use these functions in Eiffel: all the processing related to IUnknown is provided by the EiffelCOM runtime. Calls to '''QueryInterface''' are done "behind the scene" and only when needed. The component life-cycle (i.e. calls to '''AddRef''' and '''Release''') is also taken care of by the EiffelCOM runtime.
|
||||
|
||||
{{seealso|<br/>
|
||||
[[EiffelCOM Library|EiffelCOM library]] <br/>
|
||||
[[EiffelCOM: Introduction|Introduction]] <br/>
|
||||
[[COM Concepts|COM Concepts]] <br/>
|
||||
[[Coclass|Coclasses]] <br/>
|
||||
[[The Component Location|Component Location]] <br/>
|
||||
[[Access Type|Access Type]] <br/>
|
||||
[[Deeper into COM|Deeper into COM]] }}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
[[Property:title|The Component Location]]
|
||||
[[Property:weight|4]]
|
||||
[[Property:uuid|083c0120-2eda-9353-ceae-f63e7f407341]]
|
||||
|
||||
==Types of Components==
|
||||
Certainly you've heard of ActiveX, DirectX, OCX, COM+, ADO+, ASP, and other Microsoft technologies. These are all technologies that use the COM standard. This section focuses on categorizing COM components according to their properties as well as the context in which they are used. The categorization will define how the EiffelCOM wizard should be used to wrap or create a component. The first criterion that defines the type of component is its location at run time: will the component be loaded in the client process (in-process) or will the component be a remote server for a distributed application (out-of-process)? In the former case, the component is compiled as a Dynamic Link Libraries (DLL) while in the latter case it is a standard executable.
|
||||
|
||||
==In-process Components==
|
||||
|
||||
Typical instances of DLL components are found in technologies such as OCX, ActiveX, or ASP. These are small, downloadable binaries that will be loaded and executed in a container. The container acts as a client of the component. The EiffelCOM wizard provides the ability to wrap such a component by providing access to its interface to any Eiffel container. It is also possible to create a new In-process component in Eiffel.
|
||||
One main difference with out-of-process components (other than the nature of the module, DLL versus executable) is the way in-process components are activated. In the case of out-of-process components, the component will specify COM when it is ready to receive calls from client. In the case of an in-process server the call is coming directly from COM: COM first loads the DLL into the client process and then calls the exported function DllGetClassObject to access the component class object. The other three exported functions of an in-process component are DllRegister to register the component in the Windows registry, DllUnregister to unregister the component from the registry and finally DllCanUnloadNow which gets called by COM whenever it tries to unload the component from memory. These four functions must be accessible from outside the DLL for the in-process component to work properly.
|
||||
|
||||
==Out-of-process Components==
|
||||
These components are standard executable acting as servers that can be accessed locally or over a network. Typically used in a three tier client server architecture, the major difference with in-process servers (other than the module type - executable instead of DLL) is their lifetime. In-process components are typically loaded to achieve a specific task and unloaded just after the task is complete. Out-of-process components are servers, intended to run continuously. The EiffelCOM wizard allows to build clients for such servers in Eiffel. It also provides the ability to create such servers.
|
||||
|
||||
|
||||
{{seealso|<br/>
|
||||
[[EiffelCOM Library|EiffelCOM library]] <br/>
|
||||
[[EiffelCOM: Introduction|Introduction]] <br/>
|
||||
[[COM Concepts|COM Concepts]] <br/>
|
||||
[[COM Interfaces|COM Interfaces]] <br/>
|
||||
[[Coclass|Coclasses]] <br/>
|
||||
[[Access Type|Access Type]] <br/>
|
||||
[[Deeper into COM|Deeper into COM]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
[[Property:title|Deeper into COM]]
|
||||
[[Property:weight|6]]
|
||||
[[Property:uuid|f28ee911-1b15-260c-a290-540b9531113a]]
|
||||
'''Apartment models''' and '''Marshalling''' are two more interesting areas of COM internals. It is not necessary to understand these details to use the EiffelCOM wizard, but the knowledge could improve decision making when designing new EiffelCOM components.
|
||||
|
||||
==Apartment models==
|
||||
|
||||
The first interesting subject that warrants more in-depth coverage is the execution context of a component. Components can run in the same process as the client, but can also run in a separate process, even on a different machine. But this only takes processes into account. What happens if a component uses multithreading to achieve it tasks? In the case of remote servers, this is a very real possibility. The problem is that a server does not (and should not) know in advance the nature of its clients. It cannot assume that the client will be able to take advantage of its multithreading capabilities. Conversely a multithreaded client should not rely on the server's ability to handle concurrent access.
|
||||
|
||||
The solution chosen in the COM specification is to define an additional execution context called an '''apartment'''. When COM loads a component it creates the apartment in which the component will run. Multiple instances of a multithreaded component will coexist in the same apartment since asynchronous calls will be handled correctly and there is no need to add any synchronization layer. On the other hand, single threaded components will be alone in their apartments and any concurrent calls coming from clients will be first synchronized before entering the apartment. These two behaviors define two different kinds of apartments: '''Multi Threaded Apartments''' (MTA) and '''Single Threaded Apartments''' (STA).
|
||||
|
||||
|
||||
[[Image:com-2|Apartments]]
|
||||
|
||||
|
||||
Apartments solve the problem of concurrency by removing the necessity of knowing the multithreaded capability of a component and its clients. Multithreaded clients can always make asynchronous calls and depending on whether the component handles concurrent access or not, they will be forwarded directly or synchronized first. There can be multiple instances of an STA running in one process, while there can be at most one MTA.
|
||||
|
||||
==Marshalling==
|
||||
|
||||
COM calls can "cross" apartment boundaries. Components from an STA can make calls to components running in an MTA and vice versa. These apartments might be running in different processes or even on different machines. The approach chosen in the COM specification for cross-apartment calls involves a pattern of '''proxies''' and '''stubs'''.
|
||||
|
||||
The idea is to "trick" the client of an interface by providing an interface proxy in its apartment. The proxy includes exactly the same functions as the interface itself but the implementations will merely forward the call to the actual interface. The client's behavior is the same whether it is dealing the actual interface or just a proxy. A point of interest in this approach is that the client implementation is independent from the location of the component.
|
||||
|
||||
But this is not quite the whole story. The call that goes to a proxy will not be forwarded to the actual interface but to its stub. The stub is the counterpart of the proxy—it represents the client for the interface. The interface's behavior is the same whether it is communicating with the actual client or a stub. Although it is not totally true that the component implementation is independent from the location of the client, the stub pattern still helps keep code identical for the implementation of the interface themselves. The implementation of a component will still be different between an in-process or out-of-process component, since it will have to be a DLL in one case and a executable in the other. The design of the interfaces might also differ since out-of-process servers will tend to avoid too many round trips.
|
||||
|
||||
|
||||
[[Image:com-3|Cross Apartment Calls]]
|
||||
|
||||
|
||||
There is one proxy/stub pair per interface. The proxy or the stub is loaded dynamically only when needed. This proxy/stub pair constitute the '''marshaller'''. The reason for having a single name for two different things comes from how MIDL generates its code. MIDL will produce files for one DLL in which both the proxy and the stub will be included. This DLL is the marshaller.
|
||||
|
||||
==Summary==
|
||||
|
||||
This brief introduction to the Component Object Model should be enough to get started with the EiffelCOM wizard. It specifies the main characteristics that define the type of a component and that need to be given to the wizard along with the definition file.
|
||||
|
||||
|
||||
{{seealso| <br/>
|
||||
[[EiffelCOM Library|EiffelCOM library]] <br/>
|
||||
[[COM Concepts|COM Concepts]] <br/>
|
||||
[[Deeper into COM|Deeper into COM]] <br/>
|
||||
[[COM Interfaces|COM Interfaces]] <br/>
|
||||
[[Coclass|Coclasses]] <br/>
|
||||
[[The Component Location|Component Location]] <br/>
|
||||
[[Access Type|Access Type]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
[[Property:title|EiffelCOM: Introduction]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|adadc51d-3dec-320a-9405-0842eacca4f7]]
|
||||
This chapter attempts to cover enough information about COM for Eiffel developers to use the EiffelCOM wizard in an effective way. It does not cover all of COM ... that would (and does) require volumes. Still, this chapter presents the main concepts needed to understand how to build an EiffelCOM system.
|
||||
|
||||
Briefly stated, the Component Object Model is a Microsoft binary standard that establishes how two binary units can access each other at runtime. Such binary units can run in the same process, in different processes on the same machine, or even on different machines. Components can be implemented in any language as long as the compiler produces binaries compliant with the COM standard.
|
||||
|
||||
The advantages of such an approach include an increased reusability (binary reuse), a better version management (COM standard implies that new component versions are compatible with the older ones), and a built-in runtime environment. The binary reuse aspect of COM, augmented with the software quality of Eiffel, offers the developer a platform for increased productivity.
|
||||
|
||||
|
||||
{{seealso|<br/>
|
||||
[[EiffelCOM Library| EiffelCOM library]] <br/>
|
||||
[[COM Concepts|COM Concepts]] <br/>
|
||||
[[COM Interfaces| COM Interfaces]] <br/>
|
||||
[[Coclass| Coclasses]] <br/>
|
||||
[[The Component Location|Component Location]] <br/>
|
||||
[[Access Type|Access Type]] <br/>
|
||||
[[Deeper into COM|Deeper into COM]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
[[Property:title|COM and Eiffel]]
|
||||
[[Property:weight|-15]]
|
||||
[[Property:uuid|32e79152-1e8f-43cd-e014-a83aab18e440]]
|
||||
==About COM and Eiffel==
|
||||
|
||||
{{seealso|[[EiffelCOM Library| EiffelCOM library]] }}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
[[Property:title|Accessing a COM Component]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|d27be959-8f8e-a6f0-cd20-12f6ce71307f]]
|
||||
This third tutorial describes how to access a COM component. It will build a client of the <eiffel>StringManipulator</eiffel> component built in the first tutorial.
|
||||
|
||||
==Step by step instructions==
|
||||
# Launch the EiffelCOM Wizard. Note: if you are running Windows 7, you will need to right-click "EiffelCOM Wizard" in the start menu and select "Run as administrator".
|
||||
# In '''Project''', in the '''Current project''' input field, type in ''string_manipulator_client'' and press '''Enter'''.
|
||||
# In '''Project Type''', choose ''Access an existing COM component''.
|
||||
# In '''Component Information''', click the browse button (the button with '''...''') and open the file ''$ISE_EIFFEL\examples\com\wizard\string_manipulator\string_manipulator.idl'' where ''$ISE_EIFFEL'' represents the path to the EiffelStudio installation directory.
|
||||
# Click '''Next'''.
|
||||
# In '''Component Type''', choose ''In-process (*.dll)''.
|
||||
# In '''Generation Options''', click the browse button and select the directory where the project should be created (which we will later refer to as the ''destination folder''). Choose ''$ISE_EIFFEL\examples\com\wizard\string_manipulator\generated\client'' where ''$ISE_EIFFEL'' represents the path to the EiffelStudio directory.
|
||||
# Make sure both '''Compile C code''' and '''Compile Eiffel code''' are checked.
|
||||
# Select '''Overwrite existing files'''.
|
||||
# Click '''Generate'''.
|
||||
# Wait until the wizard is done.
|
||||
|
||||
==Temporary Work-Around==
|
||||
Note that because Eiffel as a language has evolved somewhat since the '''EiffelCOM Wizard''' was last released, the compile will fail on the last (Eiffel) portion of the compile. However, in this case, it is not sufficient to simply launch EiffelStudio, change the configuration, and finish the compile. The reason is that the EiffelCOM Wizard WOULD HAVE built a pre-compiled library from the C and Eiffel files just generated, and this is important because later steps in this Guided Tour depend upon that library to be there. Thus, to navigate this, you will need to execute the steps below.
|
||||
|
||||
# Let the EiffelCOM Wizard compilation proceed until it fails due to a Eiffel syntax error. Leave the EiffelCOM Wizard running. We will need it below.
|
||||
# Click the EiffelStudio button to launch EiffelStudio on the project file that was generated above.
|
||||
# The first window presented is the "Choose Your Directory" dialog box. UNCHECK the "Compile project" checkbox. Then click '''OK'''.
|
||||
# Once EiffelStudio is open, click the ''Change Project Settings'' toolbar button, and navigate to ''Target: default'' in the navigation pane.
|
||||
# Set the "Void safety" setting to "No", and set the "Syntax" setting to "Transitional syntax".
|
||||
# Click '''OK''' in the Project Settings dialog box.
|
||||
# DO NOT COMPILE, but exit EiffelStudio.
|
||||
# Copy the .ECF file just modified to a safe location. In Windows Explorer, navigate to the ...\string_manipulator\generated\client\Client\ folder. Copy the .ECF file (string_manipulator_idl_client.ecf) outside the project directory temporarily (for example, to the ...\string_manipulator\generated\client\ folder. This .ECF file has the settings that will permit the EiffelCOM Wizard compilation to succeed.
|
||||
# Place this safe copy of the modified .ECF file on the clipboard, ready to copy. Navigate to the folder where you made a copy of the above .ECF file, and in Windows Explorer, while the .ECF file is selected, from the Edit menu, select ''Copy'' (or hit Ctrl-C on the keyboard to make a copy of the file on the Windows clipboard).
|
||||
# In Windows Explorer, navigate back to the ...\string_manipulator\generated\client\Client\ folder, but don't do anything yet.
|
||||
# Quickly execute the following 2 steps: A) in the EiffelCOM Wizard, click the '''Generate''' button. B) After about 2 seconds, after the files have been generated and the C/C++ compilations have started, return to the Windows Explorer window in the ...\string_manipulator\generated\client\Client\ folder, and hit Ctrl-V on the keyboard to PASTE the saved .ECF file into the folder. Confirm when it prompts you whether to replace the .ECF file that was just generated (in Windows 7, this will be a "Copy and Replace" choice).
|
||||
# Watch the EiffelCOM Wizard Output window as it completes successfully this time.
|
||||
# With Windows Explorer, navigate to the ...\string_manipulator\generated\client\Client\EIFGENs\default\W_code\msc\ folder and confirm there is a file there named <code>precomp.lib</code> .
|
||||
|
||||
Now you are ready to continue with the Guided Tour.
|
||||
|
||||
|
||||
==First Look at the Generated Code==
|
||||
At the end of the processing the '''EiffelStudio''' button becomes enabled. Click on it. This will automatically start EiffelStudio with the generated project so you can more easily navigate through the created Eiffel classes.
|
||||
|
||||
{{note|When accessing a COM component the EiffelCOM Wizard will generate a precompiled library which includes all the generated classes. This allows for easy browsing of the generated classes, however a precompilation project is read-only, so you need to start another EiffelStudio to reuse the generated classes. The interesting classes are all related to the coclass proxy <eiffel>STRING_MANIPULATOR_PROXY</eiffel>. The proxy is the Eiffel class that gives access to the component. Each feature on the proxy calls the corresponding interface function on the component. You can use the EiffelStudio opened by the wizard to browse through the generated classes and study the class hierarchy.}}
|
||||
|
||||
==Implementing a Client==
|
||||
To implement a client of the <eiffel>StringManipulator</eiffel> component open a new EiffelStudio. Create the project in ''$ISE_EIFFEL\examples\com\wizard\string_manipulator\client'' using the ecf file found in that directory. Freeze and run the project. You are now accessing the previously built component and calling functions on its interfaces! The class <eiffel>MY_STRING_MANIPULATOR</eiffel> inherits from the generated <eiffel>STRING_MANIPULATOR_PROXY</eiffel> and redefines the feature ''replace_substring_user_precondition''. The generated interfaces include contracts for each exposed function. You can redefine the ''user_precondition'' features to implement your own preconditions.
|
||||
|
||||
{{note|If a COM component should be added to an existing client, the generated ecf file can be added as a library. }}
|
||||
|
||||
==Contracts==
|
||||
Contracts can be broken directly on the proxy in which case you will get a standard contract violation in the client. If contracts are broken on the server then the exception will be forwarded by the EiffelCOM runtime to the client. The feature replace_substring_user_precondition in <eiffel>MY_STRING_MANIPULATOR</eiffel> includes has some assertions. Comment them out. Now the contract of the [[ref:libraries/base/reference/string_8_chart|<code>replace_substring </code>]] feature is wrong and erroneous calls can be made. Quick melt the changes and run the client. Enter some invalid numbers in the fields used to call this feature. After you click '''Replace''' you will see an error message box warning you that a precondition was violated on the server side. This demonstrates contracts `over the wire'. The precondition was violated in the server; this exception was caught by the EiffelCOM runtime and sent back to the client.
|
||||
{{seealso|<br/>
|
||||
[[Creating a New COM Component|Creating a New COM Component]] <br/>
|
||||
[[Creating a new component from an Eiffel project|Creating a new component from an Eiffel Project]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
[[Property:title|Creating a New COM Component]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|f8298b7d-c1a4-c8cc-1821-066f90e7e6e0]]
|
||||
This first tutorial describes creating a COM component from a COM definition file that is either an IDL file or a Type Library. The tutorial focuses on creating an in-process (DLL) component, called <eiffel>StringManipulator</eiffel>. The component exposes one interface <eiffel>IString</eiffel> that includes the functions <eiffel>ReplaceSubstring</eiffel> and <eiffel>PruneAll</eiffel> corresponding respectively to the features [[ref:libraries/base/reference/string_8_chart|<eiffel>replace_substring</eiffel>]] and [[ref:libraries/base/reference/string_8_chart|<eiffel>prune_all</eiffel>]] of the class [[ref:libraries/base/reference/string_8_chart|<eiffel>STRING</eiffel>]] from the EiffelBase library. <eiffel>IString</eiffel> also exposes the property <eiffel>String</eiffel> which represents the manipulated string. The property can be set or read.
|
||||
|
||||
==Step by Step Instructions==
|
||||
# Launch EiffelCOM Wizard. Note: if you are running Windows 7, you will need to right-click "EiffelCOM Wizard" in the start menu and select "Run as administrator".
|
||||
# In the '''Current project''' input field, type in ''string_manipulator_server'' and press '''enter'''.
|
||||
# In '''Project Type''', choose ''Create a new COM component''.
|
||||
# In '''Component Information''', click the browse button (the button with '''...''') and open the file ''$ISE_EIFFEL\examples\com\wizard\string_manipulator\string_manipulator.idl'' where ''$ISE_EIFFEL'' represents the path to the EiffelStudio installation directory.
|
||||
# Make sure '''Generate and use marshaller DLL''' is not checked.
|
||||
# Click '''Next'''.
|
||||
# In '''Component Type''', choose ''In-process (*.dll)''.
|
||||
# In '''Generation Options''', click the browse button and select the directory where the project should be created (which we will later refer to as the ''destination folder''). Choose ''$ISE_EIFFEL\examples\com\wizard\string_manipulator\generated\component'' where ''$ISE_EIFFEL'' represents the path to the EiffelStudio installation directory.
|
||||
# Make sure both '''Compile C code''' and '''Compile Eiffel code''' are checked.
|
||||
# Make sure '''Clean destination folder prior to generation''' is selected.
|
||||
# Click '''Generate'''.
|
||||
# Wait until the wizard is done.
|
||||
|
||||
{{note|Because Eiffel as a language has evolved somewhat since the '''EiffelCOM Wizard''' was last released, you will need to go into the project file under the "Server" subdirectory (string_manipulator_idl.ecf, preferrably from within EiffelStudio) and change the following settings for this example to compile: Select "Target" in the navigator pane, then change the "Void safety" setting to "No", and "Syntax" to "Transitional syntax". Click '''OK''' in the Project Settings dialog box. Now you can finish the compile from within EiffelStudio. Note that there will be a few warnings about obsolete calls. These can be ignored for now.}}
|
||||
|
||||
|
||||
==First Look at the Generated Code==
|
||||
|
||||
At the end of the processing the '''EiffelStudio''' button becomes enabled. Click on it. This will automatically start EiffelStudio with the generated project so you can navigate more easily through the created Eiffel classes. You can save the processing output by clicking the '''Save''' button.
|
||||
|
||||
The deferred class <eiffel>STRING_MANIPULATOR_COCLASS</eiffel> represents the component and exposes all its functionality. It inherits from <eiffel>ISTRING_INTERFACE</eiffel> which corresponds to the '''IString''' interface and has one heir <eiffel>STRING_MANIPULATOR_COCLASS_IMP</eiffel> which implements all the deferred features. The default implementation in the heir is empty.
|
||||
|
||||
==Implementing the Component==
|
||||
To do something more interesting than merely returning an error to the client, edit the implementation of the <eiffel>STRING_MANIPULATOR_COCLASS_IMP</eiffel> class. There is an implementation of the class in ''$ISE_EIFFEL\examples\com\wizard\string_manipulator\server''\. Copy this file over to ''$ISE_EIFFEL\examples\com\wizard\string_manipulator\generated\component\server\component'' and freeze the project. Voila! You have your first EiffelCOM component up and running.
|
||||
|
||||
==Running the Component==
|
||||
The component needs to be registered prior to being loaded. Register an out-of-process component using the following syntax:
|
||||
<code>
|
||||
system_name.exe -Regserver</code>
|
||||
where ''system_name'' is the name of the component executable file. Register an in-process component using the ''regsvr32'' utility using the following syntax:
|
||||
<code>
|
||||
regsvr32 system_name.dll</code>
|
||||
where ''system_name'' is the name of the dll (e.g. string_manipulator). So to register the <eiffel> StringManipulator</eiffel> component, run:
|
||||
<code>
|
||||
cd $ISE_EIFFEL\examples\com\wizard\string_manipulator\generated\component\server\EIFGENs\default\W_code
|
||||
regsvr32 string_manipulator_idl.dll</code>
|
||||
Once registered, follow the steps described in [[Accessing a COM Component|Accessing a COM Component]] to build the component's client.
|
||||
|
||||
|
||||
{{seealso|<br/>
|
||||
[[Creating a new component from an Eiffel project|Creating a new component from an Eiffel Project]] <br/>
|
||||
[[Accessing a COM Component|Accessing a COM Component.]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
[[Property:title|Creating a new component from an Eiffel project]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|f1f6711a-989b-f182-377d-89dd827401d5]]
|
||||
The second tutorial describes creating a COM component from an Eiffel system. The tutorial example <eiffel>String</eiffel> is almost identical to <eiffel>StringManipulator</eiffel> of the previous tutorial. In the <eiffel>StringManipulator</eiffel> example the wizard starts from an IDL file and builds an Eiffel System; in contrast, the <eiffel>String</eiffel> example takes an Eiffel system and generates an IDL file and the required plumbing code to make the Eiffel project accessible to COM.
|
||||
|
||||
==Step by step instructions==
|
||||
# Create a new Eiffel project in the directory ''$ISE_EIFFEL\examples\com\wizard\e2idl\eproject'' where $ISE_EIFFEL represents the path to the EiffelStudio installation directory.
|
||||
# Select the ECF file located in the same directory and compile.
|
||||
# Start the EiffelCOM Wizard
|
||||
# In '''Project''', in the '''Current project''' input field, type in ''string'' and press '''enter'''.
|
||||
# In '''Project Type''', choose ''Add a COM interface to an existing Eiffel project''.
|
||||
# In the field under '''Path to Eiffel project location''' enter ''$ISE_EIFFEL\examples\com\wizard\e2idl\eproject''.
|
||||
# Click the browse button under '''Path to system's configuration file (*.ecf)''' and open the file ''$ISE_EIFFEL\examples\com\wizard\e2idl\eproject\string.ecf''.
|
||||
# In the text field under '''Target to use''', enter: ''string''.
|
||||
# In the text field under '''Name of Eiffel facade class''', enter: <eiffel>string_manipulator</eiffel>.
|
||||
# In the text field under '''Name of Eiffel facade class cluster''', enter: <eiffel>root_cluster</eiffel>.
|
||||
# Click '''Next'''.
|
||||
# In '''Component Type''', choose ''In-process (*.dll)''.
|
||||
# In '''Generation Options''', click the browse button and select the directory where the project should be created. Choose ''$ISE_EIFFEL\examples\com\wizard\e2idl\generated''.
|
||||
# Make sure both '''Compile C code''' and '''Compile Eiffel code''' are checked.
|
||||
# Make sure '''Clean destination folder prior to generation''' is selected.
|
||||
# Click '''Generate'''.
|
||||
# Wait until the wizard is done.
|
||||
|
||||
==First look at the generated code==
|
||||
|
||||
The generated Eiffel classes include:
|
||||
* <eiffel>ISTRING_MANIPULATOR_INTERFACE</eiffel>: This class represents the only interface exposed by the COM component.
|
||||
* <eiffel>STRING_MANIPULATOR_PROXY_IMP</eiffel>: This class implements the coclass, it inherits from the interface and implements its members.
|
||||
* <eiffel>ECOM_STRING_REGISTRATION</eiffel>: This class contains the code required to register the component.
|
||||
You do not need to modify or implement any classes. The wizard produces a ready-to-use component.
|
||||
|
||||
|
||||
{{tip|<br/>
|
||||
In most Eiffel systems functionality is spread out throughout the system. No single class exposes the full functionality of the system and can serve as a Facade to the outside world. Running the wizard on any such class would not be practical. Before starting the wizard write an Eiffel class that acts as a Facade and forwards client calls to the appropriate subsystems. Enter the Facade class name into '''Name of Eiffel facade class''' field. The wizard generates an IDL file from this class. }}
|
||||
|
||||
|
||||
{{seealso|<br/>
|
||||
[[Creating a New COM Component|Creating a New COM Component]] <br/>
|
||||
[[Accessing a COM Component|Accessing a COM Component.]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
[[Property:title|EiffelCOM Wizard Guided Tour]]
|
||||
[[Property:weight|-13]]
|
||||
[[Property:uuid|b0973926-aaac-0131-ae60-d1db6ecf6f38]]
|
||||
This section provides a working example of how to use the EiffelCOM wizard to create COM components and to access existing COM components. <br/>
|
||||
* [[Creating a New COM Component|Creating a New COM Component]] .
|
||||
* [[Creating a new component from an Eiffel project|Creating a new component from an Eiffel Project.]]
|
||||
* [[Accessing a COM Component|Accessing a COM Component.]]
|
||||
|
||||
|
||||
{{Caution|EiffelCOM currently requires the use of the Microsoft C compiler (available with Visual Studio or the Microsoft Windows SDK). EiffelCOM will not work at this time with the MinGW C compiler.}}
|
||||
|
||||
|
||||
{{seealso| '''See Also''' <br/>
|
||||
[[EiffelCOM Wizard Introduction|Introduction]] <br/>
|
||||
[[EiffelCOM Wizard Reference|EiffelCOM wizard reference]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
[[Property:title|EiffelCOM Wizard Introduction]]
|
||||
[[Property:weight|-14]]
|
||||
[[Property:uuid|b73bc137-55ce-5404-e178-d88565cb6b62]]
|
||||
The COM standard allows software components written in different languages to communicate with each other. However, building COM compliant applications requires the development of a large amount of "plumbing" code dedicated to supporting the technology. The EiffelCOM wizard was designed to free Eiffel developers from having to write the plumbing code.
|
||||
|
||||
The '''EiffelCOM Wizard''' is a powerful tool that enables both the rapid development of COM components in Eiffel and the access of COM components from Eiffel systems. The wizard consists of a series of input fields, in which you describe the component's characteristics. The wizard uses data you enter to produce:
|
||||
# An Eiffel system skeleton
|
||||
# The code to access or create a component
|
||||
# The component-specific runtime libraries.
|
||||
|
||||
The wizard allows Eiffel developers with little COM knowledge to develop and reuse COM components. The design of the generated code follows the Eiffel standards and will look familiar to experienced Eiffel users. The only prerequisite to use the EiffelCOM wizard is an understanding of the '''Interface Definition Language'''. '''IDL''' describes a component's interface(s). Compilers can generate '''Type Libraries''' from IDL files. Tools that need information about a given component, such as the EiffelCOM wizard, Visual Basic, etc. will analyze the Type Libraries. The IDL syntax is very close to C so should be easy to learn for developers that are familiar with this language.
|
||||
|
||||
The wizard generates code from a Type Library and additional information given by the user. The generated code consists of Eiffel classes, C/C++ files, and library files. The wizard automatically produces library files from generated C and C++ code. You do not need to modify generated C/C++ code to build your EiffelCOM system.
|
||||
|
||||
|
||||
{{note|The '''EiffelCOM Wizard''' is delivered with EiffelStudio, but it is a '''standalone tool''' (i. e., not part of the EiffelStudio IDE). You can find the wizard by following the directory path: ''$ISE_EIFFEL\wizards\com''. The executable file ''com_wizard_launcher.exe'' will launch the GUI version of the wizard. The executable file ''com_wizard.exe'' is a command-line version of the wizard described [[Wizards: Command Line Options|here]].}}
|
||||
|
||||
|
||||
==Code Generation Process==
|
||||
|
||||
[[Image:introduction]]
|
||||
|
||||
|
||||
The wizard can automatically compile the generated C and Eiffel code. To produce a Type Library corresponding to a given IDL file the wizard relies on '''MIDL''', the Microsoft IDL compiler (installed with the Windows Platform SDK). You may also provide the wizard with a Type Library directly. The wizard can also be used to add a COM interface to an existing Eiffel project. For the rest of the manual a '''COM Definition File''' will refer to the input file given to the wizard (either an IDL file, a Type Library or an Eiffel project file).
|
||||
|
||||
|
||||
{{seealso|[[EiffelCOM Wizard Guided Tour|Guided Tour]] , [[EiffelCOM Wizard Reference|EiffelCOM wizard reference]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
[[Property:title|Building a COM Component]]
|
||||
[[Property:weight|5]]
|
||||
[[Property:uuid|eb299741-4570-c2f1-011a-69da3043c6eb]]
|
||||
When building a COM component, the EiffelCOM wizard allows for the development of both in-process (DLLs) and out-of-process (EXEs) COM components in Eiffel.
|
||||
<!--break-->
|
||||
==Using the Generated Code==
|
||||
|
||||
If the project consists of adding a COM interface to an existing Eiffel project then the wizard will generate (and optionally compile) everything required to implement the COM component. No extra work is required.
|
||||
|
||||
If the project uses a COM definition file (either an IDL file or a type library), the EiffelCOM Wizard generates empty features in the classes corresponding to the component's coclasses. The implementation can then be added into these empty features.
|
||||
|
||||
{{warning|Depending on the project settings, the EiffelCOM Wizard may overwrite the files containing the classes corresponding to the component's colasses if the project is re-generated thereby replacing the implemented features with empty ones. Make sure the destination folder is different from the folder where the classes have been implemented prior to re-generating the project. }}
|
||||
|
||||
Unlike the code generated to access an existing component, the code generated to implement a new COM component will differ for an in-process or an out-of-process component. The difference lies in the component activation code in the class <eiffel>ECOM_<Name_of_system>_REGISTRATION</eiffel>. If the component is in-process then this class includes the four functions that need to be exported from an in-process COM component ( <eiffel>DllRegisterServer</eiffel>, <eiffel>DllUnregisterServer</eiffel>, <eiffel>DllGetClassObject</eiffel>, and <eiffel>DllCanUnloadNow</eiffel>). If the component is out-of-process then the registration class includes a feature initializing the component and its graphical user interface.
|
||||
|
||||
The architecture of the generated code is similar to the one used by the code generated to access an existing component: classes corresponding to the component's coclasses inherit from classes corresponding to the component's interfaces and implement all the deferred features. There is however a major difference: the classes corresponding to the component's coclasses should '''not''' be inherited from. Instead their implementation should be completed. Putting the implementation in a heir class would cause a runtime failure since the EiffelCOM generated code will instantiate the class corresponding to the component's coclass when being called rather than any descendant that would actually contain the implementation. The classes corresponding to the component's coclasses are all suffixed with <eiffel>_COCLASS_IMP</eiffel>.
|
||||
|
||||
|
||||
==Component's GUI==
|
||||
|
||||
In the case of an out-of-process server, the component might need a Graphical User Interface. There are two different scenarios in which the component can be activated: either its user launched it explicitly (e.g. by double clicking the executable icon) or it was launched by the COM runtime to satisfy a client request. In some cases it might be required for the component to have a different behavior in these two cases (for example the GUI could only be necessary when the component was launched explicitly by the user but should be hidden when launched by the COM runtime). The generated registration class for an out-of-process server includes the feature:
|
||||
<code>
|
||||
main_window: WEL_FRAME_WINDOW</code>
|
||||
|
||||
This feature is a <code>once</code> function that can return the class corresponding to the component window. By default, this window is displayed only if COM does not start the component. When COM loads an out-of-process component, it appends the command line parameter "-embedding" to the executable. The generated registration class looks for this option and if it is part of the process argument list then it sets the default window appearance to hidden. Of course this default implementation may be modified if needed.
|
||||
|
||||
|
||||
==Exceptions==
|
||||
|
||||
The COM standard specifies that each COM routine should return a status code to the client. The status code is encoded in a <eiffel>HRESULT</eiffel> value. Such behavior goes against the Command Query Separation Principle in Eiffel. The EiffelCOM generated system should use exceptions instead of returning <eiffel>HRESULT</eiffel> values to the client. By default the EiffelCOM runtime will always return <eiffel>S_OK</eiffel> meaning that the routine succeeded. To notify the client of a problem (e.g. invalid argument value), the EiffelCOM component should raise the corresponding exception from class <eiffel>ECOM_EXCEPTION</eiffel> using the feature <eiffel>trigger</eiffel> from the same class. The EiffelCOM runtime will catch the exception and send the corresponding <eiffel>HRESULT</eiffel> back to the client. Here is what the Eiffel code for a COM component should look like:
|
||||
|
||||
<code>
|
||||
note
|
||||
description: "Eiffel coclass server example"
|
||||
class
|
||||
ECOM_SERVER_COCLASS_IMP
|
||||
|
||||
inherit
|
||||
ECOM_SERVER_COCLASS -- Generated by the wizard
|
||||
|
||||
ECOM_EXCEPTION
|
||||
export
|
||||
{NONE} all
|
||||
end
|
||||
|
||||
feature -- Basic Operations
|
||||
|
||||
coclass_feature (an_argument: ARGUMENT_TYPE)
|
||||
-- Example of a coclass feature
|
||||
do
|
||||
if not is_valid (an_argument) then
|
||||
trigger (E_invalidargument)
|
||||
else
|
||||
-- Normal processing
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
is_valid (an_argument: ARGUMENT_TYPE): BOOLEAN
|
||||
-- Is an_argument a valid argument?
|
||||
do
|
||||
-- Test of validity of an_argument
|
||||
end
|
||||
|
||||
end -- class ECOM_SERVER_COCLASS_IMP</code>
|
||||
|
||||
|
||||
This class inherits from the generated Eiffel coclass and from <eiffel>ECOM_EXCEPTION</eiffel>. It implements the deferred feature <eiffel>coclass_feature</eiffel> from the generated coclass. This feature is part of the interface functions that can be called by clients of the component. Its implementation uses the feature <eiffel>trigger</eiffel> from <eiffel>ECOM_EXCEPTION</eiffel> to raise exceptions in case the feature cannot be executed normally (invalid argument). The EiffelCOM runtime catches the exception and maps it into an <eiffel>HRESULT</eiffel> that is sent back to the client.
|
||||
|
||||
|
||||
{{seealso|[[How the EiffelCOM Wizard works|How the EiffelCOM Wizard Works]] , [[Generated Files|Generated Files]] , [[Class Hierarchy|Class Hierarchy]] , [[Eiffel Project Processing|Adding a COM Interface to an Eiffel Project]] , [[Reusing a COM Component|Accessing a COM Component]] , [[Wizards: Command Line Options|Command Line Options]] }}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
[[Property:title|Class Hierarchy]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|3f78176c-179f-78a4-cf32-47ec548bbb16]]
|
||||
The generated Eiffel code reflects the architecture of the component described in the definition file. Each interface corresponds to a deferred Eiffel class that includes one deferred feature per interface function. The deferred features are implemented in heirs of the interface class. Classes inheriting from these interfaces implement either a coclass or an implemented interface (that is an interface whose instance might be used as an argument on one of the component interfaces functions).
|
||||
|
||||
|
||||
==Projects Accessing Existing COM Components==
|
||||
|
||||
In a project accessing an existing component, the Eiffel classes corresponding to component coclasses inherit from the class [[ref:libraries/com/reference/ecom_queriable_chart| <eiffel>ECOM_QUERIABLE</eiffel> ]] , which is part of the EiffelCOM library. This class includes the feature <eiffel>make_from_other</eiffel> which allows initializing the component from another instance of [[ref:libraries/com/reference/ecom_interface_chart| <eiffel>ECOM_INTERFACE</eiffel> ]] .
|
||||
|
||||
[[Image:interface-inheritance]]
|
||||
|
||||
|
||||
==Projects Implementing New COM Components==
|
||||
|
||||
In a project implementing a new component, the Eiffel classes corresponding to component coclasses inherit from the class [[ref:libraries/com/reference/ecom_stub_chart|<eiffel>ECOM_STUB</eiffel>]], which is part of the EiffelCOM library. This class includes the feature [[ref:libraries/com/reference/ecom_stub_chart|<eiffel>create_item</eiffel>]] which allows initializing the component.
|
||||
|
||||
[[Image:interface-inheritance-server]]
|
||||
|
||||
|
||||
The '''Interface_proxy''' folder includes Eiffel classes wrapping interfaces that may be returned by functions on other interfaces. These classes inherit from both the deferred interface class located in '''Common\Interfaces''' and [[ref:libraries/com/reference/ecom_queriable_chart| <eiffel>ECOM_QUERIABLE</eiffel> ]] .
|
||||
|
||||
[[Image:implemented-interface]]
|
||||
|
||||
|
||||
The '''Interface_stub''' folder includes Eiffel classes implementing interfaces that may be given to the component as an argument. These classes inherit from both the deferred interface class and [[ref:libraries/com/reference/ecom_stub_chart| <eiffel>ECOM_STUB</eiffel> ]] .
|
||||
|
||||
[[Image:implemented-interface-server]]
|
||||
|
||||
{{seealso|[[How the EiffelCOM Wizard works|How the EiffelCOM Wizard Works]] , [[Generated Files|Generated Files]] , [[Eiffel Project Processing|Adding a COM Interface to an Eiffel Project]] , [[Reusing a COM Component|Accessing a COM Component]] , [[Building a COM Component|Building a COM Component]] , [[Wizards: Command Line Options|Command Line Options]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
[[Property:title|Eiffel Project Processing]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:uuid|d94c4060-af70-930b-5023-1c2b2d069633]]
|
||||
The EiffelCOM Wizard can be used to add a COM interface to an existing Eiffel project. The advantage of that approach is that it doesn't require any knowledge about IDL. The EiffelCOM Wizard will take care of generating the IDL from the Eiffel facade class. The generated IDL will define one coclass containing one interface that corresponds to the contract view of the facade class.
|
||||
|
||||
==Designing the Facade Class==
|
||||
|
||||
The facade class should be designed so that it exposes the functionality of the Eiffel system that needs to be accessed from clients of the component. Only the public routines will be exported to the IDL and thus to clients (the class should not have any public attributes, see the list of requirements below).
|
||||
|
||||
There are some rules that the facade class must follow for the COM wizard to be able to generate correct IDL:
|
||||
|
||||
* The facade class must have a creation procedure called <eiffel>make</eiffel> that does not take any argument. This is the procedure that will be called by the EiffelCOM runtime to initialize the Eiffel object when a COM call arrives from a client.
|
||||
|
||||
* The arguments and objects returned by the public routines of the facade class must be of one of the following types: [[ref:libraries/base/reference/character_8_chart| <eiffel>CHARACTER_8</eiffel> ]] , [[ref:libraries/base/reference/integer_32_chart| <eiffel>INTEGER_32</eiffel> ]] , [[ref:libraries/base/reference/real_32_chart| <eiffel>REAL_32</eiffel> ]] , [[ref:libraries/base/reference/real_64_chart| <eiffel>REAL_64</eiffel> ]] , [[ref:libraries/base/reference/boolean_chart| <eiffel>BOOLEAN</eiffel> ]] , [[ref:libraries/base/reference/integer_32_ref_chart| <eiffel>INTEGER_32_REF</eiffel> ]] , [[ref:libraries/base/reference/boolean_ref_chart| <eiffel>BOOLEAN_REF</eiffel> ]] , [[ref:libraries/base/reference/real_32_ref_chart| <eiffel>REAL_32_REF</eiffel> ]] , [[ref:libraries/base/reference/character_8_ref_chart| <eiffel>CHARACTER_8_REF</eiffel> ]] , [[ref:libraries/base/reference/real_64_ref_chart| <eiffel>REAL_64_REF</eiffel> ]] , [[ref:libraries/base/reference/string_8_chart| <eiffel>STRING_8</eiffel> ]] , [[ref:libraries/com/reference/ecom_currency_chart| <eiffel>ECOM_CURRENCY</eiffel> ]] , [[ref:libraries/com/reference/ecom_decimal_chart| <eiffel>ECOM_DECIMAL</eiffel> ]] , [[ref:libraries/com/reference/ecom_interface_chart| <eiffel>ECOM_INTERFACE</eiffel>]] , or [[ref:libraries/com/reference/ecom_array_chart| <eiffel>ECOM_ARRAY</eiffel>]] . Features with arguments or return objects that are of a type not in this list are excluded from the generated IDL file and are not accessible to clients.
|
||||
|
||||
* The facade class should not contain any public attribute, only routines can be exported to COM clients.
|
||||
|
||||
* The facade class must belong to a successfully compiled Eiffel project.
|
||||
|
||||
|
||||
{{seealso|[[How the EiffelCOM Wizard works|How the EiffelCOM Wizard Works]] , [[Generated Files|Generated Files]] , [[Class Hierarchy|Class Hierarchy]] , [[Eiffel Project Processing|Adding a COM Interface to an Eiffel Project]] , [[Accessing a COM Component|Accessing a COM Component]] , [[Reusing a COM Component|Reusing a COM Component]] , [[Wizards: Command Line Options|Command Line Options]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[[Property:title|Generated Files]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|808d49ba-132d-f847-2d8c-f49fe8c499b1]]
|
||||
The EiffelCOM Wizard generates code into the specified destination folder. The file hierarchy is the following:
|
||||
|
||||
[[Image:folders]]
|
||||
|
||||
* The '''Client''' folder includes all the files used to access a COM component. The subfolders '''Clib''' and '''Include''' contain respectively the C++ source files and header files. If a COM Client was generated (as opposed to a COM Server), the subfolder '''Component''' contains the Eiffel class used to provide an interface to the component. Finally, if a COM Server was generated, the subfolder '''Interface_proxy''' contains classes wrapping interfaces that may be returned by functions of the component. These classes may be of interest for systems implementing a new COM component as well. The '''Client''' folder also contains an ECF file that can be used to precompile the generated Eiffel classes if the project consists of accessing an existing component. It is also possible to directly use the ECF file as a library in an existing project.
|
||||
* The '''Common''' folder includes classes that are useful both when accessing and implementing a COM component. It includes the usual C++ folders as well as the '''Interfaces''' and '''Structures''' folders. The '''Interfaces''' folder contains Eiffel classes corresponding to COM interfaces. These are deferred classes that only define the signature of the COM interfaces members. The '''Structures''' folder contains Eiffel classes wrapping any structure defined in the COM definition file (IDL or type library).
|
||||
* Optionally, the EiffelCOM Wizard may generate a '''idl''' folder which contains the IDL file generated from the Eiffel facade class if the project consists of adding a COM interface to an existing Eiffel project.
|
||||
* The '''Server''' folder includes files used to implement a COM component. Apart from the usual C++ folders, it also includes a '''Component''' folder which contains Eiffel classes corresponding to the component's coclasses and a '''Interface_stub''' folder which contains Eiffel classes wrapping interfaces that may be given as an argument to the component. This last folder may also be useful when accessing a COM component. The '''Server''' folder also includes a ECF file which can be used to compile the generated system if the project consists of creating a new component.
|
||||
|
||||
{{seealso|[[How the EiffelCOM Wizard works|How the EiffelCOM Wizard Works]] , [[Class Hierarchy|Class Hierarchy]] , [[Eiffel Project Processing|Adding a COM Interface to an Eiffel Project]] , [[Reusing a COM Component|Accessing a COM Component]] , [[Building a COM Component|Building a COM Component]] , [[Wizards: Command Line Options|Command Line Options]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
[[Property:title|How the EiffelCOM Wizard works]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|2cbfb8ef-d163-fb01-ab8f-5ab156ea36fd]]
|
||||
Depending on the project settings there are between 2 and 6 major steps involved when generating the Eiffel system:
|
||||
* '''IDL Generation''': If the project consists of adding a COM interface to an existing Eiffel project, the first step accomplished by the EiffelCOM Wizard consists of generating an IDL file from the specified facade class (see [[Eiffel Project Processing|Adding a COM interface to an Eiffel project]] for additional details).
|
||||
* '''IDL Compilation''': If the project uses an IDL file (whether it was specified or the result of processing a facade class from an existing Eiffel project), the EiffelCOM Wizard then compiles it into a type library.
|
||||
* '''Type Library Parsing''': The EiffelCOM Wizard then analyzes the type library (whether it was specified in the settings or is the result of an IDL file compilation) and stores the information it needs to generate the code into an internal representation.
|
||||
* '''Code Generation''': The EiffelCOM wizard generates both the Eiffel and C/C++ code from the information gathered during the previous step (see [[Generated Files|Generated Files]] for a list of generated files)
|
||||
* '''C/C++ Compilation''': If specified in the project settings, the EiffelCOM Wizard then compiles the generated C and C++ code into object files and libraries that will be linked into the Eiffel system.
|
||||
* '''Eiffel Compilation''': If the project consists of accessing an existing component then the EiffelCOM Wizard compiles the generated Eiffel code into a precompiled library that contains all the generated classes. If the project consists of creating a new component then the EiffelCOM Wizard compiles the generated Eiffel code into a standard project using the registration class as the root class.
|
||||
|
||||
Optionally, EiffelStudio can be opened on the generated system by clicking the corresponding button after the system has been compiled.
|
||||
|
||||
|
||||
{{note|The EiffelStudio button will only be enabled if the Eiffel code was compiled. }}
|
||||
|
||||
{{seealso|[[Generated Files|Generated Files]] , [[Class Hierarchy|Class Hierarchy]] , [[Eiffel Project Processing|Adding a COM Interface to an Eiffel Project]] , [[Reusing a COM Component|Accessing a COM Component]] , [[Building a COM Component|Building a COM Component]] , [[Wizards: Command Line Options|Command Line Options]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
[[Property:title|EiffelCOM Wizard Reference]]
|
||||
[[Property:weight|-12]]
|
||||
[[Property:uuid|d3564eb8-ef56-4ec1-599f-430b023fa7e8]]
|
||||
The '''EiffelCOM Wizard''' is delivered with EiffelStudio, but it is a '''standalone tool''' (i. e., not part of the EiffelStudio IDE). You can find the wizard by following the directory path: <code lang="text">$ISE_EIFFEL\wizards\com</code>. The executable file <code lang="text">com_wizard_launcher.exe</code> will launch the GUI version of the wizard. The executable file <code lang="text">com_wizard.exe</code> is a command line version of the wizard described [[Wizards: Command Line Options|here]].
|
||||
|
||||
The EiffelCOM Wizard facilitates the creation of Eiffel projects implementing or reusing COM components.
|
||||
|
||||
* [[How the EiffelCOM Wizard works|How the EiffelCOM Wizard works]] describes the steps followed by the wizard when creating the Eiffel project.
|
||||
* [[Generated Files|Generated Files]] describes the list of generated files and their usage.
|
||||
* [[Class Hierarchy|Class Hierarchy]] describes the class hierarchy of the generated Eiffel project.
|
||||
* [[Eiffel Project Processing|Eiffel Project Processing]] covers how to add a COM interface to an existing EiffelStudio project.
|
||||
* [[Reusing a COM Component|Accessing a COM Component]] describes how to reuse an existing COM component.
|
||||
* [[Building a COM Component|Building a COM Component]] describes how to build a new COM component.
|
||||
* [[Wizards: Command Line Options|Command Line Options]] describes using the wizard from the command line.
|
||||
|
||||
{{seealso|<br/>
|
||||
[[EiffelCOM Wizard Introduction|Introduction]] <br/>
|
||||
[[EiffelCOM Wizard Guided Tour|Guided Tour]] }}
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
[[Property:title|Reusing a COM Component]]
|
||||
[[Property:weight|4]]
|
||||
[[Property:uuid|ae910c51-2264-4b65-2567-9dfc1873beca]]
|
||||
When reusing an existing COM component, the wizard generates the necessary code to access it. The plumbing is already done so that instantiating an Eiffel class corresponding to one of the component's coclasses automatically calls the right COM initialization APIs.
|
||||
<!--break-->
|
||||
==Using the Generated Code==
|
||||
|
||||
Calling a feature on an Eiffel class corresponding to one of the component's coclasses forwards the call to the member on the corresponding interface. The data types of the function arguments are either Eiffel types defined in Eiffel data structure libraries, standard data types defined in the EiffelCOM library, or custom data types declared in the COM definition file. For example, from the following IDL line:
|
||||
<code>
|
||||
HRESULT Function ([in] int a, [out, retval] MyStruct * b)</code>
|
||||
|
||||
The wizard generates the following feature:
|
||||
<code>
|
||||
function (a: INTEGER): MY_STRUCT_RECORD</code>
|
||||
|
||||
where <eiffel>MY_STRUCT_RECORD</eiffel> is a generated Eiffel wrapper around the IDL defined structure <eiffel>MyStruct</eiffel>. Structures represent one case of custom data types, interfaces represent another. So for the following IDL:
|
||||
<code>
|
||||
HRESULT Function ([in] ISomething * pInterface)</code>
|
||||
|
||||
The wizard generates the following Eiffel feature:
|
||||
<code>
|
||||
function (p_interface: ISOMETHING_INTERFACE)</code>
|
||||
|
||||
where <eiffel>ISOMETHING_INTERFACE</eiffel> is a generated deferred class corresponding to the <eiffel>ISomething</eiffel> interface. Calling <eiffel>function</eiffel> requires passing an instance of a type that implements <eiffel>ISOMETHING_INTERFACE</eiffel>. The '''stubs''' implementing such interfaces can be found in '''Server\Interfaces_stub'''. In our example the Eiffel class would be named <eiffel>ISOMETHING_IMPL_STUB</eiffel>. The default implementation of the stub is empty and should be completed to implement the wanted behavior. This is how callbacks can be implemented using EiffelCOM.
|
||||
|
||||
|
||||
==Contracts==
|
||||
|
||||
All the Eiffel classes corresponding to the component's interfaces are generated in '''Common\Interfaces'''. These deferred classes include one deferred feature per function defined in the interface. They are equipped with automatically generated assertions. However, the wizard cannot generate fully specified contracts since it has no domain specific knowledge. It can only generate contracts that are domain independent. Such contracts, although useful, are not enough to describe entirely the behavior of the component. Generated contracts include checking for <eiffel>Void</eiffel> Eiffel objects as well as <code>null</code> C pointers for wrappers. There might be a need for additional assertions. Invariants and postconditions can be added in an heir of the generated Eiffel coclass proxy. Preconditions, however, cannot be strengthened. A workaround provided by the wizard consists of generating a precondition function for each feature in the interface. The default implementation of these functions always returns <eiffel>True</eiffel>. They can be redefined to implement the correct behavior:
|
||||
<code>
|
||||
function (a: INTEGER): MY_STRUCT
|
||||
-- Example of a generated Eiffel coclass feature
|
||||
require
|
||||
function_user_precondition: function_user_precondition
|
||||
do
|
||||
...
|
||||
ensure
|
||||
non_void_my_struct: Result /= Void
|
||||
end</code>
|
||||
|
||||
So the complete class hierarchy for an Eiffel client coclass is the following:
|
||||
[[Image:client-inheritance]]
|
||||
|
||||
|
||||
==Exceptions==
|
||||
|
||||
The COM standard requires that any interface function returns a status value (known as a <eiffel>HRESULT</eiffel>). This means that any function which adheres to the COM standard actually corresponds to a side effect feature which the Eiffel methodology tries to avoid according to the ''Command Query Separation Principle''. The workaround used in EiffelCOM systems consists in mapping these return values to Eiffel exceptions. So if the component returns an <eiffel>HRESULT</eiffel> corresponding to an error code, the EiffelCOM runtime raises an Eiffel exception that needs to be caught by the client.
|
||||
|
||||
[[Image:exception-raising]]
|
||||
|
||||
As a result, any feature in the client making calls to the Eiffel classes corresponding to the component's coclasses should include a <code>rescue</code> clause. The processing done in this clause might depend on the nature of the exception. All the standard COM exceptions can be found in the library class <eiffel>ECOM_EXCEPTION_CODES</eiffel>, which is inherited from by <eiffel>ECOM_EXCEPTION</eiffel>. The later also inherits from the kernel class <eiffel>EXCEPTIONS</eiffel> and can consequently be used by the coclass client to catch the exceptions.
|
||||
|
||||
The following code snippet illustrates how a client can process exceptions raised by a call to an Eiffel class representing a component's coclass:
|
||||
<code>
|
||||
note
|
||||
description: "Eiffel coclass client example"
|
||||
|
||||
class
|
||||
COCLASS_CLIENT
|
||||
|
||||
inherit
|
||||
ECOM_EXCEPTION
|
||||
export
|
||||
{NONE} all
|
||||
end
|
||||
|
||||
feature -- Basic Operations
|
||||
|
||||
coclass_feature_client
|
||||
-- Example of a coclass feature caller
|
||||
local
|
||||
retried: BOOLEAN
|
||||
coclass: EIFFEL_COCLASS_PROXY
|
||||
do
|
||||
if not retried then
|
||||
create coclass.make
|
||||
coclass.coclass_feature -- Actual call
|
||||
end
|
||||
rescue
|
||||
if hresult = E_notimpl then
|
||||
-- Process non implemented function error.
|
||||
retried := True
|
||||
retry
|
||||
elseif hresult = E_invalidarg then
|
||||
-- Process invalid argument error.
|
||||
retried := True
|
||||
retry
|
||||
else
|
||||
-- Forward exception to caller.
|
||||
end
|
||||
end
|
||||
|
||||
end -- class COCLASS_CLIENT</code>
|
||||
|
||||
{{seealso|[[How the EiffelCOM Wizard works|How the EiffelCOM Wizard Works]] , [[Generated Files|Generated Files]] , [[Class Hierarchy|Class Hierarchy]] , [[Eiffel Project Processing|Adding a COM Interface to an Eiffel Project]] , [[Building a COM Component|Reusing a COM Component]] , [[Wizards: Command Line Options|Command Line Options]] }}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
[[Property:title|Wizards: Command Line Options]]
|
||||
[[Property:weight|6]]
|
||||
[[Property:uuid|ca417e46-ad46-2d3f-e13e-a88366dc06b3]]
|
||||
The EiffelCOM Wizard can be used from the command line for example in batch scripts. The command line utility is <code>com_wizard.exe</code> and is located in <code>$ISE_EIFFEL\wizards\com</code>. Each command line option has both a long and a short name. The long names are all prefixed with <code>--</code> while the short names are all prefixed with <code>-</code>. Options that take an argument should have the argument directly following the option with a space or a <code>=</code> sign in between the option name and the argument as in:
|
||||
<code lang="text">
|
||||
--ace="mysystem.ecf"</code>
|
||||
this option is equivalent to:
|
||||
<code lang="text">
|
||||
-a "mysystem.ecf"</code>
|
||||
|
||||
The list of options is the following:
|
||||
* '''--client, -c''' <DEFINITION_FILE>: Build client to access COM component described by <DEFINITION_FILE>.
|
||||
* '''--server, -s''' <DEFINITION_FILE>: Build new COM component as described by <DEFINITION_FILE>.
|
||||
* '''--eiffel, -e''' <PROJECT_FILE>: Add COM interface to Eiffel project with project file (*.epr) <PROJECT_FILE>.
|
||||
* '''--ace, -a''' <ECF_FILE>: Path to ecf file of Eiffel project to be added a COM interface. Use together with <code>'--eiffel'</code>.
|
||||
* '''--facade, -f''' <FACADE_CLASS>: Name of facade class to generate IDL from. Use together with <code>'--eiffel'</code>.
|
||||
* '''--cluster, -u''' <CLUSTER>: Name of facade class cluster. Use together with <code>'--eiffel'</code>.
|
||||
* '''--outofprocess, -o''': Access or build out of process component. By default access or build in-process component (DLL).
|
||||
* '''--compilec, -i''': Compile generated C code.
|
||||
* '''--compileeiffel, -l''': Compile generated Eiffel code. Implies '--compilec'.
|
||||
* '''--marshaller, -m''': Build marshaller DLL, can only be used with '--server' and if definition file is an IDL file.
|
||||
* '''--destination, -d''' <DESTINATION>: Generate files in <DESTINATION> folder. By default files are generated in current folder.
|
||||
* '''--cleanup, -p''': Cleanup destination folder prior to generation. This option cannot be used together with <code>'--backup'</code>.
|
||||
* '''--backup, -b''': Backup overriden files by adding extension '.bac'. This option cannot be used together with <code>'--cleanup'</code>.
|
||||
* '''--graphical, -g''': Launch GUI, all other options are ignored.
|
||||
* '''--nologo, -n''': Do not display copyright information.
|
||||
* '''--version, -v''': Print version information.
|
||||
* '''--help, -h''': Display help on how to use the EiffelCOM Wizard command line utility.
|
||||
|
||||
{{seealso|[[How the EiffelCOM Wizard works|How the EiffelCOM Wizard Works]] , [[Generated Files|Generated Files]] , [[Class Hierarchy|Class Hierarchy]] , [[Eiffel Project Processing|Adding a COM Interface to an Eiffel Project]] , [[Reusing a COM Component|Accessing a COM Component]] , [[Building a COM Component|Building a COM Component]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
[[Property:title|EiffelCOM Wizard]]
|
||||
[[Property:weight|4]]
|
||||
[[Property:uuid|49d5c1b9-14f8-5bea-6641-50e9cf6715cf]]
|
||||
Type: Wizard <br/>
|
||||
Platform: Windows
|
||||
|
||||
{{seealso|<br/>
|
||||
[[EiffelCOM Library|EiffelCOM library]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[[Property:title|COM]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|993f5778-97c1-e113-31d7-61ae81c559fb]]
|
||||
Microsoft COM (Component Object Model) is a platform-independent, distributed, system for creating binary software components that can interact. COM is the foundation technology for Microsoft's OLE (compound documents) and ActiveX (Internet-enabled components) technologies.
|
||||
<!--break-->
|
||||
{{definition|Automation|Automation is a Microsoft Windows Platform term that means, when an application provides it, that that application exposes its unique features to scripting tools and other applications. Automation uses the Component Object Model (COM).}}
|
||||
|
||||
:See also [http://msdn.microsoft.com/en-us/library/windows/desktop/ms221375%28v=vs.85%29.aspx Automation].
|
||||
|
||||
|
||||
There is a host of technologies and capabilities that are available to your Eiffel applications on the Microsoft Windows platform through COM interfaces. For instance, a number of Microsoft products (e.g. Microsoft Word, Excel, Visio, Outlook, etc.) can be enhanced or controlled by other software through Automation via a COM interface. It is not complicated to, for example, have your Eiffel application open Excel, open a worksheet, evaluate its contents, and make extensive changes, or in-depth queries into its contents, either on the screen or invisibly. At this writing, all Microsoft Office products offer extensive COM interfaces for such purposes.
|
||||
|
||||
In addition, Eiffel supports the capability of making Eiffel applications into COM-compliant COM objects, thus exposing callable COM interfaces to other software for similar purposes.
|
||||
|
||||
The EiffelCOM Wizard, EiffelCOM Runtime, and EiffelCOM Library are there to make this job easier.
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
[[Property:title|EiffelRibbon Design Tool]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|E0B7F3F8-F7B1-43C4-8B7E-D175BC9A7D9F]]
|
||||
=Overview=
|
||||
|
||||
The EiffelRibbon design tool allows you to design a ribbon graphically, then generate the ribbon markup file and the Eiffel classes necessary to implement your design.
|
||||
|
||||
|
||||
[[Image:EiffelRibbon design tool 01|EiffelRibbon design tool]]
|
||||
|
||||
Figure 1: An EiffelRibbon project open in the EiffelRibbon design tool.
|
||||
|
||||
|
||||
The interface of the design tool mimics that of [[EiffelBuild|EiffelBuild]]. That is, the interface contains three panes that have counterparts in EiffelBuild:
|
||||
|
||||
# Type selector: A list of all the elements available for use in a ribbon.
|
||||
# Layout constructor: A structured view of the organization of the elements used in a particular ribbon design.
|
||||
# Object editor: The properties of the ribbon element which is currently selected in the Layout constructor.
|
||||
|
||||
So, if you've used EiffelBuild, the EiffelRibbon design tool should seem quite familiar to you.
|
||||
|
||||
=Usage=
|
||||
|
||||
The typical usage of the design tool is much like the typical usage of EiffelBuild.
|
||||
|
||||
* Create a new project ( <code lang="text">File -> New Project...</code> ) and choose a location for your EiffelRibbon project.
|
||||
* Configure the ribbon by adding elements, name the elements, and choose icons for buttons.
|
||||
* Save the project.
|
||||
* Generate the code.
|
||||
* Use EiffelStudio to compile the generated code.
|
||||
|
||||
==Size definitions and scaling policies==
|
||||
|
||||
An important aspect of Microsoft's ribbon technology is called '''[http://msdn.microsoft.com/en-us/library/windows/desktop/dd316927(v=vs.85).aspx adaptive layout]'''. When the ribbon is resized by a user at runtime, the adaptive layout makes corresponding changes to the size, format, organization and scale of the ribbon controls within each group based on size definition templates and scaling policies associated with the group. Size definitions are patterns for the use of a certain number of ribbon controls such as buttons. For a particular group of controls in an instance of ribbon, you can associate a size definition which will control the layout of the controls. At runtime, when the window hosting the ribbon is resized, a scaling policy, also associated with the group, will control how the controls in the group are reorganized in response to the resizing. When a group is selected in the EiffelRibbon tool, the group's size definition and scaling policy can be selected.
|
||||
|
||||
{{note|Not all size definitions support all scaling policy choices. So it is important to know which scaling policies are supported by a size definition that you wish to associate with a group before you set the scaling policy for the group.}}
|
||||
|
||||
===Size definitions===
|
||||
|
||||
The ribbon framework provides a significant number of size definition templates (simply called '''size definitions'''), as shown in Figure 2 below. Size definitions are usually assigned names based on the number and type of controls included in the group. For example, the size definition ''"ThreeButtonsAndOneCheckBox"'' is a size definition for three buttons plus one checkbox.
|
||||
|
||||
|
||||
[[Image:EiffelRibbon Choose Size Definition]]
|
||||
|
||||
Figure 2. Choosing a size definition for a ribbon control group.
|
||||
|
||||
|
||||
In addition to using the size definitions provided, it is possible to create your own custom size definitions. You do this with the '''Size Definition Editor'''. At the lower left of Figure 1 above you can see the tab representing the Size Definition Editor. When you expand the tab, the editor becomes visible, as in Figure 3, below.
|
||||
|
||||
|
||||
[[Image:EiffelRibbon size definition editor|Size Definition Editor]]
|
||||
|
||||
Figure 3. EiffelRibbon tool with the Size Definition Editor pinned open.
|
||||
|
||||
|
||||
Use the Size Definition Editor to create custom size definitions for your ribbon designs. The '''Add button''' button will add a new button placeholder to a custom size definition. A selected button placeholder can be altered to show or hide its label and use its small or large size.
|
||||
|
||||
|
||||
[[Image:Size Definition Editor 02|Button placeholders]]
|
||||
|
||||
Figure 4. Three button placeholders in a custom size definition (highlighted in red rectangle); one is large, and two are small. The text is visible in all cases. (This is the arrangement used for the "medium" configuration (highlighted in green rectangle) for the ''ThreeButtonCustom'' size definition used in the scaling policy figures in the next section. Screenshots of specifying arrangements used for "large" "small" configurations for ThreeButtonCustom are not shown.)
|
||||
|
||||
|
||||
Once a custom size definition has been designed, it can be named, saved, and used in a ribbon design.
|
||||
|
||||
|
||||
[[Image:Size Definition Editor 03|Name field and Save button]]
|
||||
|
||||
Figure 5. The Size Definition Editor naming field and '''Save''' button.
|
||||
|
||||
|
||||
When you generate code which uses a custom size definition, the EiffelRibbon Design Tool generates the (XML) representation for your size definition and then invoke the Microsoft Ribbon Framework's User Interface Command Compiler (UICC.exe) to validate the generated size definition code. This process can result in [http://msdn.microsoft.com/en-us/library/windows/desktop/dd316918(v=VS.85).aspx certain types of errors].
|
||||
|
||||
User Interface Command Compiler errors will appear in the Output pane of the EiffelRibbon Design Tool.
|
||||
|
||||
|
||||
===Scaling policy===
|
||||
|
||||
The notion of adaptive layout includes the ability to specify preferred sizes for elements of a group and a scaling policy that can influence how the group reacts to resizing. More detail on size definition and scaling policy is available in Microsoft's [http://msdn.microsoft.com/en-us/library/windows/desktop/dd316927(v=vs.85).aspx ribbon documentation].
|
||||
|
||||
The EiffelRibbon tool allows you to specify ideal sizes and scale policy for a groups elements through the '''Ideal sizes''' radio buttons and the '''Scale''' checkboxes, as shown in Figure 6 below.
|
||||
|
||||
|
||||
[[Image:EiffelRibbon design tool 02|Scaling policy elements: "Ideal sizes" and "Scale"]]
|
||||
|
||||
Figure 6. A group definition showing it's size definition and the scaling policy elements "Ideal sizes" and "Scale".
|
||||
|
||||
|
||||
In Figure 6, none of the Scale checkboxes is checked. This means that no scaling policy is in effect for the group ''group_context_popups''. So in a running application, a window with this ribbon might be rendered as shown in Figure 7:
|
||||
|
||||
|
||||
[[Image:EiffelRibbon scaling policy 01]]
|
||||
|
||||
Figure 7. A ribbon with no scaling policy, but with enough room to display all groups.
|
||||
|
||||
|
||||
If the user resized the window to a narrower form, the buttons in the ''group_context_popups'' group would become occluded because scaling is not in effect. This is shown in Figure 8.
|
||||
|
||||
|
||||
[[Image:EiffelRibbon scaling policy 01a]]
|
||||
|
||||
Figure 8. After resizing, some buttons of the group are no longer visible.
|
||||
|
||||
|
||||
If all of the ''Scale'' checkboxes that are supported by the size definition are checked in the EiffelRibbon tool for the group ''group_context_popups'' as shown in Figure 9, then scaling is enabled.
|
||||
|
||||
|
||||
[[Image:EiffelRibbon scaling policy 02|Scale checkboxes enable scaling policy.]]
|
||||
|
||||
Figure 9. All supported scales are enabled for ''group_context_popups''.
|
||||
|
||||
|
||||
In the running application again, the initial rendering looks the same as in Figure 7 (its ''Large'' configuration). But if the window is resized to a narrower form with scaling policy enabled, it will go through the various configurations supported by the size definition: ''Medium'', ''Small'', then ''Popup''. The different configurations for this particular ribbon and size definition are shown in Figures 10 through 12.
|
||||
|
||||
|
||||
[[Image:EiffelRibbon scaling policy 03|Medium configuration]]
|
||||
|
||||
Figure 10. Scaling policy at medium configuration (see Figure 4 above).
|
||||
|
||||
|
||||
|
||||
[[Image:EiffelRibbon scaling policy 04|Small configuration]]
|
||||
|
||||
Figure 11. Scaling policy at small configuration.
|
||||
|
||||
|
||||
|
||||
[[Image:EiffelRibbon scaling policy 05|Popup configuration]]
|
||||
|
||||
Figure 12. Scaling policy at popup configuration.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
[[Property:title|EiffelRibbon]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|2f24957c-7fdc-f918-8c0d-37a9ef525508]]
|
||||
=Introduction=
|
||||
|
||||
EiffelRibbon is a library of classes with an associated [[EiffelRibbon Design Tool|tool]] which allows the integration of the Microsoft Windows "Ribbon" into graphical user interfaces programmed with [[EiffelVision 2]] and targeted to Microsoft Windows. The ribbon is part of a [http://msdn.microsoft.com/en-us/library/aa338198.aspx#office2007uifordevelopers_detaileddesignofthenewuisystem revamped user interface approach] introduced by Microsoft with the 2007 release of the Microsoft Office applications. So you've experienced the ribbon if you've used Microsoft Word, PowerPoint, or Excel from 2007 or later.
|
||||
|
||||
|
||||
{{note|''Although EiffelRibbon classes work with EiffelVision 2 which is multi-platform, systems using EiffelRibbon can be targeted only to the Microsoft Windows platform.''}}
|
||||
|
||||
|
||||
[[Image:EiffelRibbon window 01|A ribbon on a window]]
|
||||
|
||||
Figure 1: A ribbon at the top of a window.
|
||||
|
||||
|
||||
The EiffelRibbon library contains the classes that are used to implement the ribbon facilities. The classes in the EiffelRibbon library all have class names beginning "EV_", the default prefix for EiffelVision 2 classes. So the EiffelRibbon classes can be seen as an extension to the EiffelVision 2 library. If you've used EiffelVision 2 to create applications with graphical user interfaces, then the EiffelRibbon class won't look unfamiliar to you. However, unlike other EiffelVision 2 classes, the EiffelRibbon classes only work in applications targeted to Microsoft Windows.
|
||||
|
||||
To gain a detailed understanding of the goals of Microsoft's Ribbon technology including some guidelines on designing effective ribbons, see [http://msdn.microsoft.com/en-us/library/cc872782.aspx the '''Ribbons''' page] on Microsoft's MSDN site.
|
||||
|
||||
The [[EiffelRibbon Design Tool|EiffelRibbon design tool]] helps you configure a ribbon in much the same way that [[EiffelBuild|EiffelBuild]] (ES Builder) helps you layout a GUI application.
|
||||
|
||||
|
||||
=The EiffelRibbon Library=
|
||||
|
||||
The EiffelRibbon library contains the classes necessary to add ribbons to EiffelVision 2 applications. As stated above, you won't really need to use the library directly. Rather, you use the EiffelRibbon tool to build an application that uses the library classes.
|
||||
|
||||
Still you may find it interesting to browse the classes in the library. You'll see that as in the EiffelVision 2 library, many of the classes are there to provide support for the various elements that can be used in an application. Most of the class names for EiffelRibbon-specific elements begin with the prefix "<code>EV_RIBBON</code>". Some examples are: <code>EV_RIBBON_BUTTON</code>, <code>EV_RIBBON_DROP_DOWN_GALLERY</code>, and <code>EV_RIBBON_COMBO_BOX_ITEM</code>. Again, similar to EiffelVision 2, the class <code>EV_RIBBON_COMBO_BOX_ITEM</code> models an item for a <code>EV_RIBBON_COMBO_BOX</code>.
|
||||
|
||||
So, here again, if you are familiar with EiffelVision 2, then EiffelRibbon should not seem very foreign to you.
|
||||
|
||||
|
||||
=EiffelRibbon Applications=
|
||||
|
||||
When you build an application with the [[EiffelRibbon Design Tool|EiffelRibbon tool]], it generates classes for you. These classes are often clients or heirs to classes in the EiffelRibbon library. The classes generated for the objects on your ribbon should look familiar to you as well. For example, as with EiffelBuild, for each ribbon object, say a button, there will be a class ending with the suffix "<code>_IMP</code>" which will get regenerated each time you request the EiffelRibbon tool to generate classes. Also generated is an heir to that class with a name that does not include the suffix. This class you are free to edit, as it will not be regenerated. It is in these editable classes that you provide the action sequences for your ribbon objects' behaviors.
|
||||
|
||||
The class shown in the Edit pane of EiffelStudio in Figure 2 is a portion of a generated class <code>BUTTON_CHANGE_SMALL_IMAGE</code>, representing a ribbon button, from one of the EiffelRibbon examples. <code>BUTTON_CHANGE_SMALL_IMAGE</code> is a heir to <code>BUTTON_CHANGE_SMALL_IMAGE_IMP</code>. You can see that in the redefined version of <code>create_interface_objects</code> code has been added to add an agent to the <code>select_actions</code> for the ribbon button. In this case, it's an inline agent that will toggle the associated image back and forth between two different images.
|
||||
|
||||
|
||||
[[Image:EiffelRibbon application 01|An EiffelRibbon application in EiffelStudio.]]
|
||||
|
||||
Figure 2.
|
||||
|
||||
==Project '''modes'''==
|
||||
|
||||
EiffelRibbon projects can be built in one of two different '''project modes'''. The project mode is selected from the '''Project''' menu of the EiffelRibbon design tool.
|
||||
|
||||
The default mode, called '''DLL mode''', is the most all encompassing project mode. It has the advantage that the full capabilities of EiffelRibbon are accessible. In DLL mode, a separate DLL is created to contain the generated code for each ribbon created for the project.
|
||||
|
||||
The alternative to DLL mode is '''Application mode'''. Application mode allows almost all the capabilities of DLL mode, but includes all ribbon code in the project's executable image (.exe). For this reason, it is necessary to re-freeze the project after changes when using Application mode. Re-freezing is not necessary in DLL mode.
|
||||
|
||||
Both modes allow multiple ribbons to be created for multiple windows. However certain restrictions apply in Application mode that do not apply in DLL mode. In DLL mode, different DLL's are created for each ribbon. These DLL's are separate and do not share data. However, in Application mode, a number of ribbon facilities, including the help button, the quick access toolbar, and the mini toolbar, are shared among all the ribbons defined. In DLL mode, these facilities are not shared and as such can be different for each ribbon.
|
||||
|
||||
=Usage notes=
|
||||
|
||||
==System requirements==
|
||||
|
||||
To use the EiffelRibbon library and tools you must have:
|
||||
|
||||
# Windows 7
|
||||
# The Microsoft C compiler provided with either:
|
||||
## Windows SDK 7.0 or greater
|
||||
## Visual Studio 2008 or greater
|
||||
|
||||
|
||||
==Current state of development==
|
||||
|
||||
Initial distribution:
|
||||
* The EiffelRibbon library is not usable directly. The EiffelRibbon design tool allows you to design a ribbon and will generate code that relies on the EiffelRibbon library.
|
||||
* A class <code>EV_RIBBON_TITLED_WINDOW</code> is provided with the library. <code>EV_RIBBON_TITLED_WINDOW</code> is a descendant of the standard EiffelVision 2 class <code>EV_TITLED_WINDOW</code>. The ribbon classes generated by EiffelRibbon tool can be added only to instances of <code>EV_RIBBON_TITLED_WINDOW</code>.
|
||||
|
||||
==Known issues and limitations==
|
||||
|
||||
Initial distribution:
|
||||
* Resizing policy is not yet supported.
|
||||
* When working in Application Mode, it is necessary to freeze the target system each time the EiffelRibbon code is regenerated. This is not necessary in DLL Mode.
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
[[Property:title|Microsoft Windows]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|b6513eee-a12c-1f9f-4257-b08d2e18cef4]]
|
||||
== Eiffel with Microsoft Windows ==
|
||||
|
||||
The primary source for Eiffel platform specific support for Microsoft Windows.
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
[[Property:title|Building a .NET application]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|a44736cf-41df-1679-9c6d-7b56f31d5f9a]]
|
||||
Eiffel for .NET has some specific functionality meant to leverage necessary aspects of the .NET Framework.
|
||||
|
||||
For that reason, the [[EiffelStudio: Project settings window|project settings]] for Eiffel for .NET introduces new options. These options include:
|
||||
* Specifying whether the generated assembly should be an EXE or a DLL.
|
||||
* Choosing between generating verifiable or non verifiable IL code. Non verifiable IL code executes faster but requires high trust settings.
|
||||
|
||||
The Eiffel for .NET compiler generates a single assembly whose name is the name of the system as given in the [[System Options|system options of the project settings]] .
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
[[Property:title|Eiffel CodeDom Provider Manager]]
|
||||
[[Property:weight|-1]]
|
||||
[[Property:uuid|089e658f-4bd8-df8f-5647-0c176c00359b]]
|
||||
The Eiffel Codedom Provider Manager allows graphical configuration of the Eiffel CodeDom Provider. There can be multiple configurations, each of them being associated with at least one application.
|
||||
|
||||
{{note|The Eiffel for ASP.NET installation program will add a shortcut in the start menu to the Eiffel CodeDom Provider Manager. }}
|
||||
|
||||
The main dialog of the Eiffel CodeDom Provider Manager is divided into two vertical panes. The left pane contains a list of available configurations while the right pane contains the settings corresponding to the currently selected configuration. Initially the only available configuration is the '''default''' configuration which applies to all applications. The configurable settings are listed below.
|
||||
==General Settings==
|
||||
* '''Fail on error''': Checking this box will force the Eiffel Codedom Provider to fail when an error occurs. By default the provider rescues the failure and logs an error but it might be easier for debugging to have the Eiffel CodeDom Provider stop and raise an exception.
|
||||
* '''Log server''': Server where events should be logged. By default events are logged on the machine running the CodeDom Provider, the corresponding value is a dot ("."). Consult the [[Logging|Logging]] section for additional information on logging.
|
||||
* '''Log level''': By default only errors are written to the log. It might be useful for debugging purposes to log additional information and/or warnings. This setting also allows to disable logging in case it is not required. Consult the [[Logging|Logging]] section for additional information on logging.
|
||||
|
||||
==Compiler Settings==
|
||||
* '''Default root class''': In the event where the CodeDom tree does not define an entry point, there still needs to be a root class for the Eiffel system to compile properly. If there is an entry point defined in the Codedom tree then this setting is ignored.
|
||||
* '''Precompile ace file''': This setting, if set, gives the path to the ACE file of the precompile that the CodeDom compiler should use when compiling systems with this configuration. The CodeDom compiler will first check whether the precompile for that ACE file already exists in the precompile cache (See Precompile cache below). If it finds one then it will check whether the ACE file was modified since the precompile was created. If there isn't a corresponding precompile or if the ACE file was modified then the CodeDom compiler will create a new precompile in the precompile cache using the specified ace file. It will then compile the system using the precompile in the cache corresponding to the specified ACE file.
|
||||
|
||||
{{note|If for any reason the precompilation fails then the CodeDom compiler will still compile the system but without using any precompile. }}
|
||||
|
||||
* '''Metadata cache''': Both the Eiffel CodeDom Provider and the Eiffel compiler require reading from and writing to an Eiffel Metadata Cache. These caches contain information about mapping the .NET types and members names to valid Eiffel identifiers. Because read and write access are required, the application that uses the CodeDom Provider must have write access rights to the Eiffel Metadata Cache folder.
|
||||
{{note|The Eiffel for ASP.NET installation program will grant write access rights to the Eiffel Metadata Cache folder to the ASPNET user and IIS_WPG group if they exist. ASPNET is the default user account used by the ASP.NET process (aspnet_wp.exe) under Windows XP while IIS_WPG is the default user account used by the ASP.NET process (w3wp.exe) under Windows 2003 Server. }}
|
||||
|
||||
{{warning|Changing the Eiffel Metadata Cache folder path will force the Eiffel CodeDom Provider to regenerate the cache content during the next code generation or compilation. This process can take a long time and in particular can take more time than the default timeout for an ASP.NET page. If the Eiffel CodeDom Provider is used together with ASP.NET and if the Eiffel Metadata Cache path has been modified, it is recommended to change the default timeout for the first ASP.NET page containing Eiffel code to be loaded (this can be done by setting the ''HttpServerUtility.ScriptTimeout'' property). }}
|
||||
|
||||
* '''Compiler metadata cache''': This cache is used by the Eiffel Codedom Provider Compiler to map .NET types and members names to valid Eiffel identifiers. Please consult '''Metadata cache''' for additional information (the same note and warning apply).
|
||||
* '''Precompile cache''': If the configuration defines a precompile ace file then the Eiffel CodeDom Provider will create the precompile in the directory specified in this setting. Changing this value will force the Eiffel CodeDom Provider compiler to recreate the precompile in the new directory.
|
||||
|
||||
==Assembly Prefixes==
|
||||
This list associates .NET assemblies with the prefix that will be used for Eiffel class names corresponding to .NET types belonging to the assembly. For example the prefix for the assembly ''System.Xml.dll'' is ''XML_'', this means that the Eiffel class names of all the types in the assembly ''System.Xml.dll'' will all begin with ''XML_''. This is necessary because Eiffel doesn't have a notion of namespace. The default assembly prefixes cannot be modified but new assembly/prefix pairs can be added if necessary.
|
||||
==Applications==
|
||||
This last setting will only appear for configurations other than the default configuration. It lists the applications that will use the configuration when they load the Eiffel CodeDom Provider assembly. If an application is not listed in any configuration then it will use the default configuration.
|
||||
|
||||
{{note|For a change in an existing configuration to take effect, the process that uses the modified configuration must be restarted. }}
|
||||
|
||||
==New Configuration==
|
||||
New configurations may be created by clicking the ''New'' button or the ''New'' entry in the ''File'' menu. The New Configuration dialog box asks for the name and the path of the configuration. It will create a file with the extension '''.ecd''' ('''E'''iffel '''C'''ode'''D'''om) with the specified name in the directory located at the specified path. Whenever one of the applications listed at the bottom of the New Configuration dialog loads the Eiffel CodeDom Provider, it will use this new configuration.
|
||||
==Configuration Properties==
|
||||
Double clicking on the configuration name in the configurations list, clicking the ''Properties'' button or choosing the ''Properties'' menu entry in the ''File'' menu will open the Configuration Properties dialog. This dialog gives the dates of creation and last modification of the configuration as well as the list of applications that use it if it's not the default configuration.
|
||||
|
||||
|
||||
{{seealso|[[Configuration|Configuration]], [[Logging|Logging]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
[[Property:title|eSplitter]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|2cf7249c-fd1e-b8e0-08ab-c2995b2b8227]]
|
||||
This tool comes in two flavors: a command line only utility ''esplit.exe'' and a graphical tool ''esplitter.exe''. Both applications achieve the same goal: to produce standard Eiffel source files ('''.e''' files) from Eiffel CodeDom Provider generated source files ('''.es''' files).
|
||||
==Command Line Utility (esplit.exe)==
|
||||
This utility is located in the ''Codedom\bin'' subfolder of the Eiffel for ASP.NET directory. The command line syntax is:
|
||||
<code>
|
||||
Usage: esplit -f=FOLDER [-s -d=DESTINATION -v -h -m=REGEXP -n]
|
||||
-d, --destination=DESTINATION
|
||||
Folder where generated Eiffel class files should be created.
|
||||
If not specified, creates Eiffel class files in same folder as
|
||||
corresponding Eiffel multi-class file.
|
||||
-f, --folder=FOLDER
|
||||
Folder containing Eiffel multi-class files.
|
||||
-h, --help
|
||||
Help on using this program.
|
||||
-m, --match=REGEXP
|
||||
Regular expression that file name must match to be processed, by default matches all files with extension '.es'.
|
||||
-n, --nologo
|
||||
Do not display copyright notice.
|
||||
-s, --subfolders
|
||||
Also process files in subfolders.
|
||||
-v, --version
|
||||
Version information.</code>
|
||||
So running:
|
||||
<code>
|
||||
esplit -f .</code>
|
||||
will generate Eiffel source files from all the files with extension '.es' in the current folder and running:
|
||||
<code>
|
||||
esplit -f "source" -m "C.*\.es" -d "destination" -s</code>
|
||||
will generate Eiffel source files from all the files with extension '.es' and whose filename starts with a 'C' located in the 'source' folder and all its subfolders. The Eiffel source files will be generated in the 'destination' folder.
|
||||
==Graphical Utility (esplitter.exe)==
|
||||
The graphical utility includes two panes. The first pane allows setting different parameters:
|
||||
* '''Folder containing Eiffel multi-class file(s)''': this field gives the path to the folder which contains the files to be processed. The button to the right of the field allows to browse for the folder. This field corresponds to the '-f' parameter of the command line utility.
|
||||
* '''Only process files whose filename matches the following regular expression''': this field corresponds to the '-m' parameter of the command line utility.
|
||||
* '''Also process files in subfolder''': if this check box is checked then the utility will search for files whose filenames match the regular expression in the destination folder as well as in its subfolders. This check box corresponds to the '-s' switch of the command line utility.
|
||||
* '''Generate Eiffel class files in the same folder as the Eiffel multi-class files''': checking this radio button is equivalent to not providing a '-d' parameter to the command line utility.
|
||||
* '''Generate Eiffel class files in the following folder''': The content of the field below this radio button corresponds to the '-d' parameter of the command line utility.
|
||||
Clicking on the '''Generate''' button switches to the output pane and starts the Eiffel class file generation. The output pane contains the text output resulting from the execution of the tool. It also contains two buttons:
|
||||
* '''Open Folder''': clicking on this button will open Windows Explorer in the folder where the Eiffel source files were generated.
|
||||
* '''Exit''': clicking this button will close the application.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
[[Property:title|Eiffel for ASP.NET Tools and Administration]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|960d3d4b-33d0-327f-343d-ad9d5591f07a]]
|
||||
The Eiffel for ASP.NET delivery comes together with several tools:
|
||||
* [[Eiffel CodeDom Provider Manager|Eiffel CodeDom Provider Manager]] allows administering the Eiffel CodeDom Provider.
|
||||
* [[eSplitter|ESplitter]] allows generating standard Eiffel source files ('''.e''' files) from Eiffel CodeDom Provider generated files ('''.es''') files.
|
||||
* [[Name Mapper|Name Mapper]] allows retrieving the Eiffel identifier corresponding to a .NET type or type member.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[[Property:title|Name Mapper]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|82691c82-50a5-ed7f-b138-90946d094dae]]
|
||||
The Name Mapper utility allows retrieving the Eiffel identifier corresponding to a .NET type name or a .NET member name. There are only two input fields in the interface: the .NET type name should be entered in the first one while the .NET member name should be entered in the second one. Once a valid .NET type name is entered in the type name input field, the tool starts searching for the corresponding Eiffel identifier. When the Name Mapper finds the corresponding Eiffel identifier, it automatically fills the .NET member name input combo box with all the member names of the corresponding type. This means that the .NET member type name can be chosen from the combo box entries or typed in (if it's typed in then the tool will autocomplete the member name).
|
||||
The assemblies the tool will look into for the .NET type whose name was given in the .NET type name input field are listed below the input fields. It is possible to add new assemblies to the list by clicking the ''Add'' button. Assemblies that were added this way can be removed by clicking the ''Remove'' button after selecting the assembly from the list.
|
||||
{{note|Assemblies that are listed by default cannot be removed. }}
|
||||
|
||||
{{note|When adding an assembly and if the assembly has not been consumed (i.e. the XML names mapping files have not been generated in the Eiffel Metadata Cache) then the Name Mapper will consume the assembly, this may take a while. }}
|
||||
|
||||
|
||||
{{seealso|[[Names Mappings|Names Mapping]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
[[Property:title|About the Code Document Object Model (CodeDom)]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|46136373-34bb-94b7-1f7b-934d584cf637]]
|
||||
CodeDom stands for the '''Code Document Object Model'''. CodeDom allows for representing source code in an abstract data structure. Such a representation is called a '''CodeDom tree''' as the underlying data structure uses a tree paradigm. This abstract representation can then be generated into different programming languages. Each language vendor provides its implementation of the CodeDom interfaces, this implementation is called a '''CodeDom Provider'''. Programmers can then build tools that can work with many different languages without having to know about each one, instead they just delegate code generation to each CodeDom Provider.
|
||||
The CodeDom interfaces also expose types and methods that allow for parsing source code and creating the corresponding CodeDom. The current implementation of the Eiffel CodeDom Provider does not support parsing. Code generation is a much more popular useage of the CodeDom and the current version focuses on this aspect of the technology.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
[[Property:title|Common Scenarios]]
|
||||
[[Property:weight|7]]
|
||||
[[Property:uuid|15334d95-2ef5-6e1b-1754-051342bb7290]]
|
||||
==ASP.NET==
|
||||
Probably the most common use of the CodeDom technology is via Internet Information Services (IIS) and ASP.NET. The installation of the Eiffel for ASP.NET will configure the machine so ASP.NET pages can be written in Eiffel. Consult the [[Eiffel for ASP.NET Installation|Eiffel for ASP.NET documentation]] for details on how to write ASP.NET Eiffel pages.
|
||||
==WSDL==
|
||||
Another use of the CodeDom technology is via the WSDL utility. This utility is part of the .NET Framework SDK and is a common line application that can be used to automatically generate stub source code for any Web Service described by a Web Service Description Language (WSDL) file. It is possible to tell the utility which CodeDom Provider to use via the command line as follows:
|
||||
<code>
|
||||
Wsdl /language:"EiffelSoftware.CodeDom.CodeDomProvider, EiffelSoftware.CodeDom, Version=2.0.1.1402, Culture=neutral, PublicKeyToken=def26f296efef469"<br/>
|
||||
http://api.google.com/GoogleSearch.wsdl</code>
|
||||
|
||||
{{note|The version number used above might differ depending on the version of the Eiffel CodeDom Provider dll installed on your system. }}
|
||||
|
||||
{{note|The example above uses the Google web service, the corresponding URL is correct at the time of writing but there is no guarentee it won't change (or be removed) in the future. }}
|
||||
|
||||
This will generate a file with the extension '''.es''' which contains multiple Eiffel class definitions. Compiling this file will require using the CodeDom compiler (programmatically via the CodeDom <code>ICodeCompiler</code> interface) or using the [[eSplitter|eSplitter]] tool to generate standard Eiffel source files ('''.e''' files) that can then be compiled by the standard Eiffel compiler.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
[[Property:title|Configuration]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:uuid|2d885f05-adcc-d8da-6515-4568fc76bcf6]]
|
||||
The Eiffel Codedom Provider uses XML configuration files that define additional settings specific to Eiffel. Such settings include whether to fail on error, what events to log, where to log them etc... Each application can define its own configuration to be used when it loads the Eiffel CodeDom Provider. If an application is not associated with a configuration file then a default configuration is used.
|
||||
The association between applications and configurations as well as the actual settings the configuration defines can be set in the [[Eiffel CodeDom Provider Manager|Eiffel CodeDom Provider Manager]] . Run ''ecdpman.exe'' to start the manager.
|
||||
|
||||
{{note|The Eiffel for ASP.NET installation program will add a shortcut in the start menu to the Eiffel CodeDom Provider Manager. }}
|
||||
|
||||
{{seealso|[[Eiffel CodeDom Provider Manager|Eiffel CodeDom Provider Manager]] , [[Logging|Logging]] , [[Required Permissions|Required permissions]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
[[Property:title|Eiffel Implementation]]
|
||||
[[Property:weight|7]]
|
||||
[[Property:uuid|3f945c36-521e-cd71-fc7b-d549a6c05a04]]
|
||||
There are a few topics specific to the implementation of the Eiffel CodeDom Provider that are worth mentioning.
|
||||
==Multiple Classes File==
|
||||
The Eiffel compiler expects that there is only one class per source file (file with extension '''.e'''), however the CodeDom interface specification requires multiple classes to be generated in the same file (actually in the same ''stream''). The Eiffel CodeDom Provider code generator generates files with the extension '''.es''' which may contain multiple class definitions. Each class definition is separated with a special marker that the Eiffel CodeDom Provider compiler will parse to create multiple '''.e''' files - each containing a single class definition - prior to calling the command line Eiffel compiler.
|
||||
|
||||
|
||||
{{note|Only the Eiffel CodeDom Provider compiler knows how to handle '''.es''' files. The standard Eiffel compilers can only parse '''.e''' files. }}
|
||||
|
||||
Eiffel for ASP.NET includes the [[eSplitter|eSplitter]] utility which can 'split' Eiffel multi-class files ('''.es''' files) into single class files ('''.e''' files).
|
||||
==Inheritance Snippet==
|
||||
Any type defined in a CodeDom tree to be consumed by the Eiffel CodeDom Provider may include one (and only one) snippet member that includes an inheritance clause declaration. This ''inheritance snippet'' must start with the keyword '''inherit''' and follows the Eiffel syntax for inheritance clauses. All the feature adaptations available in the Eiffel programming language may be used in such a snippet ('''rename''', '''export''', '''undefine''', '''redefine''', '''select''').
|
||||
|
||||
|
||||
{{note|The CodeDom tree itself might define additional inheritance clauses in which case the Eiffel CodeDom Provider will merge the snippet and the implicit clauses in the generated source file. }}
|
||||
|
||||
==Indexing Snippet and Precompiled Libraries==
|
||||
Another interesting snippet member is the ''indexing clause snippet'' which must start with the '''indexing''' keyword and follows the Eiffel syntax for indexing clauses. This snippet may include the indexing clause '''precompile_definition_file''' which can be used to specify the path to the precompiled library ACE file to be used by the Eiffel CodeDom Compiler. This is specially useful in ASP.NET pages where each page can specify which precompiled ACE file to use when compiling it. This path will be used instead of the path defined in the [[Configuration|configuration file]].
|
||||
|
||||
|
||||
{{note|Only one class per compile unit should define a path to the precompiled library ACE file. If more than one class contain a '''precompile_definition_file''' indexing clause then there is no guarentee on which one the Eiffel CodeDom Compiler will use. }}
|
||||
|
||||
==Eiffel Configuration==
|
||||
There are several Eiffel specific settings that can be set in the Eiffel CodeDom Provider which will affect the generated code as well as the CodeDom compiler behavior. These settings are covered in the [[Configuration|Configuration]] section.
|
||||
|
||||
|
||||
{{seealso|[[eSplitter|eSplitter]], [[Configuration|Configuration]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
[[Property:title|The Eiffel CodeDom Provider]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|0b45f31f-83ea-6301-9017-7b78c6827772]]
|
||||
The Eiffel CodeDom Provider provides an implementation of the CodeDom Provider generation and compilation interfaces defined in the System.CodeDom namespace of the .NET Framework. This documentation includes the following sections:
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
[[Property:title|Installation]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|b6197932-4bcb-c79b-afe2-1a8f36dbbd7c]]
|
||||
For certain tools (including ASP.NET) to detect the availability of a CodeDom Provider, the corresponding assembly name must be listed in the .NET machine wide configuration file typically located in:
|
||||
<code>
|
||||
%SYSTEM_DRIVE%\Windows\Microsoft.NET\Framework\vx.x.xxxx\CONFIG\machine.config</code>
|
||||
(where vx.x.xxxx corresponds to the .NET Framework version number). The available CodeDom Providers are listed under the ''<compilers>'' XML node. The Eiffel CodeDom Provider implements a.NET Installer class so that should you need to add the Eiffel CodeDom Provider to the machine configuration file, simply run the .NET SDK command line utility ''InstallUtil'' as follows:
|
||||
<code>
|
||||
InstallUtil EiffelSoftware.Codedom.dll</code>
|
||||
Conversely, should the Eiffel Codedom Provider not be required anymore, running the following command line will remove it from the ''machine.config'' file:
|
||||
<code>
|
||||
InstallUtil /u EiffelSoftware.Codedom.dll</code>
|
||||
|
||||
{{note|The '''Eiffel for ASP.NET''' installation program will take care of registering and unregistering the Eiffel CodeDom Provider properly. }}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
[[Property:title|Limitations]]
|
||||
[[Property:weight|8]]
|
||||
[[Property:uuid|6d8ab90d-f051-5010-55df-218a020d9cb5]]
|
||||
==Generation Only==
|
||||
The current implementation of the Eiffel CodeDom Provider does not support parsing Eiffel source code to produce a CodeDom tree. Only code generation and compilation are supported in the current release.
|
||||
==Managed Code Only==
|
||||
Only managed code can be generated or compiled by the Eiffel CodeDom Provider. This means that the CodeDom tree being compiled cannot include snippet members which use or define Eiffel external clauses. If the system requires access to external methods, they should be defined in a precompiled library (see [[Configuration|Configuration]] ).
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
[[Property:title|Logging]]
|
||||
[[Property:weight|4]]
|
||||
[[Property:uuid|d425684b-687c-aee6-25fa-650e5885e014]]
|
||||
The Eiffel CodeDom Provider may be configured (via the [[Eiffel CodeDom Provider Manager|Eiffel Codedom Provider Manager]] ) to log information, warnings and/or errors in the Windows Event Log. Logging may be useful for a number of reasons, some of them are:
|
||||
* To detect which CodeDom interface method is called and when it's called. This is particularly useful when the source code for the CodeDom consumer (e.g. ASP.NET) is not available.
|
||||
* To locate the source of a problem when the code generated by the Eiffel CodeDom Provider is incomplete or incorrect. This may happen if the CodeDom tree given to the Eiffel CodeDom Provider uses one of the unsupported constructs as listed in the topic [[Supported Constructs|Supported Constructs]] .
|
||||
* To detect a problem in a CodeDom tree. The logs will help in locating the location of the problem as they include contextual information on the node where the error occurred.
|
||||
|
||||
==Log Details==
|
||||
There are three types of events that can be logged: information events, warnings and errors:
|
||||
* Information events include calls to CodeDom interface methods and other events that will not affect the execution of the Eiffel CodeDom Provider
|
||||
* Warnings include events that will not prevent the CodeDom Provider from running but that may affect the results. For example if a CodeDom tree element references a .NET entity that the Eiffel CodeDom Provider cannot find then it will generate a default Eiffel name by running the default formatting algorithm. This may produce code that does not compile if the Eiffel name of the entity does not correspond to the formatted .NET name (for example if the entity is a type and the type's assembly has a non-empty prefix).
|
||||
* Errors include events that will force the execution of the Eiffel CodeDom Provider to stop. By default the Eiffel CodeDom Provider simply stops and returns an error, it is possible however to configure it so that an exception is raised.
|
||||
|
||||
==Log Location==
|
||||
All the Eiffel CodeDom Provider events are logged into the Windows '''System''' Log. This log can be viewed via the Windows administrative tool ''Event Viewer''. The source for the events raised by the Eiffel CodeDom Provider is ''Eiffel CodeDom Provider''. By default all the events are logged on the machine running the Eiffel CodeDom Provider but it is possible to change its configuration so that the events are logged on a different machine (see [[Required Permissions|Required permissions]] ).
|
||||
{{seealso|[[Eiffel CodeDom Provider Manager|Eiffel CodeDom Provider Manager]] , [[Supported Constructs|Supported constructs]] , [[Required Permissions|Required permissions]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
[[Property:title|Required Permissions]]
|
||||
[[Property:weight|6]]
|
||||
[[Property:uuid|f55af6a0-b896-12b7-fe4a-ebb1787731e5]]
|
||||
The account running the Eiffel CodeDom Provider might have restricted permissions such as when used through ASP.NET. This section aims at defining the security permissions required for the execution of the Eiffel CodeDom Provider.
|
||||
==Eiffel Metadata Cache==
|
||||
Both when generating and compiling source code, the Eiffel CodeDom Provider needs to read and write from and to the Eiffel Metadata Cache. This repository stores the mapping information between .NET and Eiffel names for both types and their members. By default the Eiffel Metadata Cache is located in the ''Assemblies'' subfolder of the Eiffel CodeDom Provider directory.
|
||||
{{note|The Eiffel for ASP.NET installation program will grant read and write access to the Eiffel Metadata Cache folder for the ''ASPNET'' (default ASP.NET worker process account on Windows XP Professional) and the ''IIS_WPG'' (default ASP.NET worker process account on Windows Server 2003) accounts. }}
|
||||
The path to the Eiffel Metadata Cache folder might be changed via the [[Eiffel CodeDom Provider Manager|Eiffel CodeDom Provider Manager]] , make sure the new folder can be read from and written to by the account running the process that uses the Eiffel CodeDom Provider.
|
||||
==Precompile Cache==
|
||||
The same requirements apply to the Precompile Cache folder. By default the Precompile Cache folder is located in the ''Precompile'' subfolder of the Eiffel CodeDom directory.
|
||||
{{note|The Eiffel for ASP.NET installation program will grant read and write access to the Precompile Cache folder for the ''ASPNET'' (default ASP.NET worker process account on Windows XP Professional) and the ''IIS_WPG'' (default ASP.NET worker process account on Windows Server 2003) accounts. }}
|
||||
As with the Eiffel Metadata Cache folder, it is possible to change the location of the Precompile Cache folder but the process running the Eiffel CodeDom Provider must always have read and write access to the folder.
|
||||
==Logging==
|
||||
Logging requires registry read access as well as event log write access. By default, the ASP.NET worker process does not have these permissions. The easiest way to grant the required permissions to the ASP.NET worker process is by using ASP.NET impersonation. This mechanism allows the ASP.NET worker process to run under a different account. This is done by providing a ''web.config'' file in the root folder of the ASP.NET application. The content of the configuration file needs to include the following declaration:
|
||||
<code>
|
||||
<configuration>
|
||||
<system.web>
|
||||
<identity impersonate="true" userName="Username" password="Password"/>
|
||||
</system.web>
|
||||
</configuration></code>
|
||||
where <code>Username</code> is the name of an account which has all the required permissions and <code>Password</code> the corresponding password. Refer to the .NET Framework SDK documentation for additional information on ASP.NET impersonation.
|
||||
The solution described above might not be acceptable on a production web server. However, it is possible to configure the Eiffel CodeDom Provider to log events on a different machine (see [[Eiffel CodeDom Provider Manager|Eiffel CodeDom Provider Manager]] ). The log machine should grant the appropriate permissions to the impersonated account for logging and could only be accessible through the web server. This setting would avoid having to grant additional permissions to the ASP.NET worker process account on the web server while still enabling logging.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
[[Property:title|Supported Constructs]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|99e15890-75a9-8b76-d0b6-b9be419be5ce]]
|
||||
Since not every language might support all the constructs CodeDom can represent, there needs to be a way for the CodeDom Provider to specify which constructs are supported and which ones are not. This is done through the <code>System.Codedom.Compiler.ICodeGenerator</code> interface using method <code>Supports</code>.<br/>
|
||||
This method takes a flag corresponding to the CodeDom contruct the caller is interested in and returns a boolean value indicating whether or not the construct is supported. In the Eiffel CodeDom Provider implementation there is a third category of constructs which will be reported as supported since the provider will generate valid Eiffel code for them but that should be distinguished from other "truly" supported constructs as the generated code will not correspond to the .NET equivalent of the construct.<br/>
|
||||
Let's take an example: Enum declarations are supported by the Eiffel CodeDom Provider because the generated Eiffel code will correctly make use of the values defined in the enum. However there is no such thing as an enum type in Eiffel so there won't be a corresponding .NET enum type in the generated assembly. Such constructs are said to be "non-roundtripable" because even if the Eiffel CodeDom Provider supported parsing source code, the corresponding CodeDom tree would not include the constructs like the initial CodeDom did.
|
||||
The lists of supported, supported without roundtrip and unsupported constructs for the Eiffel CodeDom Provider are as follows:
|
||||
|
||||
'''Supported constructs''':
|
||||
* Arrays of arrays
|
||||
* Assembly attributes
|
||||
* Complex expressions
|
||||
* Declare value types
|
||||
* Entry point method
|
||||
* Multiple interface members
|
||||
'''Supported constructs (no roundtrip)''':
|
||||
* Declare enums
|
||||
* Declare events
|
||||
* Declare interfaces
|
||||
* Nested types
|
||||
* Public static members
|
||||
* Reference parameters
|
||||
* Try catch statements
|
||||
'''Unsupported constructs''':
|
||||
* Chained constructor arguments
|
||||
* Declare delegates
|
||||
* Goto statements
|
||||
* Multidimensional arrays
|
||||
* Parameter attributes
|
||||
* Return type attributes
|
||||
* Static constructors
|
||||
* Win32 resources
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
[[Property:title|Eiffel for ASP.NET Documentation]]
|
||||
[[Property:weight|6]]
|
||||
[[Property:uuid|158e298e-e4b3-3246-651c-557f2f630957]]
|
||||
CodeDom is a Microsoft .NET technology which allows representing source code programatically so that it may be rendered in different languages. The Eiffel CodeDom Provider is installed through Eiffel for ASP.NET which is available for download for free on the Eiffel Software web site ( [http://www.eiffel.com http://www.eiffel.com] ). <br/>
|
||||
The following documents cover different aspects of the Eiffel CodeDom Provider:
|
||||
* [[Writing ASP.NET pages in Eiffel|Writing ASP.NET pages in Eiffel]] covers topics related to writing Eiffel pages in ASP.NET.
|
||||
* The [[The Eiffel CodeDom Provider|Eiffel CodeDom Provider]] documentation focuses on the Eiffel CodeDom Provider per se. Read this documentation if you intend on using the Eiffel CodeDom Provider via a different mean than ASP.NET (e.g. to generate Web Services stubs using Microsoft's WSDL utility).
|
||||
* Finally, [[Eiffel for ASP.NET Tools and Administration|Eiffel for ASP.NET Tools and Administration]] covers the Eiffel CodeDom Provider Manager, the eSplitter and the Name Mapper tools that are included in the Eiffel for ASP.NET delivery.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[[Property:title|CodeDom: Getting Started]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|06e0c7ae-6d25-a0ef-81e6-2ee3c95fe4a1]]
|
||||
== Prerequisites ==
|
||||
Before you start writing ASP.NET pages in Eiffel, you should be familiar with the ASP.NET technology itself. If you haven't already, it is strongly recommended that you follow the ASP.NET quickstarts available online at [http://samples.gotdotnet.com/quickstart/aspplus/ http://samples.gotdotnet.com/quickstart/aspplus/] . Apart from a few Eiffel specific points described in [[Using Eiffel (to write ASP.NET pages)|Using Eiffel]], writing ASP.NET pages in Eiffel follows the same rules as writing ASP.NET pages in any other language.
|
||||
Writing ASP.NET pages in Eiffel also requires to be familiar with the Eiffel programming language itself. There are no changes in the language when using it to write ASP.NET pages: the same parser and compiler are used to parse and compile standard Eiffel source code and ASP.NET generated source code. Documentation on the Eiffel programming language can be found on the [http://docs.eiffel.com Eiffel documentation web site] .
|
||||
|
||||
==Samples==
|
||||
The '''Samples''' folder of the Eiffel for ASP.NET delivery includes ASP.NET pages written in Eiffel. To run the samples you can either click on the corresponding link in the ''Start'' menu or open the page [http://localhost/EiffelSample http://localhost/EiffelSample] in your internet browser. Each sample comes with an extensively commented source code.
|
||||
{{note|The samples will only be available if IIS was installed prior to installing Eiffel for ASP.NET. }}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[[Property:title|Eiffel for ASP.NET Installation]]
|
||||
[[Property:weight|-1]]
|
||||
[[Property:uuid|c9371a07-12fb-bdb2-b076-3da3c0ec8da6]]
|
||||
Eiffel for ASP.NET is available for downloading for free at [http://www.eiffel.com http://www.eiffel.com] . Simply run the file and follow the installer instructions. It is recommended that Internet Information Services (IIS) be deployed on the machine prior to installing Eiffel for ASP.NET. If IIS is not deployed then the installation program will display a warning message box. Click '''ignore''' to keep installing the product.
|
||||
Without IIS, ASP.NET pages cannot be loaded and thus the samples will not be installed. It might still be useful to install Eiffel for ASP.NET without IIS if the Eiffel CodeDom Provider is going to be used through other [[Common Scenarios|scenarios]]. If IIS is available then the installer will open an Eiffel ASP.NET page at the end of the installation process. This page contains links to the documentation and samples. The installer will also put links to these documents in the ''Start'' menu under 'Programs\Eiffel for ASP.NET x.x' where 'x.x' corresponds to the version number of Eiffel for ASP.NET.
|
||||
==System Requirements==
|
||||
* Windows 2000 Server or Advanced Server with Service Pack 2, Windows XP Professional or 64-Bit Edition, or one of the Windows Server 2003 family products.
|
||||
* .NET Framework (1.0 or 1.1)
|
||||
* Internet Information Services with ASP.NET (optional see above)
|
||||
|
||||
==ASP.NET Installation==
|
||||
Please consult the corresponding documentation (available online at [http://msdn.microsoft.com/asp.net/ http://msdn.microsoft.com/asp.net/] ) for configuring ASP.NET properly. In particular, be aware that by default ASP.NET is disabled on Windows 2003 Server.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
[[Property:title|Writing ASP.NET pages in Eiffel]]
|
||||
[[Property:weight|-1]]
|
||||
[[Property:uuid|7027e7f3-38e1-5e26-c936-20aea0e0c205]]
|
||||
Eiffel for ASP.NET allows running ASP.NET pages written in Eiffel. It should be installed on both the web server and the developer's machine. This documentation includes the following sections:
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
[[Property:title|Names Mappings]]
|
||||
[[Property:weight|4]]
|
||||
[[Property:uuid|fa212869-ffb0-0a6a-cbd0-7a184b11491d]]
|
||||
Maybe the most striking difference when writing pages in Eiffel comes from the name of the .NET types and methods. Because of different naming conventions, overloading and differences in identifier validity rules, .NET identifiers cannot be mapped directly to Eiffel identifiers. As a consequence, the Eiffel compiler creates XML mapping files in the ''Eiffel Metadata Cache''. These files are created only once for each referenced assembly.
|
||||
{{note|The ASP.NET installation program pre-generates the Eiffel Metadata Cache for all assemblies required by ASP.NET by default. }}
|
||||
The name mapping algorithm is quite complex but it is possible to guess the Eiffel identifier from the .NET identifier in most cases. The following simple steps can be followed to retrieve the Eiffel identifier corresponding to a .NET type:
|
||||
# Only the simple type name is used, the namespace isn't used by the Eiffel identifier:
|
||||
<code>System.Xml.NameTable -> NameTable</code>
|
||||
|
||||
# Underscores are introduced in between words (prior to upper case letters other than the first character):
|
||||
<code>NameTable -> Name_Table</code>
|
||||
|
||||
# Eiffel class names are always upper case:
|
||||
<code>Name_Table -> NAME_TABLE</code>
|
||||
|
||||
# If the assembly containing the type is associated with a prefix in the Eiffel system then the prefix gets prepended to the name:
|
||||
<code>NAME_TABLE -> XML_NAME_TABLE</code>
|
||||
|
||||
The steps to follow to retrieve the Eiffel identifier corresponding to a .NET member are simpler:
|
||||
# For property getters, the "get_" prefix is removed:
|
||||
<code>get_MaxOccursString -> MaxOccursString</code>
|
||||
|
||||
# Underscores are introduced in between words (prior to upper case letters other than the first character):
|
||||
<code>MaxOccursString -> Max_Occurs_String</code>
|
||||
|
||||
# Eiffel feature names are always lower case:
|
||||
<code>Max_Occurs_String -> max_occurs_string</code>
|
||||
|
||||
Although these simplified steps will work in most cases there are certain .NET identifiers that require additional manipulation to be translated into Eiffel. The [[Name Mapper|Name Mapper]] utility should be used when such cases arise.
|
||||
{{seealso|[[Name Mapper|Name Mapper]] }}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
[[Property:title|Using Eiffel (to write ASP.NET pages)]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:uuid|c8e2ffa7-cfa8-0b4c-4b5e-e1f386b7ab11]]
|
||||
==Inheritance==
|
||||
Eiffel handles inheritance in a different way than most languages. In C# or VB.NET the behavior of a newly introduced member in a class hierarchy is defined as part of the member declaration itself (for example C# will precede the member declaration with the keyword ''override'' to accomplish the same result as an Eiffel ''redefine'' clause). In Eiffel, classes are equipped with an inheritance clause which centralizes all the feature adaptations.
|
||||
As a result, Eiffel for ASP.NET introduces '''inheritance snippets'''. Each Eiffel ASP.NET page may include at most one inheritance snippet. Such a snippet must start with the '''inherit''' keyword and may then list any valid Eiffel feature adaptation.
|
||||
|
||||
==String and Array Manifest Constants==
|
||||
The Eiffel programming language supports using '''manifest constants''' in source code. The type of these constants is inferred by the Eiffel compiler. There are two manifest constant types that require special attention on .NET: string and array. Because the Eiffel and the .NET types differ, writing code using string or array manifest constants requires additional care.
|
||||
* '''Strings''': Eiffel strings cannot be mapped directly to .NET strings because Eiffel strings are ''mutable'': an Eiffel string can be resized while the size of a .NET string is set for the lifetime of the object. However, the Eiffel class <eiffel>STRING</eiffel> defines conversion features that will automatically convert to and from a .NET string. So when the compiler expects an object of type ''System.String'' where an object of type <eiffel>STRING</eiffel> is used, it will automatically call the right conversion routine.<br/>
|
||||
However if an instance of <eiffel>STRING</eiffel> is passed as an argument to an overloaded .NET function that can accept both an instance of ''System.Object'' or an instance of ''System.String'' then the compiler will produce an error since it has no means to know which type the Eiffel object should be converted to (any Eiffel object converts to ''System.Object''). When this happens, the conversion function <eiffel>to_cil</eiffel> must be explicitely called.
|
||||
* '''Arrays''': The exact same problem also arises with arrrays. Instances of the Eiffel class <eiffel>ARRAY</eiffel> are different from instances of <eiffel>NATIVE_ARRAY</eiffel> which correspond to the .NET array type. There are however features in <eiffel>ARRAY</eiffel> that will convert to and from .NET arrays. If an overloaded .NET function can take an argument of type ''System.Object'' or ''System.Array'', any instance of <eiffel>ARRAY</eiffel> given to it must first be converted explicitly to a .NET array by calling the <eiffel>to_cil</eiffel> function.
|
||||
|
||||
==Compilation and Timeouts==
|
||||
Eiffel compilations can take longer than the default timeouts set in ASP.NET. In particular ASP.NET will run batch compilations on the entire directory that is being accessed by IIS each time the time stamp of that directory changes. If the batch compilation takes longer than the ''batchTimeout'' property defined in the machine wide configuration file (''machine.config'') then ASP.NET will start another compilation just for the requested file. This can be quite resource intensive for the Web server and thus it might be beneficial to increase the default value of 15 seconds for ''batchTimeout''.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
[[Property:title|Constructors and Creation Procedures]]
|
||||
[[Property:weight|4]]
|
||||
[[Property:uuid|f06eb414-c284-902b-0481-3f00da82d47e]]
|
||||
This section deals with what happens when objects, that is runtime instances of types, get created and initialized. When a new instance is created, there is an opportunity to initialize the state of the instance. This is done with a constructor in .NET, and with a creation procedure in Eiffel.
|
||||
|
||||
==Eiffel Creation Procedures==
|
||||
|
||||
Eiffel creation procedures are features of a class which can be used to initialize instances. Classes can have more than one creation procedure available. However, each creation procedure must ensure that the class invariant holds when the procedure completes execution. In other words, the creation procedure is there to initialize a newly created instance, and the class invariant guarantees that a newly initialized instance is actually valid.
|
||||
|
||||
There is nothing special about creation procedures themselves, they are just ordinary procedures (although by convention their names usually begin with the word "<code>make</code>"). What makes them creation procedures is the fact that their names are listed as creation procedures in the class text.
|
||||
|
||||
In Eiffel, a creation procedure can be applied to an instance at any time (not just at object creation). This is done sometimes to reinitialize existing instances.
|
||||
|
||||
==Constructors in .NET==
|
||||
|
||||
Like creation procedures in Eiffel, .NET constructors are used to initialize new instances of types. Constructors manifest themselves differently depending upon which .NET language you use. In C#, constructors always appear as a method having the same name as the class on which they are implemented. In Visual Basic .NET, they always appear as a Sub with the name <code>New</code>. Once compiled into an assembly, the metadata labels constructors as <code>. ctor</code>.
|
||||
|
||||
Constructors can have multiple versions by overloading. That is, each version would have a different set of argument types.
|
||||
|
||||
Constructors can only be applied when a new instance is created.
|
||||
|
||||
===Constructors as Eiffel Creation Procedures===
|
||||
|
||||
When types from .NET assemblies are made available to Eiffel systems, the constructors are presented as creation procedures. Just as constructors show up in the C# and VB.NET environments with names appropriate to those languages, so it is with Eiffel for .NET. Always, constructors will have feature names which begin with the word "<code>make</code>", the convention for creation procedure naming in Eiffel.
|
||||
|
||||
If there is only one version of the constructor, that version will be mapped to a single feature named <code>make</code>. However, if there are overloaded versions of the constructor, then these versions given names starting with "<code>make_from_</code>" and then followed with the argument names from the assembly metadata separated with the conjunction "<code>_and_</code>". Let's look at an example.
|
||||
|
||||
The .NET type <code>System.Drawing.Size</code>has an overloaded constructor with two versions. In the <code>System.Drawing</code> assembly metadata, these two constructor versions look like this:
|
||||
<code>
|
||||
void .ctor(int32 width, int32 height)
|
||||
void .ctor(System.Drawing.Point pt)
|
||||
</code>
|
||||
|
||||
So the argument names for the first version are <code>width</code> and <code>height</code>. For the second version there is only one argument named <code>pt</code>. The constructor versions as presented to Eiffel programmers as creation procedures look like this:
|
||||
<code>
|
||||
make_from_width_and_height (width: INTEGER; height: INTEGER)
|
||||
make_from_pt (pt: DRAWING_POINT)
|
||||
</code>
|
||||
|
||||
Presenting the names in this format handles the conflicts of overloading and provides reasonably intuitive names that comply with Eiffel naming conventions.
|
||||
|
||||
===Eiffel Creation Procedures as Constructors?===
|
||||
|
||||
Eiffel creation procedures do not map to constructors when Eiffel classes are compiled into assemblies. Rather, they are actually manifested as functions on a factory class in the namespace <code lang=text>Create</code> in the assembly. These functions return an initialized instance. In the section Type Organization there is more information about the organization of the types in assemblies built with Eiffel for .NET, along with an example of using types from such an assembly.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
[[Property:title|Eiffel Class and Feature Names]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|16e4a231-7aae-4b37-52fd-67876cc222ad]]
|
||||
Certain naming conventions are respected by Eiffel programmers. Although Eiffel is not case-sensitive, convention dictates the use of upper and lower case in particular situations. When you program in Eiffel for .NET, you create new Eiffel classes, but you also use types from .NET assemblies. These .NET types are presented to you in a view that is consistent with Eiffel conventions.
|
||||
|
||||
==Eiffel Class Names==
|
||||
|
||||
Convention dictates that Eiffel class names are always all upper case characters with words separated by the underscore ("_") character. Eiffel classes are not qualified by "namespace" as in some other languages. This means that Eiffel class names must be unique with in a system. It also means that any types from existing .NET assemblies will have their names mapped to conventional Eiffel class names in order to be used by Eiffel classes in EiffelEnvision
|
||||
|
||||
Here are some class names and their descriptions from the Eiffel Base Library. These class names comply with the Eiffel class naming convention.
|
||||
<code>
|
||||
STRING
|
||||
|
||||
Sequences of characters, accessible through integer indices in a contiguous range.
|
||||
|
||||
RANDOM
|
||||
|
||||
Pseudo-random number sequence, linear congruential method.
|
||||
|
||||
ARRAYED_LIST
|
||||
|
||||
Lists implemented by resizable arrays.
|
||||
|
||||
LINKED_QUEUE
|
||||
|
||||
Unbounded queues implemented as linked lists.
|
||||
|
||||
</code>
|
||||
|
||||
Now here are some type names from the .NET mscorlib as they appear as conventional Eiffel class names (i.e. , in the form in which they become available to Eiffel for .NET programmers), followed by their full .NET type and their Summary from the .NET library. names.
|
||||
<code>
|
||||
SYSTEM_STRING
|
||||
|
||||
System.String
|
||||
|
||||
Represents an immutable series of characters.
|
||||
|
||||
SYSTEM_RANDOM
|
||||
|
||||
System.Random
|
||||
|
||||
Represents a pseudo-random number generator, a device that produces a sequence of numbers that meet certain statistical requirements for randomness.
|
||||
|
||||
ARRAY_LIST
|
||||
|
||||
System.Collections.ArrayList
|
||||
|
||||
Implements the System.Collections.IListinterface using an array whose size is dynamically increased as required.
|
||||
|
||||
SYSTEM_QUEUE
|
||||
|
||||
System.Collections.Queue
|
||||
|
||||
Represents a first-in, first-out collection of objects.
|
||||
|
||||
</code>
|
||||
|
||||
In summary, Eiffel class names and type names from .NET assemblies made available to Eiffel programmers will be all upper case, with words separated by the underscore character.
|
||||
|
||||
===Eiffel Names for .NET Types===
|
||||
|
||||
How Eiffel compliant names are derived from .NET type names is fairly simple in most cases. The "simple" class name, that is, the word following the rightmost dot in the full class name, is converted to an Eiffel compliant name by making it upper case and separating in embedded words by underscore. In the example above, <code>System.Collection.ArrayList</code> becomes <code>ARRAY_LIST</code>.
|
||||
|
||||
The other cases in the example are not quite so simple. If the basic derivation produces a name which conflicts with a classname in the Eiffel Base Library, then it will be disambiguated. The simple derivation of <eiffel>System.String</eiffel> would be <eiffel>STRING</eiffel>, but this would conflict with Eiffel's <eiffel>STRING</eiffel> class, so <eiffel>System.String</eiffel> becomes available to Eiffel for .NET programmers as <eiffel>SYSTEM_STRING</eiffel>.
|
||||
|
||||
Sometimes it is better to disambiguate an entire assembly rather than handling individual exceptions to the simple derivation. This is done by specifying a common prefix for all types in the assembly. For example, EiffelEnvision uses a prefix of " <eiffel>DATA_</eiffel>" for all classes in the .NET assembly System.Data. As a result, the type <eiffel>System.Data.Constraint</eiffel> is available in Eiffel as class <eiffel>DATA_CONSTRAINT</eiffel>.
|
||||
|
||||
You'll see a little more about namespaces, assemblies, and Eiffel clusters in [[Type Organization|Type Organization]] .
|
||||
|
||||
===Similar Types from Both Libraries===
|
||||
|
||||
You may have noticed a similarity in the names and descriptions from the Eiffel Base Library and those from the .NET "mscorlib" library. This is not by accident. The Eiffel class <code>STRING</code> is a different class from the .NET type <code>System.String</code>, which Eiffel programmers see represented as Eiffel class <code>SYSTEM_STRING</code>. There is more on this subject in [[Similar Types Occurring in Both Libraries|Similar Types Occurring in Both Libraries]] .
|
||||
|
||||
==Eiffel Feature Names==
|
||||
|
||||
By convention, feature names in Eiffel use all lower case characters, and like class names, words are separated by underscore. Also as with class names, the names of members from .NET assemblies will be represented in a form that complies with the Eiffel convention.
|
||||
|
||||
Let's look at some simple examples. First some feature names from the Eiffel Base Library.
|
||||
<code>
|
||||
to_upper
|
||||
|
||||
From class STRING: Converts to upper case.
|
||||
|
||||
item_double
|
||||
|
||||
From class RANDOM: The current random number as a double between 0 and 1
|
||||
|
||||
</code>
|
||||
|
||||
Now check out these member names from the .NET "mscorlib" type library. These have been made available to Eiffel for .NET programmers in the Eiffel convention. Following the Eiffel name, you see their .NET member name and type name.
|
||||
<code>
|
||||
to_upper
|
||||
|
||||
Member ToUpper from typeSystem.String
|
||||
|
||||
Returns a new System.String with the same content as the target, except all upper case.
|
||||
|
||||
next_double
|
||||
|
||||
Member NextDouble from type System.Random
|
||||
|
||||
A double-precision floating point number greater than or equal to 0.0, and less than 1.0.
|
||||
|
||||
</code>
|
||||
|
||||
So, Eiffel feature names, and the names of .NET members made available to Eiffel for .NET programmers, are all lower case with words separated by underscores.
|
||||
|
||||
==Overloaded .NET Member Names==
|
||||
|
||||
The .NET object model allows overloading of function names. This means that a .NET type can support multiple functions with the same name, that vary only by the types of the arguments they accept. For example, the .NET type System. Text. StringBuilder supports nineteen overloaded versions of the Append function. Here are a couple of examples, in their .NET forms:
|
||||
<code>
|
||||
.NET function signature: Append(System.String)
|
||||
|
||||
Member of type System.Text.StringBuilder
|
||||
|
||||
Appends a copy of the specified string to the end of this instance.
|
||||
|
||||
.NET function signature: Append(System.Char)
|
||||
|
||||
Member of type System.Text.StringBuilder
|
||||
|
||||
Appends the string representation of a specified Unicode character to the end of this instance.
|
||||
|
||||
</code>
|
||||
|
||||
The Eiffel programming language does not allow overloading routine names. That means that you cannot code multiple routines with the same name in a single class. That in itself is not a problem. But it also means that to work in the .NET environment, where overloading is allowed some compromise has to be made. So, what happens is this: if you are programming in Eiffel for .NET and you are using types from a .NET assembly, those types will be presented to you as if they are Eiffel classes. We have already seen that the type and feature names will be shown in the Eiffel naming convention. With overloaded feature names, the presentation will use name augmentation to disambiguate the overloaded versions. What you see is a distinct feature name for each overloaded version. The basic feature name is augmented by adding the types of its respective arguments, separated by underscore.
|
||||
|
||||
Let's look again at the two <code>Append</code> functions from <code>System.Text.StringBuilder</code>.
|
||||
<code>
|
||||
.NET function signature: Append(System.String)
|
||||
|
||||
Known to Eiffel as: append_string (value: SYSTEM_STRING)
|
||||
|
||||
Member of type System.Text.StringBuilder, known to Eiffel as STRING_BUILDER
|
||||
|
||||
Appends a copy of the specified string to the end of this instance.
|
||||
|
||||
.NET function signature: Append(System.Char)
|
||||
|
||||
Known to Eiffel as: append_character (value: CHARACTER)
|
||||
|
||||
Member of type System.Text.StringBuilder, known to Eiffel as STRING_BUILDER
|
||||
|
||||
Appends the string representation of a specified Unicode character to the end of this instance.
|
||||
|
||||
</code>
|
||||
|
||||
The overloading story does not end quite yet. The .NET object model allows the overloading of constructors. This issue will be discussed in the section Constructors and Creation Procedures.
|
||||
|
||||
==.NET Properties as Eiffel Features==
|
||||
|
||||
Properties in .NET provide:
|
||||
* the opportunity to '''strengthen encapsulation,''' because values cannot be receive assignment without executing the property's "set" code
|
||||
* '''uniform access ''' queries because properties are queries, but unlike previous C-style OO languages in which properties did not exist, if a property is used in programming a client class, the programmer does not need to know whether the data provided by the property was done so from memory or through computation. This leaves the producer of the class with the property the latitude to change the implementation without breaking existing clients.
|
||||
|
||||
In Eiffel, the same goals are fulfilled, but a little differently. Simple attributes are well-encapsulated, because the Eiffel programming language does not allow direct assignment to them from outside the control of their class. So any assignment of the form <code>x</code>. <code>f</code> := <code>y</code> is not valid in Eiffel. To allow client to set values of the attribute <code>f</code>, the producer of the class of which <code>x</code> is an instance would have built a command (a "<code>set_</code>" procedure) to do so. Then the code in a client to set <code>f</code> would look like this: <code>x</code>.<code>set_f</code> (<code>y</code>).
|
||||
|
||||
Uniform access is achieved in Eiffel through the way in which clientssee features which are queries. The code " <code>print</code> ( <code>x</code>.<code>count</code>)" applies the query <code>count</code> to the object attached to <code>x</code> and prints the result. You cannot tell by looking at this code whether <code>count</code> is an attribute or a function, that is, whether the <code>count</code> is returned from memory or through computation. In fact, it could be either, but that is a matter for its producer to deal with. As reuse consumers the implementation of <code>count</code>is not important to us ...but the fact that the producer can change the implementation without causing our code to need modification is very important to us.
|
||||
|
||||
Because Eiffel does not support properties directly, the propertiesof typeswhich Eiffel for .NET programmers usefrom .NET assemblies have to be mapped to Eiffel features.
|
||||
|
||||
In order to ask for the property's current value (technically, receiving the result of the property's <code>get</code> routine), a query feature is generated for the property. The query will be namedthe Eiffel name of the property.
|
||||
|
||||
As noted above, setting the value of a property cannot be done in Eiffel as it is done in C# and VB.NET because Eiffel disallows assignments of the form <code>x</code>.<code>f</code> := <code>y</code>. So, for each writable property, an Eiffel command feature is available to set the value of the property. The name for this command will be "<code>set_</code>" followed by the Eiffel name for the property.
|
||||
|
||||
As a result, the code for using a .NET property looks very much like the code to use an Eiffel attribute. In the following code fragment, an instance of the type <code>System.Windows.Forms.Form</code> which is available in Eiffel for .NET as <code>WINFORMS_FORM</code> is used by an Eiffel client. <code>System.Windows.Forms.Form</code> has a property <code>Text</code> which is of type <code>System.String</code>. Here the <code>Text</code> property is being set using the <code>set_text</code> feature, and then being recalled by using the query <code>text</code>.
|
||||
<code>
|
||||
local
|
||||
my_window: WINFORMS_FORM
|
||||
my_string: SYSTEM_STRING
|
||||
do
|
||||
create my_window.make
|
||||
my_window.set_text (my_window_title) -- Set Text property
|
||||
my_string := my_window.text -- Query Text property
|
||||
.
|
||||
.
|
||||
end
|
||||
</code>
|
||||
|
||||
==Static Features in .NET==
|
||||
|
||||
In the .NET object model it is possible for certain members of a type to be "static". When these members are used, they are used without an instance of the class as a target. In essence, they are called on the class itself.
|
||||
|
||||
In Eiffel for .NET, these staticmembers will made available with feature names derivedusing the same conventions as ordinary features, but applying them will be a bit different.
|
||||
|
||||
There is not a concept analogous to static members in Eiffel. The model for object-oriented computation in Eiffel specifies that whenever feature application takes place, there must be an object, i.e. an instance of some class, that serves as a target.
|
||||
|
||||
In order to use .NET types that include static members, a special syntax has been added into Eiffel for .NET. The following example uses this syntax to call a static function:
|
||||
<code>
|
||||
local
|
||||
my_window: WINFORMS_FORM
|
||||
do
|
||||
create my_window.make
|
||||
my_window.set_text (my_window_title)
|
||||
.
|
||||
.
|
||||
feature {WINFORMS_APPLICATION}.run_form (my_window)
|
||||
end
|
||||
|
||||
</code>
|
||||
|
||||
The type <code>System.Windows.Forms.Application</code> is used here. It is available to Eiffel under the name <code>WINFORMS_APPLICATION</code>. The static member being used is <code>Run</code>, in particular the overloaded version of <code>Run</code> which takes an argument of type <code>System.Windows.Forms.Form</code>. That version is available in Eiffel under the name <code>run_form</code>.
|
||||
|
||||
The important thing to see here is that when you need to apply a static member, you introduce the call with the keyword <code>feature</code>. Then enclose the type name in braces and apply the feature as if it were targeted to an object. This isfairly close to the way that the call would be made in C#, where the feature name would be applied to the type name, versus a target object:
|
||||
<code>
|
||||
{
|
||||
Form my_window;
|
||||
my_window = new Form();
|
||||
my_window.Text = "Hello World!";
|
||||
.
|
||||
.
|
||||
Application.Run(my_window); // This is C#
|
||||
}
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
[[Property:title|Eiffel for .NET Terminology]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|ab1ac480-e2d3-df5d-5f06-963899f1072d]]
|
||||
==Eiffel Terminology Defined for C# and VB.NET Programmers==
|
||||
|
||||
Eiffel programmers feel that it is important to have a set of precise terms with which to communicate about our method of software development. Like everything else in Eiffel, the use of certain terms is not accidental. They were chosen carefully to impart particular meaning. Many of the Terms that Eiffel programmers use are different from those used by developers using other object-oriented languages. But that does not mean that their meanings will be foreign to you. If you have some understanding of object-oriented technology, you will find that in many cases the Eiffel terms describe concepts with which you were already familiar, just under different names, and possibly with slightly different meanings.
|
||||
|
||||
The intention of this glossary is to give you a list of these terms with extended definitions that will relate the Eiffel terms to language with which you may be more familiar. Also, you will find an occasional term which comes from other programming cultures but is not used in Eiffel.
|
||||
{|
|
||||
|-
|
||||
| Term
|
||||
| Definition
|
||||
|-
|
||||
| Ancestors
|
||||
| The set of Classes from which a particular Class Inherits, including the Class itself. Somewhat analogous to Bases and Interfaces in C# and VB.NET (see Proper Ancestors).
|
||||
|-
|
||||
| Argument
|
||||
| An object which is passed to a routine. Called a Parameter in C# and VB.NET.
|
||||
|-
|
||||
| Assertion
|
||||
| A declaritive expression that evaluates to true or false. Assertions in Eiffel are of different types and are used primarily for expressing software specification to potential Reuse Consumers and ensuring that executing software complies with specification. Any violation of an assertion causes an exception, and is generally indicative of a defect in the software system.
|
||||
|-
|
||||
| Attribute
|
||||
| A Feature which represents storage used in every instance of a class. Used like a Field member in C#.
|
||||
|-
|
||||
| Class
|
||||
| Software text which is the static definition of a Type (or the pattern for a Type in the case of a Generic Class). Used in very nearly the same way as in C#. In C# Classes are used for reference types and Structures for value types. In Eiffel, Classes are used for both, but see Expanded Class.
|
||||
|-
|
||||
| Class invariant
|
||||
| An Assertion on a Class which defines the valid or stable state for objects which are instances of the Class. All instances of the Class must comply with the Class Invariant at any time that they are accessible to Clients.
|
||||
|-
|
||||
| Client
|
||||
| A Class whose role is that of Client in a Client/Supplier Relationship with another Class.
|
||||
|-
|
||||
| Client/Supplier relationship
|
||||
| One of the two Relationships that can exist between Classes. Client/Supplier is characterized by a situation in which one Class, called the Client, has or uses, instances of the other Class, called the Supplier. For example, if class "X" declares an entity "y" of type STRING, then Class X becomes a Client to Class STRING as a result. At the same time, Class STRING becomes a Supplier to class X.
|
||||
|-
|
||||
| Cluster
|
||||
| A group of classes (or recursively, clusters) which share some criteria which justifies their being grouped together. Classes are usually clustered together because they model similar entities or have similar functionality. For example, in the Eiffel Base Library, there is a cluster called "structures" which contains clusters for data structures. One cluster is "list" which contains classes like LINKED_LIST and TWO_WAY_LIST. Another "structures" cluster is "dispensers" which contains classes like "ARRAYED_STACK" and "PRIORITY_QUEUE". When .NET assemblies are made available to Eiffel programmers, each assembly is viewed as a cluster.
|
||||
|-
|
||||
| Command
|
||||
| A Class Feature which can change the state of the instance to which it is applied, and does not return a result. It is the Reuse Consumer's view of a Procedure.
|
||||
|-
|
||||
| Contract
|
||||
| Like a contract between human parties, a software Contract is a formal agreement between software components, Classes in the case of Eiffel. The contract is written in the Assertions of a Supplier Class and specifies in precise terms those conditions with which potential Clients may interact with the Supplier. The contract for a particular Class is composed of the contracts for each Exported Routine of the Class as well as the Class Invariant.
|
||||
|-
|
||||
| Creation Procedure
|
||||
| A Procedure which can be used to initialize and instance of a Class. Similar to a Constructor in C# or VB.NET, but slightly different. A Creation Procedure can be applied when an object is being created, but can also be applied later to reinitialize an existing object (such is not the case with Constructors). Also, Creation Procedures are responsible for ensuring that newly created objects comply with their Class Invariants.
|
||||
|-
|
||||
| Deferred Class
|
||||
| A class which has at least one Deferred Feature. The term "Deferred" is used in the Eiffel culture like the term "Abstract" in .NET. Like an abstract class in C# or VB.NET, it is not possible to make direct runtime instances of a Deferred Class.
|
||||
|-
|
||||
| Deferred feature
|
||||
| A Class Feature which has not implementation. Like a virtual method in C#. Can apply to Commands or Queries.
|
||||
|-
|
||||
| Descendents
|
||||
| The set of all Classes which Inherit from a particular Class, including the Class itself. Somewhat analogous to subclasses in C# and VB.NET (see Proper Descendants).
|
||||
|-
|
||||
| Design by contract
|
||||
| A method of developing software in which a System is composed of a set of interacting Classes which cooperate based upon precisely defined Contracts.
|
||||
|-
|
||||
| Direct instance
|
||||
| A runtime Object of some type, based directly on the definition from a specific Effective Class. See Instance for more information. Usually used in conjunction with the class name, e.g. "an instance of class POLYGON".
|
||||
|-
|
||||
| Effective class
|
||||
| A Class which has no Deferred Features. Only if a Class is Effective can it have direct runtime instances. Effective classes are called Concrete Classes in some programming cultures.
|
||||
|-
|
||||
| Eiffel Metadata Consumer
|
||||
| Applicable to Eiffel for .NET only, this is a mechanism that works behind the scenes to make Types from .NET assemblies available to Eiffel programmers. The Metadata Consumer reconciles the differences between the naming conventions of Eiffel and .NET by providing an Eiffel compliant view of the Types in .NET assemblies.
|
||||
|-
|
||||
| Expanded Class
|
||||
| Like a value type in C#, but with some restrictions. .NET value types are viewed to Eiffel programmers as Expanded Classes. For reference types, an object field holds a reference to a runtime object (or is Void). For value types, the object field holds the fields of the runtime object. A Type based on an Expanded Class is an Expanded Type.
|
||||
|-
|
||||
| Exported feature
|
||||
| A Feature which is available to Client Classes. Analogous to "public" in C# and VB.NET. However, Exporting in Eiffel has fine granularity. It is possible to make certain Features available to Clients of all types or only to Clients which conform to certain named Classes (vs Public, Protected, Private)
|
||||
|-
|
||||
| Feature
|
||||
| Any of a Class's Attributes or Routines. Used similarly to the term Member as in C#. In Eiffel class features can be only Attributes or Routines.
|
||||
|-
|
||||
| Feature Application
|
||||
| The process of using the Class Features on instances of the Class. The model for Feature Application is "x.f(a,...)" where "x" is an entity which will be attached to some object at runtime, "f" is some Feature of the Class on which x is based, and "a,..." is an optional list of arguments which may be appropriate for some Routines. Feature "f" is "applied" to the object attached to "x". Feature Application may involve calling Routines, or may only involve returning the value of an Attribute from memory. Feature application is sometimes called "feature call".
|
||||
|-
|
||||
| Function
|
||||
| A Routine that returns a result. In C# function results can be typed as "void", meaning that there is no result returned. Eiffel functions cannot be typed "void" (see Procedure). All Functions must specify a result type. Of course, if the result type is a reference type, there could be cases in which the reference resulting from some specific call might be Void.
|
||||
|-
|
||||
| Generic Class
|
||||
| A Class which is written using parameters to represent other Classes or Types to enhance its reusability. For example, the Class LINKED_LIST [G] is a generic class in which "G" represents some other Class. So, LINKED_LIST is written independent of the items an instance of the list will hold. We cannot declare a LINKED_LIST without specifiying a Class to substitute for "G". If we want a list of cats we can declare one by using the Type: LINKED_LIST [CAT]. Generic Classes are not currently implemented in .NET, so there's no corresponding concept in C#.
|
||||
|-
|
||||
| Inheritance
|
||||
| One of the two Relationships that can exist between Classes. If Class B inherits from Class A, then every feature in Class A is potentially available to instances of Class B. Also, when ever an instance of Class A is required, an instance of Class B will suffice. Inheritance is used in Eiffel much the same as in C#. The Eiffel model allows full, controlled multiple inheritance, though. Eiffel Classes may inherit from multiple Concrete Classes and name clashes are handled through a process called Feature Adaptation.
|
||||
|-
|
||||
| Heir
|
||||
| The adjacent Proper Descendants of a Class. If class C inherits from class B and class B inherits from class A, then class A's Proper Descendants include B and C, but A's Heirs only include B.
|
||||
|-
|
||||
| Instance
|
||||
| A runtime Object of some type, based on the definition from a Class. Usually used in conjunction with the class name, e.g. "an instance of class POLYGON". In contrast with Direct Instance, an Instance of class POLYGON is a Direct Instance of POLYGON or any Proper Descendant of POLYGON such, perhaps, as RECTANGLE. So it its possible to have Objects which are Instances of Deferred Classes, but it is impossible to have Objects which are Direct Instances of Deferred Classes.
|
||||
|-
|
||||
| Interface
|
||||
| Not supported as such in Eiffel. Used in C# and VB.NET, Interfaces provide sets of specific Class Features which must be effected by any Concrete Class that "implements" the Interface. There is no concept of Interface in Eiffel that is separate from the concept of Class. So, Interfaces from .NET appear to Eiffel programmers to be completely Deferred (abstract) Classes.
|
||||
|-
|
||||
| Manifest string
|
||||
| A quoted string used in source code, for example the "Hello World!" in: <br/>
|
||||
my_string := "Hello World!" <br/>
|
||||
This is often called a "literal" string in other languages. In Eiffel, the quoted string "Hello World!" above is, by virtue of its presence, an instance of the Eiffel STRING class. In Eiffel there are manifest constants of other types as well, like numbers coded explicitly in software text, and the keywords True and False as the manifest booleans.
|
||||
|-
|
||||
| Module
|
||||
| A syntactical grouping of software text. Source code is divided into Modules in order to help organize it in some meaningful way or to achieve some goal.
|
||||
|-
|
||||
| Object
|
||||
| An instance of a Class during system execution. As such, Objects only exist at runtime.
|
||||
|-
|
||||
| Overloading
|
||||
| Not supported in Eiffel. Overloading is the ability to have more than one Feature with the same name, varying only by the Types of its Arguments. Overloading is supported by the underlying object model in .NET. As a result many classes in .NET assemblies have overloaded methods. The Eiffel Metadata Consumer disambiguates the overloaded names so that by the time you see them in the Eiffel context they appear no longer to be overloaded.
|
||||
|-
|
||||
| Parent
|
||||
| The adjacent Proper Ancestors of a Class. If class C inherits from class B and class B inherits from class A, then class C's Proper Ancestors include A and B, but C's Parents only include B.
|
||||
|-
|
||||
| Postcondition
|
||||
| An assertion coded on a Routine which defines the conditions will be true upon successful completion of the Routine. It is the responsibility of the Routine to ensure that the Postcondition holds after the Routine executes.
|
||||
|-
|
||||
| Precondition
|
||||
| An assertion coded on a Routine which defines the conditions under which the Routine can complete successfully. It is the responsibility of the Client to ensure that the Precondition holds before attempting to call the Routine.
|
||||
|-
|
||||
| Procedure
|
||||
| A Routine that does not return a result. Much like a "void" function in C#.
|
||||
|-
|
||||
| Proper ancestors
|
||||
| The set of all Classes from which a particular Class Inherits, excluding the Class itself.
|
||||
|-
|
||||
| Proper descendents
|
||||
| The set of all Classes which Inherit from a particular Class, excluding the Class itself.
|
||||
|-
|
||||
| Query
|
||||
| A Class Feature which returns information from the instance to which it is applied. Queries are the Reuse Consumer's view of Attributes and Functions.
|
||||
|-
|
||||
| Reference type
|
||||
| A type whose instances are accessed via a reference. For reference types, an object field holds a reference to a runtime object (or is Void). This is in contrast to value types, in which case, the object field holds the fields of the runtime object. In Eiffel, reference types are based on reference classes, i.e., any class which is not an Expanded Class.
|
||||
|-
|
||||
| Relationship
|
||||
| An association between two classes. There are only two types of Relationships that can exist between classes: Client/Supplier and Inheritance.
|
||||
|-
|
||||
| Reuse consumer
|
||||
| The role a software engineer assumes when in the process of making use of Classes or Types already in existence. As a reuse consumer, the engineer is interested primarily in the specification of those Classes or Types being used, rather than their implementation.
|
||||
|-
|
||||
| Reuse producer
|
||||
| The role a software engineer assumes when in the process or creating a new Class. In this role the engineer attempts to produce a product which will be reusable by him or herself and other engineers in the future. In the Eiffel culture, whenever a software engineer creates a class, it is as a Reuse Producer, and with the thought that each new class has the potential to become reusable.
|
||||
|-
|
||||
| Root class
|
||||
| For any System, the Class which will be instantiated to start the system. Specified by project settings.
|
||||
|-
|
||||
| Root procedure
|
||||
| A Creation Procedure of the Root Class of a System which will be applied to an initial instance of the Root Class at System startup. Similar to Main() in C# and VB.NET, but with the important difference that the fact that a particular Creation Procedure is the Root Procedure for a System is not define in the Class itself as with Main(). Rather, it is specified outside the Class in project settings. This helps Reuse Producers keep Classes more autonomous and therefore potentially more reusable.
|
||||
|-
|
||||
| Routine
|
||||
| A computational Class Feature which is either a Function or Procedure. Routines are the executable parts of a Class. Routines are similar to Methods in C#.
|
||||
|-
|
||||
| Software specification
|
||||
| A statement of how a software element is to be used and what it will do when executed. Ideally, this is stated in terms that do not betray how the software does what it does.In Eiffel, specification is included in the code, rather than a separate document. For a routine, it consists of its call signature and its contract. For a class, it consists of the signatures and contracts for all public features.
|
||||
|-
|
||||
| Supplier
|
||||
| A Class whose role is that of Supplier in a Client/Supplier Relationship with another Class.
|
||||
|-
|
||||
| System
|
||||
| A set of Classes related by the two Relationships which can be compiled to produce an executable.
|
||||
|-
|
||||
| Type
|
||||
| The description of a set of Objects equipped with certain Features.
|
||||
|}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
[[Property:title|Eiffel reserved words]]
|
||||
[[Property:weight|6]]
|
||||
[[Property:uuid|3b80ea3e-2ed1-adf6-ddf5-acd4e18c6d03]]
|
||||
The Eiffel programming language reserves certain words as meaningful to the Eiffel compiler.
|
||||
|
||||
To learn more about the reserved words specified by the current Eiffel standard, see the [[Eiffel programming language reserved words]] page of the [[Quick reference to the Eiffel programming language]].
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[[Property:title|Conventions]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|1f101597-06cd-b851-8cc1-e214b3eedb3e]]
|
||||
In this section, you'll find information about how the conventions that are normally used in Eiffel programming are affected by working in the presence of .NET.
|
||||
|
||||
Also, you'll find out how .NET types sometimes look a little different in the presence of Eiffel.
|
||||
|
||||
Because there are differences in the underlying object models upon which .NET and Eiffel have been designed, before .NET types can be made available to Eiffel developers, the assemblies in which they reside must be processed by a utility called the Eiffel Metadata Consumer. Fortunately, this all happens behind the scenes for you. You need only be aware that when you are looking at .NET types from within EiffelEnvision, you're seeing them through Eiffel-tinted lenses.
|
||||
|
||||
This is really the same thing that happens with other .NET languages with Visual Studio .NET support. For example, if you look at a constructor in a .NET type using the "ildasm" utility, the constructor will be named "<code>.ctor</code>". But, if you're in C# and look at the same constructor in the Visual Studio .NET Object Browser, the constructor will have the same name as the type ...it's a C# convention. Likewise, the same constructor viewed in Visual Basic .NET will have the name <code>New</code> ...that's the Visual Basic .NET convention. So, when you use EiffelEnvision, you see things as presented using Eiffel Conventions.
|
||||
|
||||
Consider what happens when you look at .NET types using EiffelEnvision. When you look, for example at the type <code lang="text">System.EventArgs</code>, you will see it represented as an Eiffel class called <code>EVENT_ARGS</code>. The members of the .NET type will show up as features of the Eiffel class. Of course, you can also see the corresponding .NET names as they exist in the assembly metadata.
|
||||
|
||||
The documents in this section tell you what you need to know in order to use .NET types in Eiffel, and vice versa.
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
[[Property:title|Similar Types Occurring in Both Libraries]]
|
||||
[[Property:weight|5]]
|
||||
[[Property:uuid|4c9f6ad8-107b-af69-3eb3-3f076f2c936c]]
|
||||
==Whose String is it anyway?==
|
||||
|
||||
Over the last 15 years or so, the Eiffel class libraries have been a source for reusable software components for developers.
|
||||
|
||||
The Eiffel Base library contains classes for commonly used objects like different kinds of numbers, strings, files, and data structures.
|
||||
|
||||
But there are also libaries of Eiffel classes for sophisticated purposes like lexical analysis and parsing, data access, and graphical user interface development.
|
||||
|
||||
Likewise .NET is delivered with assemblies containing thousands of powerful types with similar purposes.
|
||||
|
||||
===Working in Both Worlds===
|
||||
|
||||
When we build software that has access to both the rich Eiffel libraries and the many useful .NET types, we inevitably run into types from both worlds that have similar names and purposes, but are still different types with different semantics.
|
||||
|
||||
===The Case of Strings===
|
||||
|
||||
The example of these similar types which will almost certainly get in your face is the string types. You may remember that we looked briefly at the case of the string types in Naming Conventions and Name Handling.
|
||||
|
||||
The Eiffel Base Library contains class <code>STRING</code>; the .NET assembly contains type <code>System.String</code>, which Eiffel for .NETusers see as <code>SYSTEM_STRING</code>. At an abstract level both of these model sequences of characters. But they are not the same type. In fact, they are different in some important ways. For example, instances of <code>System.String</code> are immutable. So you cannot append to an instance of <code>System.String</code>. If you want to build a string by appending, you should use an instance of <code>System.Text.StringBuilder</code> to do the appending, then extract the instance of <code>System.String</code> from it. With <code>STRING</code> it is permissible to append, so you don't need a helper like the <code>System.Text.StringBuilder</code> type.
|
||||
|
||||
So the two types are similar at an abstract level, but different semantically. There are reasonable arguments for the design of each.
|
||||
|
||||
Many types in the delivered assemblies have properties which are strings or methods which return or take strings as arguments. In all these cases, the strings in question are instances of <code>System.String</code>.
|
||||
|
||||
Many classes in the delivered Eiffel libraries have features involving strings, that is attributes which are strings or routines which return or take strings as arguments. In all these cases, the strings are instances of <code>STRING</code> (except for those designed for .NET compliance).
|
||||
|
||||
In C# and VB.NET, if you specify a quoted string like <code>"Hello World!"</code> in your code, that string will conform to type <code>System.String</code>. If you do the same in Eiffel, then <code>"Hello World!"</code> will be an instance of <code>STRING</code>. In Eiffel terminology, <code>"Hello World!"</code> appearing in source code is a manifest string.
|
||||
|
||||
What all this means to you is that you cannot use an instance of <code>System.String</code> when an instance of <code>STRING</code> is called for, and vice versa. Three out of four of the executable lines in the following code sample are invalid:
|
||||
<code>
|
||||
local
|
||||
my_string: STRING
|
||||
my_system_string: SYSTEM_STRING
|
||||
do
|
||||
my_system_string := "Hello World!" -- Invalid
|
||||
my_string := "Hello World!" -- Valid
|
||||
my_string := my_system_string -- Invalid
|
||||
my_system_string := my_string -- Invalid
|
||||
.
|
||||
.
|
||||
end
|
||||
</code>
|
||||
|
||||
To handle this issue, the Eiffel for .NET class <code>STRING</code> has two features which can be used when a string of the other type is needed.
|
||||
|
||||
The first of these features is a query <code>to_cil</code> which returns an object of type <code>System.String</code> which has a sequence of characters equivalent to that of the <code>STRING</code> to which <code>to_cil</code> is applied. The <code>to_cil</code> can be applied to manifest strings by enclosing the manifest string in parentheses.
|
||||
|
||||
The other feature is a creation procedure named <code>make_from_cil</code> which takes as an argument an instance of <code>System.String</code> and initializes its target <code>STRING</code> with a sequence of characters equivalent to that of the argument.
|
||||
|
||||
In the following sample, we use these features of <code>STRING</code> to make all the lines from the previous sample valid.
|
||||
<code>
|
||||
local
|
||||
my_string: STRING
|
||||
my_system_string: SYSTEM_STRING
|
||||
do
|
||||
my_system_string := ("Hello World!").to_cil -- Valid
|
||||
my_string := "Hello World!" -- Valid
|
||||
my_string.make_from_cil (my_system_string) -- Valid
|
||||
my_system_string := my_string.to_cil -- Valid
|
||||
.
|
||||
.
|
||||
end
|
||||
</code>
|
||||
|
||||
{{note|As shown in the above example, it is necessary to apply <code>to_cil</code> to a manifest string if you are assigning it to a <code>System.String</code> or passing it as an argument where a <code>System.String</code> is called for. <br/>
|
||||
This is expected to change in a future release. It shall become unnecesary to apply <code>to_cil</code> to manifest strings. Instead, whether a <code>STRING</code> or <code>System.String</code> is needed will be determined by the context in which the manifest string is being used, and the proper type of object will be generated. }}
|
||||
|
||||
===Other Similar Types===
|
||||
|
||||
There are many other cases of types available from the .NET assemblies which have similar purpose and semantics to those found in the Eiffel libraries. Fortunately, there is none that you will have to deal with as often as strings.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
[[Property:title|Type Organization]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:uuid|1837e091-695b-339c-f04f-cbfaea791a6c]]
|
||||
In any comprehensive object-oriented system, the act of programming results in creation of new data types. There must be a way of organizing these types and/or their static representation as classes. This section tells you how classes are organized in Eiffel and in .NET, and how these organization methods are used together.
|
||||
|
||||
==Eiffel Clusters==
|
||||
|
||||
Eiffel classes are grouped in clusters. These clusters of classes ordinarily share some commonality of functionality or purpose. In other words,the classes in a particular cluster may provide the same sorts of capabilities or relate to a single software model. In the Eiffel Base Library there is a cluster called <code>list</code> which contains classes which implement different types of lists, for example, <code>ARRAYED_LIST</code>, <code>LINKED_LIST</code>, <code>SORTED_TWO_WAY_LIST</code>, <code>TWO_WAY_CIRCULAR</code>. At an abstract level all these classes are related to the software model of the notion of "list".
|
||||
|
||||
The cluster <code>list</code> is actually a subcluster of the cluster <code>structures</code> which contains clusters other than <code>list</code> related to data structures other than lists. Eiffel convention dictates that a cluster should either contain classes or subclusters, but not both.
|
||||
|
||||
So clusters serve both to categorize and locate classes. So, class <code>LINKED_LIST</code> can be described as a basic library class implementing a data structure, more particularly a list. As such, it can be found in the Base Library, in cluster <code>structures</code> in subcluster <code>list</code>.
|
||||
|
||||
==.NET Namespaces and Assemblies==
|
||||
|
||||
In .NET, types (the language independent, compiled form of classes) are stored in "assemblies". So, we locate a type we want to use by referencing the assembly in which it resides.
|
||||
As .NET programmers, we think of types as being categorized by namespace. For example, we view the type <code>System.Windows.Forms.TextBox</code> as the <code>TextBox</code> type in the context of the windows forms namespace ( <code>System.Windows.Forms</code>). Particularly, this is in contrast to type <code>System.Web.UI.TextBox</code> which is a <code>TextBox</code> for web forms. As it relates to making .NET types usable by Eiffel, the important thing to understand is that the real .NET type name is the fully qualified type name, including the namespace. Namespaces are simply a bit of "syntactic sugar" that keeps us from having to repeat the entire type name every time we use it in source code.
|
||||
===.NET Assemblies Available to Eiffel===
|
||||
When types from .NET assemblies are made available to Eiffel programmers, each assembly is mapped to a cluster. So all the types in an assembly appear as if they were Eiffel classes in a cluster which corresponds to the .NET assembly.
|
||||
To summarize, as you learned in Naming Conventions and Name Handling, unambiguous Eiffel-style names are made available for the.NET types in an assembly. The assembly is represented to Eiffel for .NET programmers as a cluster of classes. The process of name derivation is based upon a portion of the .NET type name, possibly augmented with a prefix to ensure uniqueness.
|
||||
|
||||
===Assemblies Built with Eiffel===
|
||||
|
||||
The object model for which Eiffel was designed differs in some ways from the .NET object model. Importantly, Eiffel supports the facilties of full, controllable multiple inheritance, and genericity, among other things, that the inherent .NET object model does not. That does not mean that these things cannot work in .NET. Indeed they can, and they make Eiffel for .NET very powerful. But, they do make things look a little different.
|
||||
|
||||
When you compile Eiffel for .NET, the result is a .NET assembly; either an executable system, or a library of potentially reusable data types. Because the object model for Eiffel is different from that of .NET, the assembly resulting from a compile is different in some ways.
|
||||
|
||||
First an assembly built using Eiffel for .NET will likely contain lots of types and interfaces. This is because as you use a class from the Eiffel libraries, say class <code>STRING</code>, all the classes upon which <code>STRING</code> depends ( <code>STRING</code>'s suppliers and ancestors) must also be included in the assembly. That's because the Eiffel libraries, unlike the Microsoft .NET libraries like <code>mscorlib</code> and <code>System.Data</code>, are not distributed as shared type libraries.
|
||||
|
||||
Another thing you may notice is that each Eiffel class you produce is represented by three entities in the assembly ... two classes and an interface. So, if you produce a class called <code>GUARD_DOG</code>, then in the assembly you'd see an interface called <code>GuardDog</code>, a class called <code>Impl.GuardDog</code>, and a class called <code>Create.GuardDog</code>. Again, this is done for reasons that concern the differences in the object models between Eiffel and .NET.
|
||||
|
||||
The <code>GuardDog</code> interface is what you use when you declare an entity or variable of that type. The objects attached to that entity at runtime will be of the type <code>Impl.GuardDog</code>. You create an instance of <code>Impl.GuardDog</code> and attach it to an entity of type <code>GuardDog</code> by calling a routine in the factory class <code>Create.GuardDog</code>. The factory routines will almost always have names that begin with the word " <code>Make</code>", and represent the creation routines of Eiffel the classes. So in the case of using an instance of <code>GuardDog</code> from a C# class, the code would like this:
|
||||
<code>
|
||||
{
|
||||
GuardDog aGuardDog = Create.GuardDog.Make(); //Create an instance
|
||||
aGuardDog.RollOver(); // Apply a feature
|
||||
}
|
||||
</code>
|
||||
|
||||
This object creation model accounts for some of the differences between constructors in .NET and creation procedures in Eiffel. These differences will be discussed in more detail in Constructors and Creation Procedures.
|
||||
|
||||
Another advantage is that it provides a syntax that is similar to that used to create objects in Eiffel. An Eiffel for .NET client to the class <code>GUARD_DOG</code> might use the following code to create and use an instance.
|
||||
<code>
|
||||
local
|
||||
a_guard_dog: GUARD_DOG -- Declare an entity of the type
|
||||
do
|
||||
create a_guard_dog.make -- Create an instance and attach to entity
|
||||
a_guard_dog.roll_over -- Apply a feature
|
||||
end
|
||||
</code>
|
||||
|
||||
You may have noticed in these examples that even though the type <code>GuardDog</code> was compiled from an Eiffel class, when the C# client uses <code>GuardDog</code>, it uses what would be considered the .NET naming convention for the type <code>GuardDog</code> (vs. <code>GUARD_DOG</code>) and the method name <code>RollOver</code> (vs <code>roll_over</code>). What happens here is that when assemblies are produced from Eiffel classes, by default .NET naming standards are used in the assembly.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
[[Property:title|Eiffel for .NET Integration]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:uuid|fe8a6a7d-4590-0db2-d59a-307082b18ecc]]
|
||||
==Differences between Eiffel and Eiffel for .NET==
|
||||
|
||||
===Limitation of Eiffel for .NET in version 5. 5===
|
||||
|
||||
Most of the Eiffel mechanisms are supported in 5. 5. All missing features listed below are planned for addition in future releases:
|
||||
* No creation of Eiffel expanded class support
|
||||
* Partial implementation of generic conformance (same as what was supported up to and including the 4.2 release of the Eiffel development environment).
|
||||
|
||||
Eiffel for .NET supports:
|
||||
* Multiple Inheritance
|
||||
* Design By Contract
|
||||
* Exception handling
|
||||
* Genericity
|
||||
* Covariance
|
||||
* Compilation of any existing Eiffel libraries as long as it does not include C externals that call into the Eiffel Software C runtime
|
||||
|
||||
===Added to Eiffel and Eiffel for .NET===
|
||||
|
||||
The following syntax can be used to declare .NET custom attributes on Eiffel entities (features and classes):
|
||||
<code>
|
||||
empty: BOOLEAN
|
||||
note
|
||||
description: "Is Current empty?"
|
||||
metadata: create {OBSOLETE_ATTRIBUTE}.make_obsoleteattribute_1 ("Use `is_empty' instead") end
|
||||
obsolete
|
||||
"Use is_empty instead"
|
||||
do
|
||||
Result := is_empty
|
||||
end
|
||||
</code>
|
||||
|
||||
The previous example shows the declaration of the obsolete feature <code> empty </code> . The custom attribute defined by <code>OBSOLETE_ATTRIBUTE</code> is used to ensure that any consumer of the resulting assembly will see the feature as being obsolete. The custom attribute is defined in the <code>note</code> clause <code>metadata</code>. The definition consists of a creation expression that creates the custom attribute with the right parameters.
|
||||
|
||||
Using the <code>metadata</code> tag is the most general way of applying a custom attribute. There are however some variations that are explained below:
|
||||
*<code>metada</code>: most general way, it applies a custom attribute to both the class and interface generated by the Eiffel compiler.
|
||||
*<code>class_metadata</code>: applies only to the class generated by the Eiffel compiler (mostly for advanced users).
|
||||
*<code>interface_metadata</code>: applies only to the interface generated by the Eiffel compiler (mostly for advanced users).
|
||||
*<code>property_metadata</code>: applies a custom attribute to the associated property generated by the Eiffel compiler for a query.
|
||||
*<code>assembly_metadata</code>: applies a custom attribute for the current assembly. It only works when present in the Eiffel system root class <code>note</code> clause.
|
||||
|
||||
==Differences between Eiffel for .NET and .NET==
|
||||
|
||||
===Covariance===
|
||||
|
||||
The CLR (Common Language Runtime) does not support [[ET: Inheritance#Covariance and anchored declarations|covariance]] due to a type safety issue that full covariance implies (known as a polymorphic [[ET: Inheritance#Catcalls|catcall]] in Eiffel). Although very rare, catcalls are not suitable to .NET where safety is one of the primary goals.
|
||||
|
||||
Eiffel for .NET implements a safe variant of covariance that will always perform a check on the types to avoid a catcall. So when a catcall is going to be performed a `Invalid Cast Exception` will be raised by the CLR instead of an unexpected behavior as is the default behavior in classic Eiffel (i.e., the behavior without catcall detection explicitly enabled).
|
||||
|
||||
Another advantage of Eiffel for .NET's implementation of covariance is that it can be easily understood by CLS compliant consumer tools. These tools will actually benefit from the Eiffel for .NET covariance.
|
||||
|
||||
===Genericity===
|
||||
|
||||
The CLR does not support generics at all, so that the following Eiffel for .NET classes:
|
||||
* <code>LIST [ANY]</code>
|
||||
* <code>LIST [INTEGER]</code>
|
||||
|
||||
will actually be generated as:
|
||||
* <code>LIST_ANY</code>
|
||||
* <code>LIST_Int32</code>
|
||||
|
||||
Meaning that if one wants to reuse an Eiffel generic class from another language than Eiffel for .NET, one has to use either <code>LIST_ANY</code> or <code>LIST_Int32</code>.
|
||||
|
||||
===Enum types===
|
||||
|
||||
Eiffel for .NET supports .NET enum types implicitly. From the point of view of Eiffel, they are just considered as expanded classes. The only difference is in the code generation. Eiffel for .NET cannot declare new enum types yet.
|
||||
|
||||
===ByRef===
|
||||
|
||||
Eiffel does not have the notion of `byref` argument passing. At the moment, Eiffel for .NET cannot call nor can it redefine a feature that has a byref argument.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,535 @@
|
||||
[[Property:title|Adding Class Features]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|04261c22-b28f-b045-a5d7-2c85efe992b9]]
|
||||
The features of a class make it useful. They are the things that objects which are instances of the class have and can do.
|
||||
|
||||
It is during the process of adding class features that we relate a class we are producing to other classes via the client/supplier relationship.
|
||||
|
||||
It is when we add features to a class that we can build the executable code that makes things happen.
|
||||
|
||||
==Categorizing Features==
|
||||
|
||||
In Eiffel, we have several ways of thinking abstractly about features and categorizing them. As you saw in [[Eiffel Classes|Eiffel Classes]] the <code>feature</code> clause gives us a way to group features in the software text. We have ways to group features more generally, too. Here are some.
|
||||
|
||||
===Source===
|
||||
|
||||
You remember the example class from [[Eiffel Classes|Eiffel Classes]] :
|
||||
<code>
|
||||
note
|
||||
description: Objects that model lists
|
||||
revision: $Revision: 1.5 $
|
||||
|
||||
class
|
||||
OLD_FASHIONED_LIST [G]
|
||||
|
||||
obsolete "This class is obsolete, use LINKED_LIST [G] instead"
|
||||
|
||||
inherit
|
||||
DYNAMIC_LIST [G]
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
-- Create an empty list.
|
||||
do
|
||||
before := True
|
||||
ensure
|
||||
is_before: before
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
item: G
|
||||
-- Current item
|
||||
do
|
||||
Result := active.item
|
||||
end
|
||||
|
||||
first: like item
|
||||
-- Item at first position
|
||||
do
|
||||
Result := first_element.item
|
||||
end
|
||||
|
||||
... other features omitted ...
|
||||
|
||||
invariant
|
||||
before_constraint: before implies (active = first_element)
|
||||
after_constraint: after implies (active = last_element)
|
||||
end
|
||||
</code>
|
||||
|
||||
|
||||
The example shows three of the features ( <code>make</code>, <code>item</code>, and <code>first</code>) coded directly in the class. These features (and the ones omitted from the example in the interest of brevity) may not be the only features of the class <code>OLD_FASHIONED_LIST</code>. In fact we can just about guarantee that there are other features of this class. Remember the inheritance part of the class:
|
||||
<code>
|
||||
inherit
|
||||
DYNAMIC_LIST [G]
|
||||
</code>
|
||||
|
||||
This means that every feature in <code>DYNAMIC_LIST</code> will be a feature of <code>OLD_FASHIONED_LIST</code>. So one way we can think of features is by their source.
|
||||
* Immediate features are those that are introduced by a class itself.
|
||||
* Inherited features are those that come to the class from its proper ancestors.
|
||||
|
||||
We will see more about this in [[Inheritance|Inheritance]] .
|
||||
|
||||
===Implementation Type===
|
||||
|
||||
Whenever we are building a class in Eiffel, we are potential reuse producers. As such, we can categorize the features of a class based on the three types of feature implementation:
|
||||
* Attribute
|
||||
* Function
|
||||
* Procedure
|
||||
|
||||
Attributes are those features which occupy storage in an instance. When you code an attribute in a class that you are producing, that class becomes a client to the class of the attribute. This is the most common way to establish a client/supplier relationship between the classses. Client/supplier is one of the two relationships that can exist between classes.
|
||||
|
||||
Functions and procedures are the computation features, that is they are features that involve executable code. Functions and procedures together are themselves categorized as routines.
|
||||
|
||||
Also, from the producer standpoint we may view features as whether they work from memory or through computation:
|
||||
* Memory
|
||||
** Attribute
|
||||
|
||||
* Computation
|
||||
** Function
|
||||
** Procedure
|
||||
|
||||
|
||||
This view can be valuable when a producer is considering performance issues. For example, if there is some class function called <code>count</code>, a producer might investigate whether it is better to leave <code>count</code> as a function, computing it each time the feature is applied, or to change it to an attribute and compute it less often.
|
||||
|
||||
===Usage===
|
||||
|
||||
A times we find it appropriate to categorize features by how we use them. Specifically, as:
|
||||
* Query
|
||||
** Attribute
|
||||
** Function
|
||||
|
||||
* Command
|
||||
** Procedure
|
||||
|
||||
|
||||
Queries are features that, when applied to an instance, provide a value in response. Commands instruct an intance to take some action, but do not return a value. Seeing features as either queries or commands is of primary interest to reuse consumers. But as producers there are important reasons for ensuring that when we implement a feature, we implement it as a query or as a command, but not both. We will see more about this in [[Design by Contract and Assertions|Design by Contract and Assertions]] .
|
||||
|
||||
==General Syntax of Features==
|
||||
|
||||
===More Sample Features===
|
||||
|
||||
Here is another example class. Again this class contrived, so it does not do anyhing worthwhile except show you what different types of features look like. This class has an attribute and a constant attribute, and functions and procedures both with and without arguments.
|
||||
<code>
|
||||
class
|
||||
SOME_CLASS
|
||||
|
||||
create
|
||||
make,
|
||||
make_with_arguments
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
-- Creation procedure
|
||||
do
|
||||
an_attribute := 5
|
||||
end
|
||||
|
||||
make_with_arguments (hour: INTEGER; minute: INTEGER; second: INTEGER)
|
||||
-- Another creation procedure
|
||||
do
|
||||
an_attribute := second + (minute * 60) + (hour * 3600)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
an_attribute: INTEGER
|
||||
-- An attribute of type INTEGER
|
||||
|
||||
another_attribute: INTEGER = 46
|
||||
-- A constant attribute
|
||||
|
||||
a_function: STRING is
|
||||
-- A function without arguments
|
||||
do
|
||||
Result := an_attribute.out
|
||||
end
|
||||
|
||||
another_function (an_int: INTEGER): INTEGER
|
||||
-- A function with arguments
|
||||
do
|
||||
Result := an_attribute + an_int
|
||||
end
|
||||
|
||||
feature -- Basic Operations
|
||||
|
||||
a_procedure
|
||||
-- A procedure with no arguments
|
||||
do
|
||||
an_attribute := an_attribute + 5
|
||||
end
|
||||
|
||||
another_procedure (an_int: INTEGER; another_int: INTEGER)
|
||||
-- A procedure with arguments
|
||||
do
|
||||
an_attribute := an_attribute + an_int + another_int
|
||||
end
|
||||
|
||||
end -- Class SOME_CLASS
|
||||
</code>
|
||||
|
||||
===Feature Declaration===
|
||||
|
||||
When you write a feature in a class, you typically will include some of the following:
|
||||
* The feature's name
|
||||
{{note|In Eiffel every feature of a class must have a name that is unique within that class. }}
|
||||
|
||||
* The feature's type (in the case of an attribute or a function)
|
||||
* The feature's formal argument list (in the case of a function or procedure that has arguments)
|
||||
* The actual value of the feature (in the case of a constant attribute)
|
||||
* The implementation code (in the case of a function or procedure)
|
||||
|
||||
Let's dissect one feature and identify its parts:
|
||||
<code>
|
||||
another_function (an_int: INTEGER): INTEGER
|
||||
-- A function with arguments
|
||||
do
|
||||
Result := an_attribute + an_int
|
||||
end
|
||||
</code>
|
||||
|
||||
In this feature:
|
||||
* "another_function" is the feature's name.
|
||||
* "(an_int: INTEGER)" is the argument list.
|
||||
** "an_int" is the name of the first argument
|
||||
** "INTEGER" is the type "an_int"
|
||||
|
||||
* "INTEGER" (after the argument list) is the feature's type.
|
||||
* "do " introduces the implementation code.
|
||||
* "Result := an_attribute + an_int" is an instruction.
|
||||
|
||||
{{note|This feature is a function. As a consequence the computation uses the keyword "<eiffel>Result</eiffel>' as an entity name for the value to be returned by the function }}
|
||||
|
||||
* "<eiffel>end</eiffel>" ends the feature declaration
|
||||
|
||||
==General Structure of Routines==
|
||||
|
||||
As you can imagine, the possibilities for coding routines are more complex than those for coding attributes. Routines always contain the keyword "is" after the first part of the feature declaration. The part after the "is" is called the routine part.
|
||||
|
||||
The routine part is made up of the following sections:
|
||||
* [[#Header Comment|Header Comment]]
|
||||
* [[#obsolete|Obsolete]]
|
||||
* [[#Precondition|Precondition]]
|
||||
* [[#Local Declarations|Local Declarations]]
|
||||
* [[#Routine Body|Routine Body]]
|
||||
* [[#Postcondition|Postcondition]]
|
||||
* [[#Rescue|Rescue]]
|
||||
|
||||
All of the sections except the Routine Body are optional.
|
||||
|
||||
Here is another feature, a routine, which has all of these except obsolete and rescue.
|
||||
<code>
|
||||
insert_text_header_item (a_label: STRING;
|
||||
a_width, a_format: INTEGER; insert_after_item_no: INTEGER)
|
||||
-- Insert a text item to the header control
|
||||
-- after the `insert_item_item_no' item.
|
||||
require
|
||||
exists: exists
|
||||
label_not_void: a_label /= Void
|
||||
insert_after_item_no_positive: insert_after_item_no >= 0
|
||||
local
|
||||
hd_item: WEL_HD_ITEM
|
||||
do
|
||||
create hd_item.make
|
||||
hd_item.set_text (a_label)
|
||||
hd_item.set_width (a_width)
|
||||
hd_item.set_format (a_format)
|
||||
insert_header_item (hd_item, insert_after_item_no)
|
||||
ensure
|
||||
item_count_increased: item_count = old item_count + 1
|
||||
end
|
||||
</code>
|
||||
|
||||
Using this example, let's identify and discuss the different sections.
|
||||
|
||||
===Header Comment===
|
||||
<code>
|
||||
-- Insert a text item to the header control
|
||||
-- after the `insert_item_item_no' item.
|
||||
</code>
|
||||
|
||||
Although the feature's header comment is optional, most Eiffel programmers and their technical managers feel that it should never be omitted. Header comments are included by some language processing tools in specialized views of classes. Header comments are almost always short, usually no more than a few lines. Header comments serve to provide a natural language description of what the feature will do (in the case of features that are commands) or what information it provides (in the case of queries).
|
||||
|
||||
===Obsolete===
|
||||
|
||||
The feature <code>insert_text_header_item</code> is not obsolete ... but if it were it would include an <code>obsolete</code> clause. This works much like the obsolete part for classes that we saw in [[Eiffel Classes|Eiffel Classes]] : the keyword "<code>obsolete</code>" followed by a manifest string which bears a message to potential reuse consumers:
|
||||
|
||||
===Precondition===
|
||||
<code>
|
||||
require
|
||||
exists: exists
|
||||
label_not_void: a_label /= Void
|
||||
insert_after_item_no_positive: insert_after_item_no >= 0
|
||||
</code>
|
||||
|
||||
The precondition part of a feature is introduced by the keyword "<code>require</code>". It contains a set of assertions which define the state necessary for the correct execution of a routine. We will see more about assertions in [[Design by Contract and Assertions|Design by Contract and Assertions]] .
|
||||
|
||||
===Local Declarations===
|
||||
<code>
|
||||
local
|
||||
hd_item: WEL_HD_ITEM
|
||||
</code>
|
||||
|
||||
This part contains the declarations for any "local entities" used by the feature. Sometimes the computation accomplished in a feature requires the use of entities which are only temporary. It would not be appropriate to make these attributes of the class. So, instead we can use local entities, which have scope only within the feature in which they are declared. In the example, <code>hd_item</code> is available as type <code>WEL_HD_ITEM</code> during the computation of feature <code>insert_text_header_item</code>.
|
||||
|
||||
{{note|A local entity name must not be the same as any feature of the class in which its feature occurs or the same as any argument name of the feature in which it occurs. }}
|
||||
|
||||
===Routine Body===
|
||||
<code>
|
||||
do
|
||||
create hd_item.make
|
||||
hd_item.set_text (a_label)
|
||||
hd_item.set_width (a_width)
|
||||
hd_item.set_format (a_format)
|
||||
insert_header_item (hd_item, insert_after_item_no)
|
||||
</code>
|
||||
|
||||
This is the routine body for a fairly typical effective, internal routine. It contains the instructions that get the job done for the feature. You may notice that experienced Eiffel programmers rarely write routine bodies that are more than a few lines long. The reason for this is more that just intuitive. This phenomenon will be explained in the section [[Design by Contract and Assertions|Design by Contract and Assertions]] .
|
||||
|
||||
There are other forms that a routine body can take. Here are some examples of some others:
|
||||
|
||||
====External Routines====
|
||||
<code>
|
||||
cwin_tooltips_class: POINTER
|
||||
external
|
||||
"C [macro ] : EIF_POINTER"
|
||||
alias
|
||||
"TOOLTIPS_CLASS"
|
||||
end
|
||||
</code>
|
||||
|
||||
The routine body above is for an "external" routine. External routines are used to represent within Eiffel classes, routines that are written in other languages.
|
||||
|
||||
{{tip|Because of the high degree of language interaction provided by Microsoft.NET, it is not necessary in Eiffel for.NET to use externals to use software components from.NET assemblies. Instead, these components are presented to the Eiffel programmer as if they were Eiffel classes. Read more about this in [[Conventions|Conventions]] . }}
|
||||
|
||||
====Once Routines====
|
||||
<code>
|
||||
once
|
||||
Result := some_computation
|
||||
</code>
|
||||
|
||||
This is the routine body of a "once" routine, specifically a "once function". A once routine is introduced by the keyword "<code>once</code>" rather than <code>do</code>. It contains an computational body that executes only the first time it is called. Upon subsequent calls it has no effect.
|
||||
|
||||
Once procedures are useful for doing things that for some reason should not be done multiple times.
|
||||
|
||||
If the once routine is a function (as the example above), it computes the resulting object on the first call, then on subsequent calls, it returns the object it originally created without executing the computation. Once functions facilitate shared objects which helps us in avoiding global entities. A class containing a once function can be inherited by many other classes. The first object to apply the once function causes the resulting object to be created and initialized. Subsequent applications by any other object accesses the originally created instance.
|
||||
|
||||
====Deferred Routines====
|
||||
<code>
|
||||
deferred
|
||||
</code>
|
||||
|
||||
The body for a deferred routine is simply the keyword "<code>deferred</code>". Below is the deferred routine body in the context of an entire feature.
|
||||
<code>
|
||||
set_position (new_position: INTEGER)
|
||||
-- Set `position' with `new_position'
|
||||
require
|
||||
exists: exists
|
||||
valid_minimum: new_position >= minimum
|
||||
valid_maximum: new_position <= maximum
|
||||
deferred
|
||||
ensure
|
||||
position_set: position = new_position
|
||||
end
|
||||
</code>
|
||||
|
||||
As we learned in [[Eiffel Classes|Eiffel Classes]] a deferred routine has specification, but no implementation. We will investigate deferred classes and features further in [[Inheritance|Inheritance]] .
|
||||
|
||||
===Postcondition===
|
||||
<code>
|
||||
ensure
|
||||
item_count_increased: item_count = old item_count + 1
|
||||
</code>
|
||||
|
||||
The postcondition part of a routine is introduced by the keyword "<code>ensure</code>". The postcondition is a group of assertions which describe the state that must be satisfied upon the successful completion of the routine. We will see more about assertions in [[Design by Contract and Assertions|Design by Contract and Assertions]] .
|
||||
|
||||
===Rescue===
|
||||
|
||||
Our example feature does not have an explicitly coded rescue part. The rescue, introduced by the keyword "<code>rescue</code>", provides a routine with a chance to do additional processing in the case that it incurs an exeption during normal processing. We will learn about the rescue clause in the section [[Exception Mechanism|Exception Mechanism]] . Until then, you can see what a rescue part looks like in the feature below.
|
||||
<code>
|
||||
bind
|
||||
-- Bind socket to local address in `address'.
|
||||
require
|
||||
socket_exists: exists
|
||||
valid_local_address: address /= Void
|
||||
local
|
||||
ext: ANY
|
||||
retried: BOOLEAN
|
||||
do
|
||||
if not retried then
|
||||
ext := address.socket_address
|
||||
c_bind (descriptor, $ext, address.count)
|
||||
is_open_read := True
|
||||
end
|
||||
rescue
|
||||
if not assertion_violation then
|
||||
is_open_read := False
|
||||
retried := True
|
||||
retry
|
||||
end
|
||||
end
|
||||
</code>
|
||||
|
||||
==Making Features Available to Clients==
|
||||
|
||||
Remember that when we build classes in Eiffel, we keep in mind the possibility that these classes may eventually become valued, reusable software components. As a result we have a responsibility to make sure that our classes allow clients to use them in a safe and productive fashion. We make available those features that it is appropriate for clients to use, and we hide the rest. In Eiffel we say that a feature that is available to clients is "exported".
|
||||
|
||||
Control of the export of features inherited from ancestors is done by using the "export" keyword. Export of inherited features will be discussed in Inheritance.
|
||||
|
||||
Control of the export of immediate features (those features introduced in the text of a class) is done with the "feature" clause. We have encountered the feature clause already:
|
||||
<code>
|
||||
feature -- Access
|
||||
|
||||
an_attribute: INTEGER
|
||||
-- An attribute of type INTEGER
|
||||
|
||||
another_attribute: INTEGER = 46
|
||||
-- A constant attribute
|
||||
|
||||
a_function: STRING
|
||||
-- A function without arguments
|
||||
do
|
||||
Result := an_attribute.out
|
||||
end
|
||||
|
||||
another_function (an_int: INTEGER): INTEGER
|
||||
-- A function with arguments
|
||||
do
|
||||
Result := an_attribute + an_int
|
||||
end
|
||||
|
||||
feature -- Basic Operations
|
||||
|
||||
a_procedure
|
||||
-- A procedure with no arguments
|
||||
do
|
||||
an_attribute := an_attribute + 5
|
||||
end
|
||||
|
||||
another_procedure (an_int: INTEGER; another_int: INTEGER)
|
||||
-- A procedure with arguments
|
||||
do
|
||||
an_attribute := an_attribute + an_int + another_int
|
||||
end
|
||||
</code>
|
||||
|
||||
A feature clause like the ones in the example above means that all the features that follow, until the next feature clause are exported to clients based on any class. Technically, this
|
||||
<code>
|
||||
feature -- Basic Operations
|
||||
</code>
|
||||
|
||||
is equivalent to
|
||||
<code>
|
||||
feature {ANY} -- Basic Operations
|
||||
</code>
|
||||
|
||||
which means that the following features are available to clients which conform to class <code>ANY</code>. And in Eiffel, <code>ANY</code> is the class from which all other classes inherit. As a consequence all classes conform to <code>ANY</code>.
|
||||
|
||||
Inside the braces is a list of classes which are eligible as clients.
|
||||
<code>
|
||||
feature {STRING_HANDLER} -- Implementation
|
||||
</code>
|
||||
|
||||
Features following this example from class <code>STRING</code> will be available to client class <code>STRING_HANDLER</code> and all its proper descendants.
|
||||
|
||||
As stated above, you can put a list of class names in the braces:
|
||||
<code>
|
||||
feature {CLASS_A, CLASS_B, CLASS_C} -- Semi-private features
|
||||
</code>
|
||||
|
||||
Often features which are solely for implementation should not be seen or used by clients of any type. You can ensure this by exporting to class <code>NONE</code>, the class from which no other class can inherit:
|
||||
<code>
|
||||
feature {NONE} -- Implementation
|
||||
</code>
|
||||
|
||||
==Eiffel Instructions and Control Structures==
|
||||
|
||||
When you begin to write routines in Eiffel, you will need to understand how to write instructions. Fortunately, the set of instruction types you can code is fairly small. Here we will look the most common of these. You will see some more in other topics.
|
||||
|
||||
===Creation===
|
||||
<code>
|
||||
create hd_item.make
|
||||
</code>
|
||||
|
||||
We discussed creation procedures and the process of bringing new objects into being in [[Eiffel Classes|Eiffel Classes]] . A creation instruction starts with the keyword "<code>create</code>". It creates a new object, initialize its fields, may apply a creation procedure, and attaches the object to an entity.
|
||||
|
||||
===Procedure Call===
|
||||
<code>
|
||||
hd_item.set_text (a_label)
|
||||
</code>
|
||||
|
||||
The application of a feature to an object constitutes an instruction if the feature is a procedure.
|
||||
|
||||
===Assignment===
|
||||
|
||||
In Eiffel, assignment syntax is simple. But depending upon the types involved, what actually happens may need some explanation. Assume here that <code>is_open_read</code> is an attribute declared as type <code>BOOLEAN</code>:
|
||||
<code>
|
||||
is_open_read := False
|
||||
</code>
|
||||
|
||||
In this instruction, an attribute of type <code>BOOLEAN</code> named <code>is_open_read</code> is being assigned the value of the manifest boolean "<code>False</code>". The attribute <code>is_open_read</code> is based on an expanded class <code>BOOLEAN</code>, so that means that the value for <code>False</code> is held in the field for <code>is_open_read</code>. This is in contrast to what happens with reference types.
|
||||
<code>
|
||||
my_string := some_other_string
|
||||
</code>
|
||||
|
||||
In this assignment, we will assume that both entities are type <code>STRING</code>. Because <code>STRING</code> is a reference type, the field for <code>my_string</code> will hold either a reference to an instance of <code>STRING</code>, or it will be <code>Void</code>. When the assignment above executes, then whatever was in the field for <code>my_string</code> is replaced with a reference to the same object that <code>some_other_string</code> refers to.
|
||||
|
||||
In summary, for objects based on expanded classes, assignment means assignment of value. For objects based on reference classes, assignment means assignment of reference.
|
||||
|
||||
====Conformance====
|
||||
|
||||
There is an important rule about assignment in Eiffel. The rule exists to ensure type safety. Consider the following assignment which we would read as "<code>x</code> receives <code>y</code>" or "<code>x</code> gets <code>y</code>".
|
||||
<code>
|
||||
x := y
|
||||
</code>
|
||||
|
||||
then the rule says that this assignment is valid only if the type of <code>y</code> conforms to the type of <code>x</code>.
|
||||
|
||||
The rule is clear enough ... if you know what "conform" means. In the presence of mechanisms like expanded types, inheritance, and genericity, a precise definition of conformance is lengthy.
|
||||
|
||||
But we can get by for a while with much less. For now let us be satisfied with this:
|
||||
|
||||
Consider the classes in the declarations for <code>x</code> and <code>y</code>.
|
||||
|
||||
You might be tempted to say that to achieve "conformance", the classes would have to be the same. And for the case in which the declarations for <code>x</code> and <code>y</code> are expanded classes, you'd be correct. They conform only if they are the same class.
|
||||
|
||||
But for reference types, that constraint is unnecessarily restrictive. So, for reference types, the class of <code>y</code> conforms to that <code>x</code> if it is the same class or a proper descendant of that class. This facilitates a powerful idea known as polymorphic attachment that we will see more closely in the section on [[Inheritance|Inheritance]] .
|
||||
|
||||
===Conditional===
|
||||
<code>
|
||||
if l_c.is_digit then
|
||||
l_state := 2
|
||||
elseif l_c = '-' or l_c = '+' then
|
||||
l_state := 1
|
||||
else
|
||||
l_state := 3
|
||||
end
|
||||
</code>
|
||||
|
||||
Conditionals in Eiffel use at a minimum the keywords "<code>if</code>", "<code>then</code>", and "<code>end</code>". Optionally, they may use the keywords "<code>elseif</code>" and "<code>else</code>".
|
||||
|
||||
===Loop===
|
||||
<code>
|
||||
from
|
||||
i := lower
|
||||
until
|
||||
i > upper
|
||||
loop
|
||||
if item (i) /= Void and then v.is_equal (item (i)) then
|
||||
Result := Result + 1
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
</code>
|
||||
|
||||
There is only one structure for loops in Eiffel. In its typical form it uses the four keywords "<code>from</code>", "<code>until</code>", "<code>loop</code>", and "<code>end</code>". The instructions following the <code>from</code> keyword do any initialization necessary for the loop. After <code>until</code> is a boolean expression which is the exit condition. The loop body after <code>loop</code> is a list of instructions that are executed for every iteration. As you can imagine, something in the loop body should most likely cause the exit condition eventually to become true.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
[[Property:title|Design by Contract and Assertions]]
|
||||
[[Property:weight|5]]
|
||||
[[Property:uuid|41172f82-227a-96b1-2dad-624f04374ee0]]
|
||||
==Motivation: Concerning Correctness==
|
||||
|
||||
When you produce an element of software, how do you know that what you produced is correct?
|
||||
|
||||
This is a difficult question for anyone to answer. Informally speaking, correct software is software that does what it is supposed to do. That is what makes answering the question so tricky. Before you can have any idea whether the software is correct, you must be able to express what it is supposed to do ... and that proves to be quite difficult itself.
|
||||
|
||||
In conventional software engineering, a document called a software specification is written in order to describe what it is that a piece of software is supposed to do. Writers of software specifications tend to pursue one of two approaches: the informal or the formal.
|
||||
|
||||
Informal specifications attempt to describe software behavior in the natural languages with which humans communicate on a daily basis. There are problems with this approach. Natural language is not precise. Informal specifications are subject to interpretation and affected by the ambiguities, noise, and contradiction inherent in natural language.
|
||||
|
||||
In order to avoid these problems, proponents of formal methods of specification turn to the most precise language they know: mathematics. It may be no exaggeration that the study of formal methods has produced more PhD's in Computer Science than it has well-specified software systems. Still the idea that the precision of mathematics can be brought to bear on the problem of specifying software is quite appealing. But, problems lurk here as well. Formal specifications are difficult and time-consuming to construct and verify against themselves, and most software engineers do not have a working knowledge of the mathematics required to work with formal specifications.
|
||||
|
||||
There is one more significant problem with both of these approaches. Even if you have a very precise specification, expressed in elegant text and graphics, and placed carefully in an expensive ring binder, how do you know that the software product actually reflects that specification and vice versa? If either is changed, the other must be as well. This is the document synchronization problem.
|
||||
|
||||
===Design by Contract to the Rescue===
|
||||
|
||||
Design by Contract (DbC) begins as an implementation of some of the ideas from formal methods and matures into a powerful way of thinking about software. And it does it in a way that is easy for programmers and managers to understand. DbC also puts the software specification into the software document itself which makes it checkable at runtime and eliminates the document synchronization problem.
|
||||
|
||||
==Model for Software Correctness==
|
||||
|
||||
Design by Contract is built around a model for software correctness that is really pretty simple.
|
||||
|
||||
Suppose there is software routine called <code>s</code>. If we were going to test <code>s</code>, we would probably devise some test inputs or test values to be in place when <code>s</code> starts and then observe what things look like after <code>s</code> completes. If they look the way we think they should then that leads us to believe that S is working correctly for those test inputs.
|
||||
|
||||
We can generalize and formalize that process a bit, taking it back from testing an into design. If indeed we know what it means for <code>s</code> to be correct, then we should be able to make a statement of any conditions that must be true prior to executing <code>s</code>. That is, we will state the conditions required for it to be possible for <code>s</code> to run correctly. We call this statement of preconditions for success <code>s</code>'s precondition.
|
||||
|
||||
Likewise we should be able to make a statement of the conditions that will be true always if <code>s</code> works correctly. This we call <code>s</code>'s postcondition.
|
||||
|
||||
As an example, suppose <code>s</code> accepted an argument of type <code>REAL</code> and returned another <code>REAL</code> which was the square root of the argument. The precondition for <code>s</code> would be that the argument could not be less that zero, as there is no real square root for negative numbers. <code>s</code>'s postcondition would be that the result multiplied by itself would yield the value of the original argument (give or take a little to allow for floating point error).
|
||||
|
||||
==Assertions in Eiffel==
|
||||
|
||||
Each Eiffel feature which is a routine, i.e. a function or procedure, can support one assertion for a precondition and one for a postcondition. We saw where precondition and postcondition fit into the structure of the routine in [[Adding Class Features|Adding Class Features]] . An assertion is expressed as one or more assertion clauses which are logically <code>and</code>-ed together to produce the assertion. Assertions clauses are boolean expressions that evaluate to true or false.
|
||||
|
||||
Let's look at another example. Assume you need to produce a class to model a time of day. Each instance of <code>TIME_OF_DAY</code> would hold some particular time of day accurate to the second between 00:00:00 and 23:59:59 inclusive.
|
||||
|
||||
As a producer, you would be faced with a decision concerning how to maintain the time internally in each instance. For the purpose of our example, let us consider two alternatives:
|
||||
# Keep three instance of <code>INTEGER</code>. One each for hour, minute, and second.
|
||||
# Keep one instance of <code>INTEGER</code> representing the time of day as the number of seconds since 00:00:00.
|
||||
|
||||
This would be an implementation issue for the producer, because it would not affect the services that <code>TIME_OF_DAY</code> could offer clients. If we have a query called <code>minute</code> the first alternative allows us simply to provide the value from storage. Whereas the second alternative will likely cause us to compute <code>minute</code> each time it is requested. But the service looks and works the same for the client in either alternative.
|
||||
|
||||
For now let us assume that we are using the first design alternative. In that case we would code class features for <code>hour</code>, <code>minute</code>, and <code>second</code>.
|
||||
<code>
|
||||
feature -- Access
|
||||
|
||||
hour: INTEGER
|
||||
-- Hour expressed as 24-hour value
|
||||
|
||||
minute: INTEGER
|
||||
-- Minutes past the hour
|
||||
|
||||
second: INTEGER
|
||||
-- Seconds past the minute
|
||||
</code>
|
||||
|
||||
Below is the code for a procedure <code>set_second</code> which receives an argument of type <code>INTEGER</code> and sets the value of the <code>second</code> feature to the argurment.
|
||||
<code>
|
||||
set_second (s: INTEGER)
|
||||
-- Set the second from `s'.
|
||||
do
|
||||
second := s
|
||||
end
|
||||
</code>
|
||||
|
||||
The routine is simple enough, but there is a problem with it. Suppose a client calls <code>set_second</code> with an argument whose value is invalid, say 3574. Our routine would just stuff this value into <code>second</code> and we would end up with an instance of <code>TIME_OF_DAY</code> which is invalid. In the days before Design by Contract, as soon as we recognized that this problem exists, we would go into "defensive programming" mode and code some "<code>if</code>" statements inside the routine to validate the argument, before acting.
|
||||
|
||||
Consider though what we can do with Design by Contract. We will add a precondition assertion to <code>set_second</code> that expresses the need for valid arguments.
|
||||
<code>
|
||||
set_second (s: INTEGER)
|
||||
-- Set the second from `s'.
|
||||
require
|
||||
valid_argument_for_second: 0 <= s and s <= 59
|
||||
do
|
||||
second := s
|
||||
end
|
||||
</code>
|
||||
|
||||
The precondition is introduced by the keyword "<code>require</code>". The text "<code>valid_argument_for_second</code>" is the label for the assertion clause. The boolean expression "<code>0 <= s and s <= 59</code>" says that a good value for <code>s</code> will be between 0 and 59 inclusive.
|
||||
|
||||
Remember that the precondition specifies those things that must be true if <code>set_second</code> has a chance of working correctly. As such, upon execution, the body of this routine will never be executed if an attempt is made to call it in a state that does not meet its precondition. Instead, the caller will incur a precondition violation exception. We will investigate more about what exceptions mean further in [[Exception Mechanism|Exception Mechanism]] .
|
||||
|
||||
So, what about a postcondition? We noted earlier that a postcondition should make a statement of what will be true in the case that the routine does its work correctly. For <code>set_second</code> this means that after it finishes, the query <code>second</code> should have the same value as the argument that was received from the caller. Below is the feature with the postcondition added.
|
||||
<code>
|
||||
set_second (s: INTEGER)
|
||||
-- Set the second from `s'.
|
||||
require
|
||||
valid_argument_for_second: 0 <= s and s <= 59
|
||||
do
|
||||
second := s
|
||||
ensure
|
||||
second_set: second = s
|
||||
end
|
||||
</code>
|
||||
|
||||
The postcondition is introduced by the keyword "<code>ensure</code>". Here the expression "<code>second = s</code>" makes certain that the routine did actually do the necessary work to ensure that the value of <code>second</code> matches the value of the argument received.
|
||||
|
||||
As you look at the postcondition, you may be tempted to think that the one-line body of the routine is so simple as to make the postconditon unnecessary. To answer this concern we need to look again for a moment at software specification.
|
||||
|
||||
===Specification of a Routine===
|
||||
|
||||
If we remove the instructions from a routine and leave its signature, header comment and assertions, we have a specification for the routine.
|
||||
<code>
|
||||
set_second (s: INTEGER)
|
||||
-- Set the second from `s'.
|
||||
require
|
||||
valid_argument_for_second: 0 <= s and s <= 59
|
||||
ensure
|
||||
second_set: second = s
|
||||
end
|
||||
</code>
|
||||
|
||||
This specification of <code>set_second</code> tells us what is required of reuse consumers if they wish to use <code>set_second</code> and what <code>set_second</code> promises to do for them. Importantly, it does that without revealing how it does it does what it does.
|
||||
|
||||
So, this specification view, officially called the contract view, is how consumers of class <code>TIME_OF_DAY</code> would view the feature.
|
||||
|
||||
If this is the specification, then haven't we put the cart before the horse? The answer is yes. We have done so to illustrate the problems that assertion-based specification can help solve.
|
||||
|
||||
Instead of starting with a routine and adding a specification, we really want to start in accepted software engineering fashion with specification first, and then add implementation. Therefore, the specification you see above would exist before the implementation.
|
||||
|
||||
Now back to the concern over whether the postcondition is redundant for this simple one-line routine. Obviously, if this specification exists first, then the postcondition must be there, and it would be silly to remove it later. But, more importantly, suppose that when the producer of the class decided on an implementation, he or she chose the second design alternative we mentioned above. This would mean that the internal state of an instance of <code>TIME_OF_DAY</code> would be only one <code>INTEGER</code> with the number of seconds since midnight. That would mean that the query <code>second</code> would probably be a function that computed seconds from that one <code>INTEGER</code> instead of an attribute.
|
||||
|
||||
What would change in the specification of <code>set_second</code>? Nothing. The implementation for the routine would be more complex, but what it does, setting the second in an instance of <code>TIME_OF_DAY</code>, would stay the same.
|
||||
|
||||
In summary, the precondition and postcondition ensure for use that the routine will only execute if called in a state in which the precondition is true, and then will either complete in a state in which the postcondition is true, or cause a postcondition violation exception.
|
||||
|
||||
===The Contract for a Routine===
|
||||
|
||||
Having assertions on its routines forms a contract between the <code>TIME_OF_DAY</code> class and all potential reuse consumers. The contract is much like a contract in business, with obligations and benefits for both parties.
|
||||
* The client's benefits are outlined in the postcondition.
|
||||
* The client's obligations come from the precondition.
|
||||
* The supplier's obligations are in the postcondition.
|
||||
* The supplier's benefits come from the precondition.
|
||||
|
||||
We can see the specifics of these by using <code>set_second</code> as an example.
|
||||
* The client gets the desired value for seconds set in the instance.
|
||||
* The client must provide an argument that is valid for seconds.
|
||||
* The supplier must update the instance successfully.
|
||||
* The supplier need not risk disaster attempting to process in an invalid state nor waste time validating the argument received from the client.
|
||||
|
||||
===Valid State for Instances===
|
||||
|
||||
Assertions on <code>TIME_OF_DAY</code>'s routines gives us specification for each routine and guarantees that if the specification of a routine gets violated at runtime we will be served immediately with an exception. This will go a long way toward preventing invalid instances from going unnoticed in a running system and fouling up lots of other stuff.
|
||||
|
||||
What will help even more is something called a class invariant. Using the class invariant we are able to state what it means for an instance of a class to be valid, or as it is sometimes put, in a stable state.
|
||||
|
||||
The class invariant is an assertion like precondition and postcondition, but there is only one per class. Check [[Eiffel Classes|Eiffel Classes]] to see how the class invariant fits into the class structure.
|
||||
|
||||
How would we code the class invariant for <code>TIME_OF_DAY</code>? An instance would be valid if its hour were between 0 and 23 inclusive, minute were between 0 and 59 inclusive, and its second were between 0 and 59 inclusive. We can code the invariant as shown below.
|
||||
<code>
|
||||
invariant
|
||||
hour_valid: 0 <= hour and hour <= 23
|
||||
minute_valid: 0 <= minute and minute <= 59
|
||||
second_valid: 0 <= second and second <= 59
|
||||
</code>
|
||||
|
||||
The name invariant implies that the assertion can never be false ... and that's true up to a point. It's really more like, "it must be true at times when it really counts".
|
||||
|
||||
At runtime the invariant must be true for an instance at anytime that the instance is available to clients. In general, this means that the invariant must be true before and after the execution of every exported routine.
|
||||
|
||||
As with the assertions on routines, if ever the invariant is not true when it should be, then a class invariant violation occurs.
|
||||
|
||||
Remember in the example above, that the features <code>hour</code>, <code>minute</code>, and <code>second</code> are queries, but they could be either attributes or functions.
|
||||
|
||||
==The Contract for a Class==
|
||||
|
||||
Earlier we saw the contract for a routine. Now we can define the contract for a class as the aggregation of the contracts for all its exported features, plus its class invariant.
|
||||
|
||||
In Design by Contract we design based on these contracts. They are the specifications for the modules in our system. We work in a reuse-oriented world. Whenever we produce a class, we produce it with a comprehensive contract which serves as its specification. We build each class with the thought that it may eventually become reusable.
|
||||
|
||||
When we are in our reuse consumer role, using existing classes, we tend not to look at the implementations for the classes we use. Instead we look at their contract views. It is there that we find the obligations and benefits of using each class.
|
||||
|
||||
==Contracts and Debugging==
|
||||
|
||||
We saw earlier that having contracts in the code tends to expose bugs at an early stage of development. It is possible selectively to turn off and on the runtime checking of assertions by changing project settings. Checking assertions does involve processing. More about turn off assertion checking in a moment.
|
||||
|
||||
Having contracts on a class gives another advantage when the contract gets broken. The contract tells us whose fault it is. Whenever there is a violation of a precondition, postcondition, or class invariant then the software is out of specification. This situation is called a defect, or bug.
|
||||
|
||||
Whose fault is it? If a precondition was violated, then a client class attempted to call a routine in a supplier, but made the call in a state that did not satisfy the supplier's precondition. Therefore the bug is in the client.
|
||||
|
||||
If a postcondition was violated, then a client made a call in a state that did satisfy the supplier's precondition, but the supplier was unable to complete the work as agreed. Therefore the fault lies with the supplier.
|
||||
|
||||
If a class invariant was violated, then the instance has been placed in an invalid state during the execution of a routine, and left that way when the processing completed. This caused the invariant violation. As with the postcondition violation, because the problem occurred while executing routines in the supplier, preconditions must have been met. The supplier then is to blame.
|
||||
|
||||
Based on this knowledge, we can say that it is most practical first to turn off runtime checking of postconditions and invariants as we gain confidence in a class. Meaning of course, that we feel confident that any calls that meet preconditions will be properly processed. Then our only worry is that some deranged client will, with total disregard for our carefully crafted preconditions, make calls to our routines from invalid states. So, maybe we will leave precondition checking turned on for a while.
|
||||
|
||||
==Contracts and Inheritance==
|
||||
|
||||
In the section titled [[Inheritance|Inheritance]] you saw that it was possible through inheritance to produce a new class that has all the features of an existing one. This is a very powerful notion, and could be dangerous. What would keep descendants from redefining inherited features with semantics that were totally different from those intended by the producer of the original class? Nothing if it were not for the contract.
|
||||
|
||||
Simply speaking assertions on a parent class, preconditions, postconditions, and class invariants, all are inherited by the class's proper descendants.
|
||||
|
||||
For class invariants, if any new invariants are coded in an heir, they will be added to those inherited from the parent, using a non-strict version of logical "and" (We will define non-strict booleans in Writing Assertions below).
|
||||
|
||||
That is simple enough. And the situation is also simple for effective routines inherited and left unchanged ... the contracts stand as written.
|
||||
|
||||
From our example above you may have gotten the idea that contracts are really useful only for effective routines. Such is not the case. In fact, specifying a contract on a deferred routine is really a powerful notion. It says not only that effective descendants must provide an implementation for this routine, but also that there is a contract that must be satisfied. Effecting or redefining a routine in a descendant class will not make the contract go away. Here is an feature from the Base Library deferred class <code>ACTIVE</code> which models data structures with a current item, and is an ancestor to many effective container type classes.
|
||||
<code>
|
||||
feature -- Element change
|
||||
|
||||
replace (v: G)
|
||||
-- Replace current item by `v'.
|
||||
require
|
||||
writable: writable
|
||||
deferred
|
||||
ensure
|
||||
item_replaced: item = v
|
||||
end
|
||||
</code>
|
||||
|
||||
Feature <code>replace</code> carries the semantics necessary for replacing an item in an <code>ACTIVE</code>. It does not, however provide an implementation. All implementers must produce versions of <code>replace</code> that satisfy the contract specified here.
|
||||
|
||||
It actually is possible to alter a feature assertion in an effected or redefined version(technically its a replacement of the original version):
|
||||
* The precondition can only become weaker than in the inherited contract.
|
||||
* The postcondition can only become stronger than in the inherited contract.
|
||||
|
||||
These rules are imposed as a consequence of the effect of effected or redefined routines on polymorphism and dynamic binding. But, you can understand them from an intuitive viewpoint, if you reconsider the business contract analogy. Suppose a contractor makes a deal with a client to do certain work (represented by the postcondition). Part of the deal might be that the client agrees to have a site ready by a certain date (represented by the precondition). The contractor represents the parent class in the analogy. Now suppose the contractor brings in a subcontractor (representing the heir class) to do a portion of the work. The subcontractor cannot force the client to change the date that the site is to be ready to an earlier date (no strengthing of the precondition). The deal with the client was made by the contractor and so no new or stronger requirements can be imposed by the subcontractor. Likewise the subcontractor must provide at least as much work as was bargained for by the contractor, but may promise to provide more if appropriate (strengthing of postcondition is allowed.)
|
||||
|
||||
In Writing Assertions below you will see the syntax for weaking preconditions and strengthening postconditions.
|
||||
|
||||
==Unfinished Business==
|
||||
|
||||
In the section [[Adding Class Features|Adding Class Features]] , we promised to explain two issues during this discussion of Design by Contract.
|
||||
|
||||
===Short Routines===
|
||||
|
||||
One of these is the tendency of mature Eiffel programmers to write routines that are quite short. It should be clear by now that we wish to build a contract on each routine. The contract describes the semantics of the routine in a declarative fashion. In other words, it tells what the routine does, without giving an indication of how it does it.
|
||||
|
||||
Try to imagine giving a declarative description of a routine that was 50 lines long. This could get ugly. So experienced Eiffel developers decompose complex computations into chunks small enough to be described through a clear, concise set of assertions.
|
||||
|
||||
===Command/Query Separation===
|
||||
|
||||
In [[Adding Class Features|Adding Class Features]] , we saw that we can categorize features as either queries or commands. A query will ask a question or make an observation about an instance. A command will tell the instance to take some action which may result in changing the instances internal state.
|
||||
|
||||
We said that when program routines, that those routines should either be commands or queries but not both. Importantly, asking a question about an instance should not change the instance. Likewise, taking an action that changes the state of an instance should not return a result.
|
||||
|
||||
Here's the rationale. In a routines postcondition we use boolean expressions to ensure that the routine has done its job properly. Likewise, class invariants, which define the valid state for instances, are written as boolean expressions. In both cases we may use the features of the class which are queries to ask about an instances current state.
|
||||
|
||||
If a query that we use in an assertion were to change the state of the instance, then the result we received would be invalid as soon as we received it.
|
||||
|
||||
Therein lies the primary reasoning behind command/query separation. You cannot reason about the integrity of an object if the act of asking a question changes the object.
|
||||
|
||||
==Writing Assertions==
|
||||
|
||||
You have seen fairly typical assertions written in the examples above. Study the classes in the libraries to see some excellent working examples. There are a couple of things that need to be covered.
|
||||
|
||||
===Non-Strict Booleans===
|
||||
|
||||
One is that, as you can probably imagine, it is not a good thing to cause an exception during the process of checking an assertion. One of the most common ways to cause such an exception is to apply a feature to a Void reference.
|
||||
|
||||
The way to avoid this is to use the non-strict booleans "<code>and then</code>" and "<code>or else</code>". These forms of "<code>and</code>" and "<code>or</code>" do not force the checking of all conditions. As soon as a determination can be made, they stop checking. It is typical to see "<code>and then</code>" used to avoid applying a feature to a void reference in preconditons. Below is a creation procedure that uses a non-strict boolean in its precondition.
|
||||
<code>
|
||||
make (a_nm: STRING; a_offset: INTEGER)
|
||||
-- Initalize with name `a_nm' and utcoffset `a_offset'.
|
||||
require
|
||||
name_not_empty: a_nm /= Void and then not a_nm.is_empty
|
||||
offset_valid: a_offset >= -12 and a_offset <= 12
|
||||
do
|
||||
name := a_nm.twin
|
||||
utcoffset := a_offset
|
||||
ensure
|
||||
name_initialized: name.is_equal (a_nm)
|
||||
utcoffset_initialized: utcoffset = a_offset
|
||||
end
|
||||
</code>
|
||||
|
||||
===Replacing Inherited Feature Assertions===
|
||||
|
||||
To replace a precondition on a feature you are effecting or redefining, you use the "<code>require else</code>" keywords to introduce new conditions. These conditions will be logically "<code>or</code>-ed" with the original precondition to form an new one.
|
||||
|
||||
Likewise use "<code>and then</code>" to add conditions to a postcondition. The added conditions will be "<code>and</code>-ed" to the original.
|
||||
|
||||
Below is an example of weakening a precondition. The first feature shown is from class <code>DYNAMIC_CHAIN</code> in the Base Library.
|
||||
<code>
|
||||
remove_left
|
||||
-- Remove item to the left of cursor position.
|
||||
-- Do not move cursor.
|
||||
require
|
||||
left_exists: index > 1
|
||||
deferred
|
||||
ensure
|
||||
new_count: count = old count - 1
|
||||
new_index: index = old index - 1
|
||||
end
|
||||
</code>
|
||||
|
||||
The next feature is from <code>DYNAMIC_LIST</code>, a proper descendant of <code>DYNAMIC_CHAIN</code>. <code>DYNAMIC_LIST</code> weakens the precondition it inherited from <code>DYNAMIC_CHAIN</code>. Originally in <code>DYNAMIC_CHAIN</code>, "<code>index > 1</code>" was required for <code>remove_left</code>. In <code>DYNAMIC_LIST</code> either "<code>index > 1</code>" or "<code>not before</code>" (or both) will suffice.
|
||||
<code>
|
||||
remove_left
|
||||
-- Remove item to the left of cursor position.
|
||||
-- Do not move cursor.
|
||||
require else
|
||||
not_before: not before
|
||||
deferred
|
||||
end
|
||||
</code>
|
||||
|
||||
==Not Writing Assertions==
|
||||
|
||||
Let's close this discussion of Design by Contract with one more interesting and point to make about assertions. The precondition and postcondition parts of a routine are optional, as you may remember from [[Adding Class Features| Adding Class Features]] . Suppose you write a routine and do not code either precondition or postcondition. You might be tempted to think that you have simply written a routine that has no contract. But, that would not be the case.
|
||||
|
||||
The contract exists, even though you do not code it explicitly. If it were written out, it would look as follows.
|
||||
<code>
|
||||
my_routine
|
||||
-- My descriptive header comment
|
||||
require
|
||||
True
|
||||
ensure
|
||||
True
|
||||
end
|
||||
</code>
|
||||
|
||||
What does this mean? It means that you have selected the weakest possible precondition and postcondition for your routine. Of course, this may be perfectly valid under some circumstances.
|
||||
|
||||
Just understand that if your routine could speak, it would be telling you, "I can always work successfully without any particular guarantees from you at all. On the other hand, I won't promise you any particular results when I get done."
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
[[Property:title|Eiffel Classes]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|f905426e-3109-b60b-adf0-c74b83c81b55]]
|
||||
The unit of software reuse in Eiffel is the class.
|
||||
|
||||
The unit of modularity in Eiffel is the class.
|
||||
|
||||
The unit of type modeling in Eiffel is the class.
|
||||
|
||||
All Eiffel code must exist within the context of a class.
|
||||
|
||||
In Eiffel, application systems, or simply systems, are created by assembling a set of related classes. The classes in a system will be related only by one or both of the two allowable relationships in object-oriented design.
|
||||
|
||||
Having read the above, you should be convinced that the concept of class is important and far-reaching. The fact that we have precise rules about classes simplifies life a lot. The only kind of module in Eiffel is a class. Each class exists in one source file (which contains only that class), and contains the code necessary to provide a static definition of a data type. Every runtime entity, i.e. every object, must be an instance of a class. Because we can depend upon these things in Eiffel, we have consistency and predictabililty in the inherently complex world of software development.
|
||||
|
||||
Let's take a look at how classes are structured.
|
||||
|
||||
The code that makes up an Eiffel class is divided into the following parts:
|
||||
|
||||
==Structure of a Class==
|
||||
|
||||
All of the above, except Class header, are optional. So the simplest Eiffel class you could build would look like this:
|
||||
<code>
|
||||
class
|
||||
SIMPLE
|
||||
end
|
||||
</code>
|
||||
|
||||
Okay, so class SIMPLE is only interesting in its simplicity. Let's look at an example that is more illustrative:
|
||||
<code>
|
||||
note
|
||||
description: Objects that model lists
|
||||
revision: $Revision: 1.4 $
|
||||
|
||||
class
|
||||
OLD_FASHIONED_LIST [G]
|
||||
|
||||
obsolete "This class is obsolete, use LINKED_LIST [G] instead"
|
||||
|
||||
inherit
|
||||
DYNAMIC_LIST [G]
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
-- Create an empty list.
|
||||
do
|
||||
before := True
|
||||
ensure
|
||||
is_before: before
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
item: G
|
||||
-- Current item
|
||||
do
|
||||
Result := active.item
|
||||
end
|
||||
|
||||
first: like item
|
||||
-- Item at first position
|
||||
do
|
||||
Result := first_element.item
|
||||
end
|
||||
|
||||
... other features omitted ...
|
||||
|
||||
invariant
|
||||
before_constraint: before implies (active = first_element)
|
||||
after_constraint: after implies (active = last_element)
|
||||
</code>
|
||||
Here is a class that, although completely contrived, utilizes all of the required and optional parts of the class. Let's look at each part individually.
|
||||
===Note===
|
||||
<code>
|
||||
note
|
||||
description: Objects that model lists
|
||||
revision: $Revision: 1.4 $
|
||||
</code>
|
||||
|
||||
The <code>note</code> part of a class is there to allow you as a producer to record information of your choice which will help you or other reuse consumers at some later time to locate understand the class. This important in Eiffel because we try to treat every class as if someday it will become reusable.
|
||||
|
||||
Information in <code>note</code> does not change the semantics of the class.
|
||||
|
||||
The <code>note</code> part in the class above is typical. It is introduced with the language keyword <code>note</code>, and contains two note clauses, each of which is comprised of an index and a single index value. You can code note clauses with indexes that you devise yourself, so there is nothing inherently special about "<code>description</code>" and "<code>revision</code>" as used above. But, these indexes could be special to tools which analyze libraries of classes use them. Although these clauses have only one index value each, it is permissible to put more, separated by commas.
|
||||
|
||||
===Class Header===
|
||||
<code>
|
||||
class
|
||||
OLD_FASHIONED_LIST [G]
|
||||
</code>
|
||||
|
||||
The class header is introduced by the keyword "class", which in turn can be preceded by one of three keywords which mark the class as <code>deferred</code>, <code>expanded</code>, or <code>frozen</code>. In our example, the class has none of these markings, so it is an effective class whose instances are access by reference.
|
||||
|
||||
The keyword class is followed by the class name, in this case "<code>OLD_FASHIONED_LIST</code>".
|
||||
|
||||
Of the three keywords for header marks, the one which you will encounter most often is <code>deferred</code>. A class is deferred if it contains one or more features that are deferred, that is, features which have been specified in the class but for which no implementation has been provided. Proper descendants of a deferred class will provide implementations for its deferred features.
|
||||
|
||||
===Formal Generics===
|
||||
<code>
|
||||
class
|
||||
OLD_FASHIONED_LIST [G]
|
||||
</code>
|
||||
|
||||
In this example the class name is followed by the specification of one formal generic parameter "<code>G</code>". The presence of one or more formal generic parameters will designate a class as a generic class. The formal generic parameter is a place holder for a class name which will be provided by reuse consumers. For example if we wrote a class which was a client to <code>OLD_FASHIONED_LIST</code> we would substitute the class name for the type of objects that we would want to build an <code>OLD_FASHIONED_LIST</code> of. We might make this declaration:
|
||||
<code>
|
||||
my_list_of_cats: OLD_FASHION_LIST [CAT]
|
||||
</code>
|
||||
|
||||
The entity <code>my_list_of_cats</code> could then be attached at runtime to an <code>OLD_FASHIONED_LIST</code> of objects of type <code>CAT</code>. So the class <code>CAT</code> becomes an actual generic parameter and substitutes for <code>G</code> in the declaration.
|
||||
|
||||
Of course formal generic parameters cannot be the same name as a class name in the same universe. If multiple formal generic parameters are used, they are separated by commas.
|
||||
|
||||
You will learn more about generic classes in the section titled [[Genericity|Genericity]] .
|
||||
|
||||
===Obsolete===
|
||||
<code>
|
||||
obsolete "This class is obsolete, use LINKED_LIST [G] instead"
|
||||
</code>
|
||||
|
||||
<code>OLD_FASHION_LIST</code>s are obsolete ... and the class is marked as such by include the line above. The manifest string contains an explanation, instructions, and/or recommended alternatives. Compilers and other language tools can deliver this message to potential reuse consumers. As with <code>note</code>, <code>obsolete</code> has no effect on the semantics of the class.
|
||||
|
||||
Obsolete is rarely used because of the nature of certain elements of the Eiffel methodology. For example, if implementations are well-hidden behind implementation-independent specifications, then those implementations may be changed to adapt the class to changing execution environments in such a way that clients are unaffected.
|
||||
|
||||
===Inheritance===
|
||||
<code>
|
||||
inherit
|
||||
DYNAMIC_LIST [G]
|
||||
</code>
|
||||
|
||||
One of the two possible relationships between classes, inheritance is also a powerful software reuse mechanism. In this example class <code>OLD_FASHIONED_LIST</code> declares itself to be a proper descendant of class <code>DYNAMIC_LIST</code>.
|
||||
|
||||
There will be more in the section called . For now though, be aware of two important implications of this declaration:
|
||||
* Every feature of <code>DYNAMIC_LIST</code> is available to <code>OLD_FASHIONED_LIST</code> and potentially available to its clients.
|
||||
* Whenever an instance of <code>DYNAMIC_LIST</code> is called for, then an instance of <code>OLD_FASHIONED_LIST</code> will suffice.
|
||||
|
||||
===Creators===
|
||||
<code>
|
||||
create
|
||||
make
|
||||
</code>
|
||||
|
||||
The creators part of a class declares a procedure as being a creation procedure. In this case the procedure in question is the one named <code>make</code>. By convention, creation procedure names begin with the word " <code>make</code>".
|
||||
|
||||
Let's take a quick look at object creation. Consider this declaration:
|
||||
<code>
|
||||
my_list_of_cats: OLD_FASHION_LIST [CAT]
|
||||
</code>
|
||||
|
||||
Here the entity <code>my_list_of_cats</code> can be attached to an object of type <code>OLD_FASHION_LIST [CAT]</code> at runtime. The process of converting <code>my_list_of_cats</code> from holding a void reference to holding a reference to a object modeling a list of cats, starts when a creation instruction is executed. The creation instruction creates the instance and may apply a creation procedure to initialize the instance. A creation instruction for the declaration above would look like this:
|
||||
<code>
|
||||
create my_list_of_cats.make
|
||||
</code>
|
||||
|
||||
The <code>create</code> keyword is used to introduce a creation instruction. This instruction causes the following four things to happen:
|
||||
* A shell of a new instance of <code>OLD_FASHION_LIST [CAT]</code> is created in memory with a memory field for every attribute
|
||||
* Each field is initialized with standard default values
|
||||
** False for type <code>BOOLEAN</code>
|
||||
** Null character for type <code>CHARACTER</code>
|
||||
** The appropriate form of zero for number types
|
||||
** <code>Void</code> for reference types
|
||||
|
||||
* Attach the new instance to the entity <code>my_list_of_cats</code>
|
||||
* Apply the creation procedure <code>make</code>
|
||||
|
||||
Once these steps complete successfully, <code>my_list_of_cats</code> will be attached to a valid instance (i.e., an instance in which the class invariant is true) of <code>OLD_FASHIONED_LIST [CAT]</code>.
|
||||
|
||||
===Features===
|
||||
<code>
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
-- Create an empty list.
|
||||
do
|
||||
before := True
|
||||
ensure
|
||||
is_before: before
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
item: G
|
||||
-- Current item
|
||||
do
|
||||
Result := active.item
|
||||
end
|
||||
|
||||
first: like item
|
||||
-- Item at first position
|
||||
do
|
||||
Result := first_element.item
|
||||
end
|
||||
</code>
|
||||
|
||||
The features part of a class is the area in which we feel that most of the "programming" is done. It is here that we define those things that instances of a class have and can do. We will learn more about features in the next section [[Adding Class Features|Adding Class Features]] .
|
||||
|
||||
Until then let's just take a quick look at how features fit into a class. Notice that in our example the features part is introduced by the keyword "<code>feature</code>". In fact there are two occurrences of <code>feature</code> in this example, each followed by a comment.
|
||||
|
||||
You may declare multiple <code>feature</code> statements. This helps you group features in a manner that makes sense. Here we see the first group contains those features which are listed as creation procedures in the creators part of the class. The second group of features labeled "<code>Access</code>" contains a set of queries available to clients of the class.
|
||||
|
||||
Although the words "<code>Initialization</code>" and "<code>Access</code>" are actually in comments after the <code>feature</code> keyword, some language processing tools apply some significance to these, for example, ordering the groups in "pretty-printed" views of a class. Also, some tools allow you to build templates for creating new classes which have <code>feature</code> clauses already in place for predetermined groups.
|
||||
|
||||
{{tip|There is not a technical requirement governing the grouping or ordering of features in a class. It is the option of the producer of a class to group and order the features in a fashion that holds some meaning. Many years of Eiffel development experience are reflected in the classes in the EiffelBase Library. This is a good place to look for examples of well constructed classes. }}
|
||||
|
||||
|
||||
===Invariant===
|
||||
<code>
|
||||
invariant
|
||||
before_constraint: before implies (active = first_element)
|
||||
after_constraint: after implies (active = last_element)
|
||||
</code>
|
||||
|
||||
|
||||
Here's the last word in a class definition ... both literally and figuratively. The invariant part, introduced not surprisingly by the keyword "<code>invariant</code>", is that portion of the class in which we can state what it means for an object to be a valid instance of this class.
|
||||
|
||||
We will learn more about class invariants in the section titled [[Design by Contract and Assertions|Design by Contract and Assertions]] .
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
[[Property:title|Eiffel for .NET Compliance]]
|
||||
[[Property:weight|9]]
|
||||
[[Property:uuid|1e19c2f0-995e-02c1-0588-c134a11e0003]]
|
||||
As of Eiffel for ASP.NET 5.6 and EiffelStudio 5.7; Eiffel for .NET introduces the notion of '''Eiffel-Compliance'''. .NET specifies a number of language interoperability rules in a [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconwhatiscommonlanguagespecification.asp Common Language Specification-Compliance] specification. Eiffel for .NET supports all CLS-Compliant type and features in .NET but now additionally supports a number of non-CLS-compliant types and features. This is the purpose of the Eiffel-Compliant notion.
|
||||
|
||||
The information contained within this page does not go into any depth on the Common Language Specification (CLS) or CLS-Compliance. For this information please see Microsoft's on-line documentation [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconwhatiscommonlanguagespecification.asp What is the Common Language Specification?]
|
||||
|
||||
<span id="applicablity"></span>
|
||||
==Applicability==
|
||||
|
||||
The CLS states a number of rules .NET tools have to abide by to be CLS-compliant. These rules relate to both a .NET producer, generally a compiler generating .NET code, and a consumer, which is generally a compiler that consumes .NET assemblies for reuse. Eiffel-Compliance relates only to consumption of .NET assemblies.
|
||||
<span id="what_is_compliant"></span>
|
||||
==What is Eiffel-Compliant?==
|
||||
|
||||
As already stated, anything CLS-compliant is Eiffel-compliant. Eiffel-Compliancy merely allows for exceptions so that non-CLS-compliant assemblies, types and members can be used in Eiffel for .NET.
|
||||
|
||||
The following list outlines the supported non-CLS-compliant types:
|
||||
* All unsigned numerical basic types such as <eiffel>System.UInt32</eiffel> as <eiffel>System.UInt64</eiffel>, represented by <eiffel>NATURAL_xx</eiffel>.
|
||||
* Native pointers (<eiffel>System.IntPtr</eiffel>), represented by <eiffel>POINTER</eiffel>
|
||||
|
||||
|
||||
Typically assemblies, types or members are marked with the <eiffel>System.CLSCompliantAttribute</eiffel> attribute to explicitly indicate if they are CLS-compliant or not. The Eiffel for .NET compiler will now ignore this attribute and instead infer if they are Eiffel-Compliant. With such an inference engine at hand, Eiffel for .NET opens itself up to extended support for COM/Legacy interop as well support assemblies not adhering to CLS-compliant rules, for one reason or another.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
[[Property:title|Event Programming with Agents]]
|
||||
[[Property:weight|7]]
|
||||
[[Property:uuid|ea9d7b9d-daa1-a415-628a-e3a2f935c12a]]
|
||||
In Eiffel there is a facility referred to as agents.
|
||||
|
||||
The implementation of agents is an advanced topic, but you do not have to understand the details of the implementation of agents to put agents to work for you. That is what you will learn in this section.
|
||||
|
||||
==Objects that Represent Operations==
|
||||
|
||||
Object technology is based on the idea that when we model systems based on objects, representing the "things" they manipulate. As to operations on these objects, they appear in the corresponding classes, as routines (functions and procedures). Operations are not objects.
|
||||
|
||||
Sometimes, on the other hand, the "things" we model with our objects could represent operations. For example, we might want to build a list of tasks to be performed later; each task is defined by a routine. Each of the objects in the list will represent the corresponding routine.
|
||||
|
||||
Such an object, representing an operation, is called an agent.
|
||||
|
||||
If we can have a run-time object that represents an operation, then we can place the object in the structure of another object, where at some later time, a client can cause the associated operation to execute.
|
||||
|
||||
This is a very desirable model for event driven processing, like graphical user interfaces. The operations that are executed when a user take some action like clicking on a button, could be represented by agents. When the user interface element is initialized, agents that represent the action routines are stored within the interface element. Then at the time that an event, say a button click, occurs, the agents for that event are retrieved and their associated operations are executed.
|
||||
|
||||
Another area in which agents are commonly used is in traversing data structures. Many of the data structure classes in the Base Library include routines which take agents as there arguments. For example, the feature <code>do_all</code> takes an agent which represents some procedure and will apply the procedure to every item in the structure.
|
||||
|
||||
==Classes to Model Operations==
|
||||
|
||||
We know that there are two types of routines in Eiffel, functions and procedures.
|
||||
|
||||
The implementation of agents correspondingly relies on three classes in the Base Library: class <code>ROUTINE</code> for the general notion, and its heirs <code>FUNCTION</code>, with and <code>PROCEDURE</code>. In addition, <code>PREDICATE</code>, an heir of <code>FUNCTION</code> , covers the particular case of a function returning a boolean result.
|
||||
|
||||
When you use an agent from a client routine, you will be building an instance of either <code>FUNCTION</code> or <code>ROUTINE</code>.
|
||||
|
||||
==Using Agents==
|
||||
|
||||
Below is an instruction which passes an agent as an argument to a procedure.
|
||||
<code>
|
||||
button.select_actions.extend (agent gauge.step_forward)
|
||||
</code>
|
||||
|
||||
In this example, the producer wants to add the action of stepping the gauge forward in the event that a button is clicked. The keyword "<code>agent</code>" is used to indicate that at runtime an object of type <code>PROCEDURE</code> should be created which represents applying the feature <code>step_forward</code> to the object attached to <code>gauge</code>. It is the object of type <code>PROCEDURE</code> that is passed as the argument.
|
||||
|
||||
It is important to understand that <code>step_forward</code> does not get applied at the point that the instruction above is executed. Rather the procedure object that represents <code>step_forward</code> is given to the button to hold in reserve. Then at the point that the button click event takes place, the button will go through its list of <code>select_actions</code> executing their associated routines. Only then does <code>step_forward</code>get applied to <code>gauge</code>.
|
||||
|
||||
===Agents with Arguments===
|
||||
|
||||
In this example, the routine "<code>step_forward</code>" on which the agent is based takes no arguments. If you drilled down into the workings of this example you would find that class that implements the feature <code>extend</code> is class <code>EV_NOTIFY_ACTION_SEQUENCE</code>. You would also see that the signature for the feature <code>extend</code> is as essentially as follows.
|
||||
<code>
|
||||
extend (v: PROCEDURE [TUPLE])
|
||||
</code>
|
||||
|
||||
We don't have to know too much about the workings of agents to see that "<code>extend</code>" takes an argument <code>v</code> which is of type <code>PROCEDURE</code>. The actual generic parameter <code>TUPLE</code> represents the set of "open" arguments. In this case, <code>extend</code> is expecting an agent with no open arguments.
|
||||
|
||||
===Open and Closed Arguments===
|
||||
|
||||
It is this business of open and closed arguments which really makes agents remarkable. To get a feel for it, let's simplify the example some. Instead of considering an agent passed as an argument let's look at it as a simple assignment within a class.
|
||||
|
||||
Suppose a class has a feature declared as shown below.
|
||||
<code>
|
||||
my_procedure: PROCEDURE [TUPLE]
|
||||
</code>
|
||||
|
||||
Then what can be assigned to <code>my_procedure</code>?. An agent, of course. Say the class has procedures as follows.
|
||||
<code>
|
||||
no_argument_procedure
|
||||
-- A procedure with no arguments
|
||||
do
|
||||
print ("No argument here!%N")
|
||||
end
|
||||
|
||||
two_argument_procedure (an_int: INTEGER; another_int: INTEGER)
|
||||
-- A procedure with two arguments
|
||||
do
|
||||
print ("My arguments are: " + an_int.out + " and " + another_int.out + "%N")
|
||||
end
|
||||
</code>
|
||||
|
||||
Then the following assignment is valid.
|
||||
<code>
|
||||
my_procedure := agent no_argument_procedure
|
||||
</code>
|
||||
|
||||
What this means is that the agent created and associated with the procedure <code>no_argument_procedure</code> must conform to the type <code>PROCEDURE [TUPLE]</code>. The feature <code>my_procedure</code> (which is of type <code>PROCEDURE [TUPLE]</code>) can be attached at runtime to an agent representing a procedure with no open arguments, which indeed is what <code>no_argument_procedure</code> is.
|
||||
|
||||
Now let's turn our attention to the other procedure <code>two_argument_procedure</code>. You might think that because it takes two arguments, that you would not be able to build an agent from it which could be assigned to the attribute <code>my_procedure</code>. But you can do it by closing the two arguments at the time that the agent is created, as in the following.
|
||||
<code>
|
||||
my_procedure := agent two_argument_procedure (1, 2) -- Is Valid
|
||||
</code>
|
||||
|
||||
What happens here is that values are fixed for those arguments at the time that the agent, an object of type <code>PROCEDURE [ TUPLE]</code> is created.
|
||||
|
||||
So this is the wonderful thing about agents. A routine which will be represented as an agent does not have to be an exact fit for the expected signature. By closing some arguments at agent creation, you have effectively produced a new and conforming routine.
|
||||
|
||||
The advantage of this is that you can sometimes avoid building specialized routines for the sole purpose of having a routine which conforms to the agent signature.
|
||||
|
||||
To leave an argument open, you hold its place with a question mark. If you intend for all arguments to be open, then you may make them all question marks, or leave off the arguments entirely.
|
||||
<code>
|
||||
my_procedure := agent two_argument_procedure (?, 2) -- Argument 1 left open
|
||||
my_procedure := agent two_argument_procedure (?, ?) -- Both arguments left open
|
||||
my_procedure := agent two_argument_procedure -- Both arguments left open
|
||||
</code>
|
||||
|
||||
If an argument is open, then it means that a value is not provided for that argument at the time that the agent is created. The implication is that the value must be provided at some time prior to the time that the agent's associated routine gets executed. A precondition to executing a routine associated with an agent is that the agent has a valid set of arguments (called operands within the <code>ROUTINE</code> classes) for the call. If you were to leave one or both of the arguments to <code>two_argument_procedure</code> open as in the examples above, the assignment would still work due to the rules governing <code>TUPLE</code> conformance. But, at runtime unless the other arguments had been provided, the "<code>valid operands</code>" precondition would be violated.
|
||||
|
||||
Let's see an example in which we leave a target open. Suppose we have a class that has a feature coded as below
|
||||
<code>
|
||||
my_strings: LINKED_LIST [STRING]
|
||||
</code>
|
||||
|
||||
and some code to put some strings in <code>my_strings</code>:
|
||||
<code>
|
||||
create my_things.make
|
||||
my_strings.extend ("Hello")
|
||||
my_strings.extend ("World!")
|
||||
</code>
|
||||
|
||||
Our class also has a feature called <code>print_on_new_line</code> which we created to print a string preceded by a new line character.
|
||||
<code>
|
||||
print_on_new_line (s: STRING)
|
||||
-- Print `s' preceded by a new line
|
||||
do
|
||||
print ("%N" + s)
|
||||
end
|
||||
</code>
|
||||
|
||||
Now suppose we want to print the values of all the strings in <code>my_strings</code> each on a separate line by invoking <code>print_on_new_line</code>. Traditionally, we would do it by traversing the <code>LINKED_LIST</code> and printing each item. Like this:
|
||||
<code>
|
||||
from
|
||||
my_list.start
|
||||
until
|
||||
my_list.exhausted
|
||||
loop
|
||||
print_on_new_line (my_list.item)
|
||||
my_list.forth
|
||||
end
|
||||
</code>
|
||||
|
||||
The availability of agents gives us new options. <code>LINKED_LIST</code> has a feature <code>do_all</code> which comes to it from its ancestor <code>LINEAR</code>. The <code>do_all</code> feature's signature looks like this:
|
||||
<code>
|
||||
do_all (action: PROCEDURE [TUPLE [G]])
|
||||
</code>
|
||||
|
||||
As an argument <code>do_all</code> takes an agent based on a procedure with one open argument which is the same type as the list items (in this class, <code>G</code> is the formal generic parameter representing the type of the items being stored). Then it traverses the list executing the routine associated with that agent and uses the current list item to satisfy the open argument.
|
||||
|
||||
Instead of coding the loop shown above, we can code this instruction:
|
||||
<code>
|
||||
my_list.do_all (agent print_on_new_line (?))
|
||||
</code>
|
||||
|
||||
we leave open the argument required by <code>print</code>, and <code>do_all</code> will provide it as a reference to the current list item as it traverses the list.
|
||||
|
||||
===Targets for Agents' Routines===
|
||||
|
||||
In Eiffel every routine must be applied against a target object. In our model for computation, <code>x.f (a, ...)</code>, the <code>x</code> is the target of the application of feature <code>f</code>. In the case of an agent, the agent must account for objects for each of the arguments and an object for the target of the routine.
|
||||
|
||||
Let's identify the targets in the examples shown. First:
|
||||
<code>
|
||||
button.select_actions.extend (agent gauge.step_forward)
|
||||
</code>
|
||||
|
||||
Here the target is the object attached to the entity "gauge" which is (although you cannot determine it from this line taken out of context) an object of type <code>EV_GAUGE</code>.
|
||||
|
||||
How about this:
|
||||
<code>
|
||||
my_procedure := agent two_argument_procedure (1, 2)
|
||||
</code>
|
||||
|
||||
Here, since there was no qualification, then the target is the current instance. Same with this:
|
||||
<code>
|
||||
my_list.do_all (agent print_on_new_line (?))
|
||||
</code>
|
||||
|
||||
Again, consider the fact that the agent must account for objects for each of the arguments to a routine, and an object for the target. So, in the examples we've seen so far, the target is close, that is provided at the time of the creation of the agent.
|
||||
|
||||
But we can actually leave the target open as well. Now we cannot use the question mark notation to do that, because if we did, there would be no way to know of which class the routine is a feature. So instead, we mark an open target with the class name in braces.
|
||||
|
||||
Suppose in our list of strings example, we wanted to print the strings, then convert them to lower case, then print them again. Remember that "do_all" has one open argument, which will be provided as the current list item during the traversal.
|
||||
<code>
|
||||
my_list.do_all (agent print_on_new_line (?))
|
||||
my_list.do_all (agent {STRING}.to_lower)
|
||||
my_list.do_all (agent print_on_new_line (?))
|
||||
</code>
|
||||
|
||||
In between printing the list two times, we provide <code>do_all</code> with an agent that representing the <code>STRING</code> class's feature <code>to_lower</code> which will convert each string in the list to lower case. Notice that <code>to_lower</code> does not take an argument of type <code>STRING</code> as <code>print_on_new_line</code> did. Rather it gets applied to an instance of <code>STRING</code>, so it is targeted to a string. So we leave its target open and <code>do_all</code> provides the current list item as the target.
|
||||
|
||||
Agents for Functions
|
||||
|
||||
So far all the agents that we have coded have created instances of <code>PROCEDURE</code>. But functions are routines and can be represented as agents as well. The difference is that functions have a return value.
|
||||
|
||||
Let's extend the string example by using an agent that represents a function. Suppose we wanted to print only those strings which contain a particular character, say the exclamation point.
|
||||
|
||||
Here again we'll use a feature of the <code>LINKED_LIST</code> class. There is a feature called <code>do_if</code> which takes two agents as arguments. One is an action procedure like the argument that <code>do_all</code> takes, and the other is a function which returns a boolean and used as a test. As each list item is current, the test is applied first. If the result is true, then the action is applied with the current item.
|
||||
<code>
|
||||
my_list.do_if (agent print_on_new_line(?), agent {STRING}.has('!'))
|
||||
</code>
|
||||
|
||||
|
||||
The agent for the action is the same as we used earlier. We've added an agent for the test. It represents applying the <code>has</code> feature of the <code>STRING</code> class. Here the target is left open, because we want each of the strings in the list to be the target of <code>has</code>.
|
||||
|
||||
|
||||
Compatibility note
|
||||
|
||||
Versions of the Kernel Library classes ROUTINE, PROCEDURE, FUNCTION and PREDICATE prior to EiffelStudio 17-05 had an extra generic parameter at the initial position; the usual actual generic parameter was ANY. It has been removed. The compiler has been engineered so that in almost all cases it will still accept the old style.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
[[Property:title|Exception Mechanism]]
|
||||
[[Property:weight|6]]
|
||||
[[Property:uuid|84a159dd-4a19-af73-7b0b-0618a284142a]]
|
||||
==Motivation: Concerning Robustness==
|
||||
|
||||
The notion of software correctness that we saw in [[Design by Contract and Assertions|Design by Contract and Assertions]] is half of the formula for software reliability. Correctness covers what the software is supposed to do, that is, its specification.
|
||||
|
||||
Of course,there is always a potential for things to go out of the bounds of the specification. This happens, for exampleif a client makes a call to a routine from a state in which the routine's precondition is not true.
|
||||
|
||||
How well software responds to situations which outside specification is called robustness. Together correctness and robustness define software reliability.
|
||||
|
||||
==Consequences of Contracts==
|
||||
|
||||
In the presence of Design by Contract, what happens to running software boils down to a simple rule:
|
||||
|
||||
A routine callcan complete in one of only two ways:
|
||||
# The routine fulfills its contract.
|
||||
# The routine fails to fulfill its contract.
|
||||
|
||||
As a follow-on, we can add that:
|
||||
* Any routine that fails to fulfill its contract must cause an exception in its caller.
|
||||
|
||||
==Reacting to Exceptions==
|
||||
|
||||
Again, because of Design by Contract, we can state the following rule for dealing with exceptions:
|
||||
|
||||
A routine that incurs an exeception can react in one of only two ways:
|
||||
# It can return the instance to a stable state and retry the entire routine with the same or a different strategy.
|
||||
# It can fail, causing an exception in its caller.
|
||||
|
||||
There is an Eiffel mechanism called the rescue clause which facilitates the first alternative.
|
||||
|
||||
===The Rescue Clause===
|
||||
|
||||
The <code>rescue</code> clause is part of the routine structure we saw in [[Adding Class Features|Adding Class Features]] . The rescue clause is a sequence of instructions introduced by the keyword "<code>rescue</code>". At the point that an exception occurs, the processing of the normal instructions in the routine body will cease, and the instructions in the rescue clause will be executed instead.
|
||||
|
||||
If the instructions in the rescue clause can set things up so that it might prove fruitful to attempt to retry the routine, it can do so by issuing a <code>retry</code> instruction. When the <code>retry</code> instruction is executed, the routine is restarted from its beginning, although local entities are not re-initialized. You will see why in the example below.
|
||||
|
||||
If the rescue clause exits without issuing a <code>retry</code> instruction, then the routine fails.
|
||||
|
||||
It should be noted that rescue clauses and retry instructions are not something that are used commonly. Out of the approximately 2000 classes in the delivered Eiffel libraries, there are only 16 occurrences. Many of these are oriented toward network and database operations for which some reasonable recovery might be possible.
|
||||
<code>
|
||||
transmit: (p: PACKET)
|
||||
-- Transmit packet `p'
|
||||
require
|
||||
packet_not_void: p /= Void
|
||||
local
|
||||
current_retries: INTEGER
|
||||
r: RANDOM_NUMBER_GENERATOR
|
||||
do
|
||||
line.send (p)
|
||||
rescue
|
||||
if current_retries < max_retries then
|
||||
r.next
|
||||
wait_millisecs (r.value_between (20, 500))
|
||||
current_retries := current_retries + 1
|
||||
retry
|
||||
end
|
||||
end
|
||||
</code>
|
||||
|
||||
In the example above, <code>rescue</code> is used to recover from a situation in which an exception occurs in trying to send a packet. When the exception occurs the rescue clause will, if the maximum number of retries has not been reached, wait for some random length of time. Then, after having updated the number of retries, it will issue the <code>retry</code> instruction. If the maximum number of retries is reached, the rescue clause will exit without executing the retry, constituting a failure.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
[[Property:title|Genericity]]
|
||||
[[Property:weight|4]]
|
||||
[[Property:uuid|3a0bfe10-78e7-00eb-d9a0-b977d1fa352a]]
|
||||
We got a very short introduction to generic classes when we were looking at the formal generic part of class structure in [[Eiffel Classes|Eiffel Classes]] . That discussion left to the imagination the motivation and benefits for creating generic classes.
|
||||
|
||||
You will see that most of the generic classes model containers for multiple items and at least one of their formal generic parameters represents the type of items that are to be stored in the container. Some generic classes, like <code>LINKABLE</code>, care for only one instance of the type represented by their formal generic parameter.
|
||||
|
||||
==Motivation==
|
||||
|
||||
Imagine that a software producer is planning to build a class which would represent a list of things. Someone might ask "What kinds of things?" To which the producer would reply, "Just things. I want my list to be usable for all kinds of things."
|
||||
|
||||
Using the idea of polymorphic attachment that we learned in [[Inheritance|Inheritance]] , the producer could build such a class. It might have a query <code>item</code> which would return the thing from the list to which a cursor currently points. It might have a command <code>put</code> which would enter some new thing into the list.
|
||||
|
||||
What would be the type of <code>item</code>? And what would be the type of the argument to <code>put</code>?
|
||||
|
||||
If the producer wants the class to handle all kinds of things, then the answer must be class <code>ANY</code>, the class from which all others inherit.
|
||||
<code>
|
||||
class
|
||||
LIST_OF_THINGS
|
||||
...
|
||||
feature -- Access
|
||||
|
||||
item: ANY
|
||||
-- The thing currently pointed to by cursor
|
||||
...
|
||||
feature -- Element change
|
||||
|
||||
put (new_item: ANY)
|
||||
-- Add `new_item' at the end of the list
|
||||
...
|
||||
</code>
|
||||
|
||||
This will work, but has some definite disadvantages. Suppose you choose to use this class to maintain a list of cats in one of your classes. You might make this declaration:
|
||||
<code>
|
||||
my_cats: LIST_OF_THINGS
|
||||
-- A list of my cats
|
||||
</code>
|
||||
|
||||
Then you could add individual instances to the list:
|
||||
<code>
|
||||
fluffy, twinkie: CAT
|
||||
...
|
||||
my_cats.put (fluffy)
|
||||
my_cats.put (twinkie)
|
||||
</code>
|
||||
|
||||
One problem with this type of list is that the type system will not help you keep from doing something pathological like:
|
||||
<code>
|
||||
fluffy, twinkie: CAT
|
||||
thor: PSYCHOTIC_HYDROPHOBIC_CAT_HATING_DOG
|
||||
-- A very nasty dog
|
||||
...
|
||||
my_cats.put (fluffy)
|
||||
my_cats.put (twinkie)
|
||||
my_cats.put (thor)
|
||||
</code>
|
||||
|
||||
Another problem is that to do any <code>CAT</code> things with an item in the list, you must reattach it to a <code>CAT</code> entity. The following is invalid.
|
||||
<code>
|
||||
my_cats.item.purr -- Is invalid
|
||||
</code>
|
||||
|
||||
This is because "item" is type <code>ANY</code> and although it may be currently attached to an instance of <code>CAT</code>, the static typing system cannot guarantee that. So you must use an object test as we saw in the polymorphism example in [[Inheritance|Inheritance]] .
|
||||
<code>
|
||||
...
|
||||
if attached my_cats.item as some_cat then
|
||||
some_cat.purr
|
||||
end
|
||||
</code>
|
||||
|
||||
You can see that this type of list has its drawbacks. Of course you could build a <code>LIST_OF_CATS</code> class in which <code>item</code> and the argument for <code>put</code> would be of type <code>CAT</code>. This would let you <code>purr</code> a cat without pulling it out of the list, and it would also prevent you from accidently letting old Thor in with the cats. But, every time you needed a list to hold a different type of object, you have to write a new class.
|
||||
|
||||
Indeed, this is how things are done in environments without facilities genericity.
|
||||
|
||||
What we would like to have is a way to produce the text of the list class once. Then only when we make declarations do we add the additional information about the particular types we want allowed in the list.
|
||||
|
||||
==Basic Genericity==
|
||||
|
||||
In Eiffel this is accomplished through generic classes. Generic classes are written relative not to a specific class but to a kind of phony class name called a formal generic parameter. With genericity, the <code>LIST_OF_THINGS</code> class might become a class called <code>LIST</code> which is a list of items of type <code>G</code>. In class <code>LIST</code> we would declare <code>item</code> as type G, as well as the argument to <code>put</code>.
|
||||
<code>
|
||||
class
|
||||
LIST [G]
|
||||
...
|
||||
feature -- Access
|
||||
item: G
|
||||
-- The item currently pointed to by cursor
|
||||
...
|
||||
feature -- Element change
|
||||
put (new_item: G)
|
||||
-- Add `new_item' at the end of the list
|
||||
...
|
||||
</code>
|
||||
|
||||
We could declare feature <code>my_cats</code> as a <code>LIST</code> of items of type <code>CAT</code>. By doing so we are providing <code>CAT</code> as an "actual generic parameter" in the declaration. Then we are free to treat the features of <code>LIST</code> as if the class name <code>CAT</code> had been substituted for every occurrence of the formal generic parameter <code>G</code>.
|
||||
<code>
|
||||
my_cats: LIST [CAT]
|
||||
-- A list of my cats
|
||||
fluffy, twinkie: CAT
|
||||
...
|
||||
my_cats.put (fluffy)
|
||||
my_cats.put (twinkie)
|
||||
...
|
||||
my_cats.item.purr -- Valid now
|
||||
</code>
|
||||
|
||||
The following would no longer be valid:
|
||||
<code>
|
||||
my_cats: LIST [CAT]
|
||||
-- A list of my cats
|
||||
|
||||
thor: PSYCHOTIC_HYDROPHOBIC_CAT_HATING_DOG
|
||||
...
|
||||
my_cats.put (thor) -- Is invalid
|
||||
</code>
|
||||
|
||||
==Constrained Genericity==
|
||||
|
||||
The generic class <code>LIST</code> illustrated above is perfectly useful for making typed lists of any type of object. The features of the <code>LIST</code> will not attempt to use the objects in the list in any way. Sometimes though, it is important for a class to be guaranteed more about the nature of the types that can be substituted for its formal generic parameter.
|
||||
|
||||
Take for example the case of a class called <code>SORTED_LIST</code>. A <code>SORTED_LIST</code> is a list, of course, but it is special in that it acts upon the elements that it holds to keep them in order.
|
||||
|
||||
A <code>SORTED_LIST</code> needs to be able to order its elements. So, it must be able to apply queries to those elements to determine which should sort high than which. The elements themselves must respond to ordering operations.
|
||||
|
||||
If <code>SORTED_LIST</code> were defined like we did <code>LIST</code>
|
||||
<code>
|
||||
class
|
||||
SORTED_LIST [G]
|
||||
</code>
|
||||
|
||||
there would be no guarantee that ordering operations, like "<" and ">" could be applied to all types that could be listed. An Eiffel facility called "constrained genericity" will solve this problemfor us. In the case of <code>SORTED_LIST</code>, we would add to the formal generic part as follows.
|
||||
<code>
|
||||
class
|
||||
SORTED_LIST [G -> COMPARABLE]
|
||||
</code>
|
||||
|
||||
You may remember from [[Inheritance|Inheritance]] that if we make instances of a class comparable with each other, then we make the class inherit from <code>COMPARABLE</code> and effect the feature "<".
|
||||
|
||||
Here, constrained genericity does two things for us.
|
||||
* First, it states that any candidate for substitution for <code>G</code> must conform to class <code>COMPARABLE</code>. Typically this means it must inherit from <code>COMPARABLE</code>.
|
||||
* Second, it allows, within the features of <code>SORTED_LIST</code>, the features of <code>COMPARABLE</code> to be applied to any item which has a type of <code>G</code>.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
[[Property:title|Eiffel for .NET]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|446038d8-abd6-0a0d-2b90-94124e1ac810]]
|
||||
These pages contain documentation that describes the Eiffel programming language.
|
||||
|
||||
Eiffel is considered both a development methodology and a programming language.
|
||||
|
||||
Eiffel the methodology is a recipe for constructing quality software which is based on a small number of powerful concepts. Eiffel the programming language is a notation which is designed to support the methodology.
|
||||
|
||||
Eiffel for.NET is the Eiffel programming language made available in the .NET environment. Eiffel for.NET components can use and inherit from components in .NET assemblies that were produced using other.NET languages. And, programmers using other.NET languages can use and inherit from components in assemblies produced using Eiffel for.NET.
|
||||
|
||||
==Eiffel for.NET Conventions Documentation==
|
||||
|
||||
In Eiffel, as in other languages, certain conventions have evolved which constitute "the way things are done". Conventions provide consistency and therefore predictability and readability of software text.
|
||||
|
||||
When working in the.NET components, particularly when using components from .NET assemblies delivered from Microsoft or produced by other parties, the conventions of Eiffel are maintained in the view of these components. The first section, titled [[Conventions]], provides you with an understanding of Eiffel terminology, conventions , and how they work with .NET.
|
||||
|
||||
The remaining sections of this section are a reference for the Eiffel for .NET language. It is not a complete reference, though. Rather, it is intended to help you with the tasks that Eiffel programmers commonly encounter.
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
[[Property:modification_date|Sat, 12 Jan 2019 18:39:06 GMT]]
|
||||
[[Property:publication_date|Sat, 12 Jan 2019 18:34:23 GMT]]
|
||||
[[Property:title|Inheritance]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:uuid|7e4cb7ba-fda6-8eac-3e27-bbb8fafd8673]]
|
||||
Inheritance, along with client/supplier, are the two relationships that can exist between classes.
|
||||
|
||||
Inheritance lets us mirror in software the types of abstractions that are common in many problem domains, i.e., the more general to the more specialized.
|
||||
|
||||
Inheritance also gives a way us to combine these abstractions.
|
||||
|
||||
Inheritance allows us to make extensions and adaptations to existing software, while at the same time, leaving the original software unaltered.
|
||||
|
||||
==The Eiffel Inheritance Model==
|
||||
|
||||
If class <code>B</code> inherits from class <code>A</code>, then:
|
||||
* Every feature of <code>A</code> is also a feature of <code>B</code>
|
||||
* In any case in which an instance of <code>A</code> is called for, then an instance of <code>B</code> will suffice.
|
||||
|
||||
Flexibility and adaptability are key qualities of the Eiffel inheritance model. On an informal level, this means that, except as prevented by certain constraints, a class can inherit from a set of classes containing just about any other classes.
|
||||
|
||||
Eiffel classes can be effective or deferred. If a class is effective, then it is completely implemented. As a result, it is possible to create and use direct instances of an effective class at runtime.
|
||||
|
||||
If a class is deferred, then it is not completely implemented. A class is deferred if it contains at least one deferred feature. So, it is possible for you to mark a feature (and by consequence also its class) as deferred when you code it. This means that the specification for this class dictates that such a feature exists, but there is no implementation for the feature included in the class. As a result, there can be no direct instances of deferred classes at runtime. However, a class that inherits from a deferred class can implement, or effect, the deferred features. This results in an effective descendant to the deferred class. And it is possible to create direct instances of this effective descendant. Such instances would also be instances (albeit not direct instances) of the original deferred class.
|
||||
|
||||
What this means to us as software producers, is that in any development effort, we have available a great number of classes which can serve as potential starting points. That is, classes that we could make parents to the classes we produce. And, those classes do not have to chosen from a strict dichotomy of classes which are either completely abstract or completely implemented. Inheritance from classes that are deferred but have some implemented features is both possible and encouraged. It reuses existing software and it reduces the opportunity for error.
|
||||
|
||||
Consider the deferred class <code>COMPARABLE</code> from the Eiffel Base Library. A portion of <code>COMPARABLE</code> is shown below:
|
||||
<code>
|
||||
deferred class
|
||||
COMPARABLE
|
||||
|
||||
feature -- Comparison
|
||||
|
||||
is_less alias "<" (other: like Current): BOOLEAN
|
||||
-- Is current object less than `other'?
|
||||
deferred
|
||||
end
|
||||
|
||||
is_less_equal alias "<=" (other: like Current): BOOLEAN
|
||||
-- Is current object less than or equal to `other'?
|
||||
do
|
||||
Result := not (other < Current)
|
||||
end
|
||||
|
||||
is_greater alias ">" (other: like Current): BOOLEAN
|
||||
-- Is current object greater than `other'?
|
||||
do
|
||||
Result := other < Current
|
||||
end
|
||||
|
||||
is_greater_equal alias ">=" (other: like Current): BOOLEAN
|
||||
-- Is current object greater than or equal to `other'?
|
||||
do
|
||||
Result := not (Current < other)
|
||||
end
|
||||
|
||||
is_equal (other: like Current): BOOLEAN
|
||||
-- Is `other' attached to an object of the same type
|
||||
-- as current object and identical to it?
|
||||
do
|
||||
Result := (not (Current < other) and not (other < Current))
|
||||
end
|
||||
</code>
|
||||
|
||||
If you are producing a class that you wish to support basic comparison operators, like "<" and ">", you can have that class inherit from <code>COMPARABLE</code>, which has features which correspond to those operators. The text for <code>COMPARABLE</code> contains eight features. Seven of these are effective and one is deferred.
|
||||
|
||||
So through inheritance from <code>COMPARABLE</code>, your class, let's call it <code>WHATZIT</code>, would now have these features available. But how would the features of <code>COMPARABLE</code> know what it means to compare <code>WHATZIT</code>s?
|
||||
|
||||
Of course, it would have no way of knowing, so you must show it. And you do that by writing the implementation for "<", the one deferred feature that <code>WHATZIT</code> inherits from the <code>COMPARABLE</code> class.
|
||||
|
||||
When you look closely at the effective features of <code>COMPARABLE</code>, you see that their implementations are ultimately based on "<". If we were not able to inherit from multiple partially implemented classes, then we would be forced to implement many more features, a process which invites error, or, in the case of comparison, to move to a less appealing model.
|
||||
|
||||
==The Inheritance Part of Classes in Eiffel==
|
||||
|
||||
Because the inheritance model has such flexibility, it must also have adaptability. A consequence of inheriting from multiple classes is that it would be possible to inherit multiple features with the same name ... and you remember from [[Adding Class Features|Adding Class Features]] that a class is not allowed to have more than one feature with the same name. A process called feature adaptation allows us to resolve these issues in an heir. Feature adaptation is also done for reasons other than resolving name clashes as well.
|
||||
|
||||
Feature adaptation is an enabling capability, but it is also one that takes some study to understand fully.
|
||||
|
||||
We will look at the types of feature adaptation that will serve most useful to you as you begin to produce Eiffel software.
|
||||
|
||||
In [[Eiffel Classes|Eiffel Classes]] you saw where the inheritance part fits into the class structure. Shown below is a portion of class <code>LINKED_QUEUE</code> from the Eiffel libraries. <code>LINKED_QUEUE</code> is an effective class which implements the abstract notion of a <code>QUEUE</code> (a deferred class) with an implementation based on the services provided by <code>LINKED_LIST</code> (an effective class).
|
||||
<code>
|
||||
class
|
||||
LINKED_QUEUE [G]
|
||||
inherit
|
||||
QUEUE [G]
|
||||
undefine
|
||||
is_empty,
|
||||
copy,
|
||||
is_equal
|
||||
redefine
|
||||
linear_representation,
|
||||
prune_all,
|
||||
extend
|
||||
select
|
||||
item,
|
||||
put
|
||||
end
|
||||
LINKED_LIST [G]
|
||||
rename
|
||||
item as ll_item,
|
||||
remove as ll_remove,
|
||||
make as ll_make,
|
||||
remove_left as remove,
|
||||
put as ll_put
|
||||
export
|
||||
{NONE}
|
||||
all
|
||||
{ANY}
|
||||
writable,
|
||||
extendible,
|
||||
wipe_out,
|
||||
readable
|
||||
undefine
|
||||
fill,
|
||||
append,
|
||||
prune,
|
||||
readable,
|
||||
writable,
|
||||
prune_all,
|
||||
extend,
|
||||
force,
|
||||
is_inserted
|
||||
redefine
|
||||
duplicate,
|
||||
linear_representation
|
||||
select
|
||||
remove
|
||||
end
|
||||
</code>
|
||||
|
||||
Okay ... now calm down ... please. This is an example from a very highly-evolved and sophisticated library which is replete with software reuse. <code>LINKED_QUEUE</code> has two parents and uses considerable feature adaptation. In fact, it uses every feature adaptation option available. The benefit is obvious, though. <code>LINKED_QUEUE</code> class has only seven features actually coded. In total there are only 26 lines of instructions!
|
||||
|
||||
In practice you can use inheritance, even multiple inheritance, to do some quite productive programming in Eiffel without having to write anything that looks like the inheritance part of <code>LINKED_QUEUE</code> above.
|
||||
|
||||
Regardless, let's break <code>LINKED_QUEUE</code>'s inheritance part into chunks and take a look at some of them.
|
||||
|
||||
===Rename===
|
||||
<code>
|
||||
rename
|
||||
item as ll_item,
|
||||
remove as ll_remove,
|
||||
make as ll_make,
|
||||
remove_left as remove,
|
||||
put as ll_put
|
||||
</code>
|
||||
|
||||
As you might have already guessed, the rename part, introduced oddly enough by the keyword "<code>rename</code>", is used to rename features.
|
||||
|
||||
Specifically, it is used when an heir wants to use a feature from a parent, but wants to use it under a different name than that by which the parent knows it. So in the example, the feature known as <code>item</code> in <code>LINKED_LIST</code> is perfectly usable in <code>LINKED_QUEUE</code>, but must be applied as <code>ll_item</code>.
|
||||
|
||||
This is common when your class inherits two different features with the same name from two different parents and you want to be able to use them both. Because you can only have one feature with a given name, then rename one of the features.
|
||||
|
||||
===New Exports===
|
||||
<code>
|
||||
export
|
||||
{NONE}
|
||||
all
|
||||
{ANY}
|
||||
writable,
|
||||
extendible,
|
||||
wipe_out,
|
||||
readable
|
||||
</code>
|
||||
|
||||
The new exports part is introduced by the keyword "<code>export</code>". This section allows you to change the export status of inherited features. Remember from [[Adding Class Features|Adding Class Features]] that features become available (or not) to clients by their export status. Export status of immediate features is controlled in the feature clause. But here we are dealing with inherited features, so we control their status in the export part of the class's inheritance section. Any feature not mentioned will have the same export status as it did in the parent class.
|
||||
|
||||
In this example, the keyword "<code>all</code>" is used first to say that all features inherited form <code>LINKED_LIST</code> are unavailable to any clients (export to class <code>NONE</code>). This is typical for a class like <code>LINKED_QUEUE</code> in which the features important to the client come from the deferred parent, in this case <code>QUEUE</code>, and the class <code>LINKED_LIST</code> is used only for implementation. But, it seems that also in this case, the producer felt differently about the features <code>writable</code>, <code>extendible</code>, <code>wipe_out</code>, and <code>readable</code>, and decided the allow clients of <code>ANY</code> type to utilize these features inherited from <code>LINKED_LIST</code>.
|
||||
|
||||
===Undefine===
|
||||
<code>
|
||||
undefine
|
||||
is_empty,
|
||||
copy,
|
||||
is_equal
|
||||
</code>
|
||||
|
||||
Next, undefine ... it's probably not what you think. You might assume that undefine is a way to banish forever any inherited features that you just do not want to deal with. But what happens to features whose names are listed in an undefine clause is that they become deferred features in the heir.
|
||||
|
||||
Undefine is useful if you inherit two different features of the same name from different parents, a situation you cannot live with. If you like one and you don't like the other, then you can undefine the one you don't like. The the only version you get is the one you like.
|
||||
|
||||
Another way you might use undefine is in the case in which you actually want a feature to be deferred in an heir that was effective in a parent.
|
||||
|
||||
===Redefine===
|
||||
<code>
|
||||
redefine
|
||||
linear_representation,
|
||||
prune_all,
|
||||
extend
|
||||
</code>
|
||||
|
||||
The redefine part lists the names of effective features for which the producer of the heir class would like to provide implementations that replace the inherited implementations.
|
||||
|
||||
So, in this example the implementation for <code>linear_representation</code>, for example, that <code>LINKED_QUEUE</code> would have inherited from <code>QUEUE</code> will not be used. Instead <code>LINKED_QUEUE</code> implements its own version of <code>linear_representation</code>.
|
||||
|
||||
{{note|When a class implements a version of an inherited feature which was deferred in its parent, this is known as "effecting" the feature. Because features being effected are getting their first implementation, it is not necessary to list their names in the redefine part, or anywhere else in the inheritance part of the heir. }}
|
||||
|
||||
===Select===
|
||||
<code>
|
||||
select
|
||||
remove
|
||||
</code>
|
||||
|
||||
The select part is used only under special circumstances. The case in which select is required involves a situation called "repeated" inheritance. Repeated inheritance occurs when an heir inherits more than once from the same ancestor. Usually this means it has two or more parents who have a common proper ancestor (but it can occur directly). The features from the common ancestor are inherited by each of the parents and passed on to the heir. The rules and effects of repeated inheritance occupy an entire chapter in the official Eiffel programming language reference and will not be reproduced here. Just understand at this point that it is sometimes necessary to use <code>select</code> to provide the dynamic binding system with an unambiguous choice of features in the presence of polymorphic attachment.
|
||||
|
||||
You should note also that repeated inheritance can and does occur often without causing any problem at all. In fact it happens in every case of multiple inheritance, due to the fact that all classes inherit from class ANY and receive its features as a result. The reason it is not a problem is that in the case that any feature makes it from the original common ancestor along multiple paths to the heir with its name and implementation still intact, it will arrive as only one feature heir. This is called sharing and nothing special needs to be done to make it happen.
|
||||
|
||||
==Polymorphism==
|
||||
|
||||
It is time now to see another way in which inheritance helps build more extendible software.
|
||||
|
||||
Assume that we have to build classes that model different types of polygons. We would do this by building a class for polygon which would model a garden-variety polygon, a multi-sided closed figure. But when we consider that there are specialized types of polygons, like triangles and rectangles, we realize that to support these specializations, we need classes for them as well. And this is an obvious opportunity for inheritance. All triangles and rectangles are polygons. So, we start with class <code>POLYGON</code> and its proper descendants <code>TRIANGLE</code> and <code>RECTANGLE</code>.
|
||||
|
||||
So we can make declarations like:
|
||||
<code>
|
||||
my_polygon: POLYGON
|
||||
your_polygon: POLYGON
|
||||
my_triangle: TRIANGLE
|
||||
my_rectangle: RECTANGLE
|
||||
another_rectangle: RECTANGLE
|
||||
</code>
|
||||
|
||||
Assume these declarations are in force for all the examples this section on polymorphism.
|
||||
|
||||
We saw in [[Adding Class Features|Adding Class Features]] that we can say that one class conforms to another if it is the same class or one of its proper descendants. Therefore POLYGON conforms to <code>POLYGON</code>. Also, <code>TRIANGLE</code> and <code>RECTANGLE</code> conform to <code>POLYGON</code>. But, importantly, <code>POLYGON</code> does not conform to <code>TRIANGLE</code> or <code>RECTANGLE</code>. This makes sense intuitively, because we know all rectangles and triangles are polygons ... and we also know that not all polygons are rectangles.
|
||||
|
||||
===Polymorphic Attachment===
|
||||
|
||||
These facts affect how assignments can work. Using the declarations above:
|
||||
<code>
|
||||
my_polygon := your_polygon -- Is valid
|
||||
your_polygon :=my_polygon -- Is valid
|
||||
my_polygon :=my_rectangle -- Is valid
|
||||
my_polygon := my_triangle -- Is valid
|
||||
</code>
|
||||
|
||||
but
|
||||
<code>
|
||||
my_rectangle := my_polygon -- Is not valid
|
||||
my_triangle := my_polygon -- Is not valid
|
||||
</code>
|
||||
|
||||
and of course
|
||||
<code>
|
||||
my_rectangle := my_triangle -- Is not valid
|
||||
</code>
|
||||
|
||||
Consider now the assignment below which is valid.
|
||||
<code>
|
||||
my_polygon := my_rectangle
|
||||
</code>
|
||||
|
||||
After an assignment like this executes the entity <code>my_polygon</code> will be holding at runtime a reference to an instance of a type which is not a direct instance of its declared type <code>POLYGON</code>. But conformance ensures us that, although it may not be a direct instance, it will indeed by an instance. (all rectangles are polygons).
|
||||
|
||||
Depending upon how many different types of polygons get modeled in classes, the entity "<code>my_polygon</code>" could be attached objects of may different types ... it could take on many forms. This in fact is the basis for the term "polymorphism"; having many forms. So we speak of "polymorphic attachment" as the process by which at runtime entities can hold references to objects which are not of the entity's declared type ... but they are of conforming types.
|
||||
|
||||
Now let's see how we get some value from this.
|
||||
|
||||
===Dynamic Binding===
|
||||
|
||||
Suppose that one of the features of <code>POLYGON</code> is a query <code>perimeter</code> which returns an instance's perimeter. The producer of <code>POLYGON</code> may have implemented <code>perimeter</code> as a function that computes the perimeter by adding up the lengths of all the sides. This approach is guaranteed to work for all polygons, and we can apply the <code>perimeter</code> feature to any polygon. Let's print some perimeters:
|
||||
<code>
|
||||
print (my_polygon.perimeter)
|
||||
print (my_triangle.perimeter)
|
||||
print (my_rectangle.perimeter)
|
||||
</code>
|
||||
|
||||
<code>TRIANGLE</code> and <code>RECTANGLE</code> might have properties, expressed as queries, which as a part of their specialization, distinguish them from run-of-the-mill polygons. Two features of rectangles are <code>width</code> and <code>height</code> the lengths of the sides.
|
||||
|
||||
Armed with these <code>RECTANGLE</code>-specific features, the producer of <code>RECTANGLE</code> may say, "Now I no longer have to depend upon that crude implementation of <code>perimeter</code> that is inherited from <code>POLYGON</code>. I can build an efficient <code>RECTANGLE</code>-specific implementation of <code>perimeter</code>, based on the knowledge that for all <code>RECTANGLE</code>s perimeter = 2*(width+height)"
|
||||
|
||||
To implement this specialized version of <code>perimeter</code>, the producer of <code>RECTANGLE</code> must add the feature to the class, but also must list its name in the "<code>redefine</code>" part of the <code>RECTANGLE</code>'s inheritance clause.
|
||||
<code>
|
||||
class
|
||||
RECTANGLE
|
||||
inherit
|
||||
POLYGON
|
||||
redefine
|
||||
perimeter
|
||||
end
|
||||
.
|
||||
.
|
||||
feature
|
||||
perimeter: REAL
|
||||
-- Sum of lengths of all sides
|
||||
do
|
||||
Result := 2 * (width + height)
|
||||
end
|
||||
</code>
|
||||
|
||||
You would expect then, that this version of perimeter would be executed in the following context:
|
||||
<code>
|
||||
print (my_rectangle.perimeter)
|
||||
</code>
|
||||
|
||||
But what makes this interesting is that even in the context below
|
||||
<code>
|
||||
my_polygon := my_rectangle
|
||||
print (my_polygon.perimeter)
|
||||
</code>
|
||||
|
||||
in which <code>perimeter</code> is being applied to a entity declared as <code>POLYGON</code>, the specialized version of <code>perimeter</code> from <code>RECTANGLE</code> is being used. It would be impossible to ensure at compile time which version of <code>perimeter</code> is most appropriate. So it must be done at runtime. This ability to choose the best version of a feature to apply, just at the moment it needs to be applied, is called "dynamic binding".
|
||||
|
||||
Static typing tells us at compile time that it is safe to apply <code>perimeter</code> to <code>my_polygon</code> No matter which of the types of polygons is attached to <code>my_polygon</code>, there will be a <code>perimeter</code> feature that will work.
|
||||
|
||||
Dynamic binding tells us that when we apply <code>perimeter</code>, we know that the most appropriate version of the feature will get applied at runtime.
|
||||
|
||||
===Object Test===
|
||||
|
||||
Now let's add another situation. Consider the code below:
|
||||
<code>
|
||||
my_polygon := my_rectangle
|
||||
print (my_polygon.perimeter)
|
||||
print (my_polygon.width) -- Is invalid
|
||||
</code>
|
||||
|
||||
We could apply <code>perimeter</code> to <code>my_polygon</code> and everything is fine ... we even get <code>RECTANGLE</code>'s specialized version of the feature. But it is invalid for us to try to apply <code>width</code> to <code>my_polygon</code> even though we feel (with rather strong conviction) that at this point in execution, <code>my_polygon</code> will be attached to an object of type <code>RECTANGLE</code>, and we know that <code>width</code> is a valid query on <code>RECTANGLE</code>s.
|
||||
|
||||
The reason follows. When we declared <code>my_polygon</code> as type <code>POLYGON</code>, we made a deal that says that the only features that can be applied to <code>my_polygon</code> are the features of <code>POLYGON</code>. Remember that static typing guarantees us at compile time that at runtime there will be at least one version of the feature available that can be applied.
|
||||
<code>
|
||||
print (my_polygon.width) -- Is invalid
|
||||
</code>
|
||||
|
||||
But in the case above, the guarantee cannot be made. <code>my_polygon</code> is declared with class <code>POLYGON</code> which has no <code>width</code> feature, despite the fact that some of its proper descendants might.
|
||||
|
||||
Does this mean that we can never do <code>RECTANGLE</code> things with this instance again, once we have attached it to <code>my_polygon</code>?
|
||||
|
||||
No. There is a language facility called the '''object test''' which will come to our rescue. The object test will allow us safely to attach our instance back to an entity typed as <code>RECTANGLE</code>. After doing so, we are free use <code>RECTANGLE</code> features.
|
||||
<code>
|
||||
my_polygon := my_rectangle
|
||||
print (my_polygon.perimeter)
|
||||
if attached {RECTANGLE} my_polygon as l_rect then
|
||||
print (l_rect.width)
|
||||
end
|
||||
</code>
|
||||
In this code, the entity <code>l_rect</code> is a fresh local entity produced during the object test. So, the code can be read: if at this point, <code>my_polygon</code> is attached to an instance of type <code>RECTANGLE</code>, then attach that instance to a fresh local entity named <code>l_rect</code>, then apply <code>width</code> to <code>l_rect</code> and print the result.
|
||||
|
||||
|
||||
:'''Note:''' The object test replaces the functionality of an obsolete mechanism called assignment attempt. Assignment attempt used the syntax '''<code>?=</code>''' in the context of assignment versus the '''<code>:=</code>''' of normal assignment.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
[[Property:title|Referenced Assembly Type and Feature Name Conversion]]
|
||||
[[Property:weight|10]]
|
||||
[[Property:uuid|5d575090-35d9-f983-7308-172b1641173f]]
|
||||
The [[The Eiffel for .NET language|Eiffel for .NET language]] is Eiffel. It is not a variant of Eiffel that spawned a new language, as is '''C#''', or a dramatic evolution of Eiffel, such as '''Visual Basic .NET''' is to '''VB6'''. As Eiffel stands today, and will probably remain, Eiffel's conventions for class names and features names match neither that of the OO Pascal variants nor the "Camel-casing" conventions recommended by Microsoft. Eiffel also does not support the notion of full quantified type names. The period (''''.'''') between namespace and type names is not valid Eiffel syntax. These naming convention rules pose a problem for maintaining the Eiffel programming language. To address this issue, when referencing .NET assemblies, all referenced assembly type names and feature names are converted into "Eiffel-case" when using Eiffel.
|
||||
<span id="eiffel_case"></span>
|
||||
==What is Eiffel-Case?==
|
||||
|
||||
Eiffel-casing is almost the same for both class and class feature names, all words are separated using underscores (''''_''''). The differences between them are class names are always in uppercase and feature names always in lowercase.
|
||||
|
||||
<eiffel>MY_CLASS</eiffel> is an example of an Eiffel-cased class name. <br/>
|
||||
<eiffel>my_feature</eiffel> is an example of an Eiffel-cased feature name.
|
||||
|
||||
There are somewhat complex rules to ensure that the automatic formatting of .NET name, type, method, property and otherwise are formatted to the most readable name possible, given their casing convention. One absolute is that the Eiffel compilation process will remove the namespace from the fully qualified type name and format just the type name.
|
||||
|
||||
For example <eiffel>System.Windows.Forms.Border3DStyle</eiffel> would yield <eiffel>BORDER_3D_STYLE</eiffel>.
|
||||
|
||||
Stripping the namespace from the name is essential to keep type names short and usable, who wants to use <eiffel>SYSTEM_WINDOWS_FORMS_BORDER_3D_STYLE</eiffel>? Dropping the namespace does present a potential problem; assemblies may have two type that are of the same name but are located in different namespaces. A resolution to this comes in the form of assembly prefixes. For every assembly a class name prefix can be specified. Eiffel Software have reserved a set of assembly prefix pairs which should not be change as they are used by the Eiffel class libraries. For example, ''System.Windows.Forms.dll'' contains a commonly used type called <eiffel>System.Windows.Forms.Form</eiffel>. During the compilation process ''System.Windows.Forms.dll'' will be evaluated and <eiffel>System.Windows.Forms.Form</eiffel> will yield the <eiffel>FORM</eiffel> Eiffel class name. ''System.Windows.Forms.dll'' has been assigned the '<eiffel>WINFORMS_</eiffel>' prefix, so <eiffel>FORM</eiffel> would actually be <eiffel>WINFORMS_FORM</eiffel> instead.
|
||||
<span id="prefixes"></span>
|
||||
==Reserved Prefixes==
|
||||
|
||||
The following table displays the fixed assembly class name prefixes, assigned by Eiffel Software, for the Microsoft Foundation Class Libraries:
|
||||
{|
|
||||
|-
|
||||
| mscorlib.dll
|
||||
| No Prefix
|
||||
|-
|
||||
| System.dll
|
||||
| <eiffel>SYSTEM_DLL_</eiffel>
|
||||
|-
|
||||
| System.XML.dll
|
||||
| <eiffel>XML_</eiffel>
|
||||
|-
|
||||
| System.Data.dll
|
||||
| <eiffel>DATA_</eiffel>
|
||||
|-
|
||||
| System.Web.dll
|
||||
| <eiffel>WEB_</eiffel>
|
||||
|-
|
||||
| System.EnterpriseServices.dll
|
||||
| <eiffel>ENTERPRISE_</eiffel>
|
||||
|-
|
||||
| System.Windows.Forms.dll
|
||||
| <eiffel>WINFORMS_</eiffel>
|
||||
|}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[[Property:title|Using Referenced Assemblies]]
|
||||
[[Property:weight|8]]
|
||||
[[Property:uuid|acbe0407-b95e-6268-fc20-238f91df595e]]
|
||||
Eiffel for.NET is a first class citizen in the Microsoft.NET programming world. This means that if you are programming in Eiffel for.NET, you have full access to the thousands of software components in the .NET type libraries. But, that's not all. You also have full access to the thousands of components in the traditional Eiffel class libraries. And even that's not all. You have the ability to build software components which comply with.NET standards, so that they can be used by programmers using any other .NET language. Still not all. When you use Eiffel, you can choose to build software that will run under Microsoft.NET, but will be portable to other popular operating systems as well.
|
||||
|
||||
Being an Eiffel for.NET programmer obviously put you in a very powerful position. How do you take advantage of it?
|
||||
|
||||
To use.NET software components from Eiffel for.NET requires you to have some understanding of both.NET and Eiffel and how their respective object models differ. If you have read the rest of the help topics in [[Eiffel for .NET|this section]] then you have a pretty good idea of what the Eiffel method and language are all about.
|
||||
|
||||
When you begin to build software in Eiffel for.NET, you will likely find a need to reuse types from the.NET libraries. These libraries are called assemblies. When you reference a assembly using Eiffel for.NET, the types in the assembly become available to you in a form that makes them look like so many Eiffel classes. The names for types and members will conform to Eiffel conventions. The same thing happens when you are programming against assemblies in Visual Basic.NET or Visual C#.NET.
|
||||
|
||||
The section called [[Conventions]] covers the details the Eiffel conventions and how the.NET types are made available to Eiffel for.NET programmers.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[[Property:title|The Eiffel for .NET language]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|ba6cd8d3-683c-4167-bdef-a0274c392f34]]
|
||||
This section focuses on defining Eiffel for .NET. The key requirement for Eiffel for .NET is the exclusive use of the common language runtime with a minimum Eiffel-specific runtime. The second requirement is to generate IL code that is CLS compliant, meaning that other CLS compliant languages/compilers/tools will be able to reuse .NET components written in Eiffel for .NET. The last requirement is to generate verifiable IL code.
|
||||
|
||||
Because not all Eiffel functionalities are present in .NET, the task of the Eiffel compiler is made more complicated since it has to emulate those mechanisms instead of reusing what .NET provides. For example the common language runtime of .NET does not support:
|
||||
* [[uuid:b8c10baa-4f50-adfe-a6f8-9cb56a8f1917#Multiple inheritance|multiple inheritance]]
|
||||
* [[uuid:b8c10baa-4f50-adfe-a6f8-9cb56a8f1917#Genericity|genericity]]
|
||||
* [[uuid:b8c10baa-4f50-adfe-a6f8-9cb56a8f1917#Covariance|covariance]]
|
||||
* agents
|
||||
|
||||
We will see:
|
||||
* what is Eiffel on .NET
|
||||
* which Eiffel mechanisms have been implemented and which haven't.
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
[[Property:title|Known issues]]
|
||||
[[Property:weight|4]]
|
||||
[[Property:uuid|78c6b5c1-a87b-805d-38bd-fe64cb4e7c0d]]
|
||||
==Eiffel issues==
|
||||
* Check here first for the [[Eiffel for .NET Integration|details of the integration of Eiffel with .NET]] .
|
||||
* The root creation routine of the root class must not take any arguments. You can access the command line given to the application via the class <code>ARGUMENTS</code>.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[[Property:title|.NET]]
|
||||
[[Property:weight|-15]]
|
||||
[[Property:uuid|55eda2f0-0c60-f08c-b141-913e31a49c2f]]
|
||||
==.NET==
|
||||
|
||||
Eiffel support for Microsoft .NET.
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
[[Property:title|.NET Installation instructions]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|64f7e3b1-f6ee-5cc4-1006-2fc4dfdaeae7]]
|
||||
Before installing the .NET components for Eiffel for .NET you need to install either the .NET Framework, the .NET Framework SDK or Visual Studio .NET. If you do not have any of those installed, the installation program will prompt you with a warning because no .NET features support will be installed.
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
[[Property:title|ADO.NET Sample]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|45d24893-63d0-c2a9-9f62-ead08ca4b901]]
|
||||
This sample consist of a command line showing how to interact with a database.
|
||||
|
||||
The DataReader object is somewhat synonymous with a read-only/forward-only cursor over data. The DataReader API supports flat as well as hierarchical data. A DataReader object is returned after executing a command against a database. The format of the returned DataReader object is different from a recordset. For example, you might use the DataReader to show the results of a search list in a web page.
|
||||
|
||||
==Compiling==
|
||||
|
||||
To compile the example:
|
||||
# Launch EiffelStudio.
|
||||
# Select '''Use existing Ace (control file)''' and click '''OK'''
|
||||
# Browse to ''$ISE_EIFFEL\examples\dotnet\ado\ado3\''
|
||||
# Choose the Ace file for the version of the .net framework you are running
|
||||
# Choose the directory where the project will be compiled, by default the same directory containing the Ace file.
|
||||
# Click '''OK'''.
|
||||
|
||||
|
||||
==Running==
|
||||
|
||||
After you launch the sample, the following output appears:
|
||||
<code>
|
||||
Customer ID Company Name
|
||||
ALFKI Alfreds Futterkiste
|
||||
ANATR Ana Trujillo Emparedados y helados
|
||||
ANTON Antonio Moreno Taquera
|
||||
AROUT Around the Horn
|
||||
BERGS Berglunds snabbkp
|
||||
BLAUS Blauer See Delikatessen
|
||||
BLONP Blondesddsl p`re et fils
|
||||
BOLID Blido Comidas preparadas
|
||||
BONAP Bon app'
|
||||
BOTTM Bottom-Dollar Markets
|
||||
BSBEV B's Beverages
|
||||
CACTU Cactus Comidas para llevar
|
||||
CENTC Centro comercial Moctezuma
|
||||
CHOPS Chop-suey Chinese
|
||||
COMMI Comrcio Mineiro
|
||||
CONSH Consolidated Holdings
|
||||
DRACD Drachenblut Delikatessen
|
||||
DUMON Du monde entier
|
||||
EASTC Eastern Connection
|
||||
... ... ...
|
||||
WILMK Wilman Kala
|
||||
WOLZA Wolski Zajazd
|
||||
</code>
|
||||
|
||||
When the display is finished, the application wait for you to pressed the return key to finish the application.
|
||||
|
||||
==Under the Hood==
|
||||
|
||||
This application shows how to interact with a database. First the connection to the database is opened:
|
||||
<code>
|
||||
create connection.make ("server=(local)\NetSDK;Trusted_Connection=yes;database=northwind")
|
||||
</code>
|
||||
Then a request to the database is made:
|
||||
<code>
|
||||
create command.make ("select * from customers", connection)
|
||||
connection.open
|
||||
reader := command.execute_reader
|
||||
</code>
|
||||
Finally, the result of the request is displayed:
|
||||
<code>
|
||||
from
|
||||
ok := reader.read
|
||||
until
|
||||
not ok
|
||||
loop
|
||||
io.put_string (reader.item ("CustomerID").to_string)
|
||||
io.put_string ("%T%T")
|
||||
io.put_string (reader.item ("CompanyName").to_string)
|
||||
io.new_line
|
||||
ok := reader.read
|
||||
end
|
||||
</code>
|
||||
|
||||
This sample uses the following ADO.NET classes:
|
||||
* <eiffel>SQL_DATA_READER</eiffel>
|
||||
* <eiffel>SQL_CONNECTION</eiffel>
|
||||
* <eiffel>SQL_COMMAND</eiffel>
|
||||
|
||||
==Notes==
|
||||
|
||||
This sample is translated from the example located in the QuickStart\howto\samples\adoplus subdirectory of the .NET Framework SDK samples directory of Microsoft Visual Studio .NET.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
[[Property:title|ADO .NET Samples]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|b58f17b4-57b0-ffe9-3160-86d95512f900]]
|
||||
ADO .NET is the data access technology of Microsoft .NET.
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
[[Property:title|Calculator: console]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|70cc4687-0d3e-2859-70f6-c603c05d085c]]
|
||||
=The Calculator Sample=
|
||||
|
||||
This sample consists of a command line reverse Polish notation (RPN) calculator.
|
||||
{{note|A RPN calculator works slightly differently from standard calculators. It consists of a stack of numbers. Operations are applied to the two numbers on top of the stack. The result is then put on top of the stack so that it can be used in the next operation. This sample refers to the top of the stack as ''Accumulator''. }}
|
||||
|
||||
==Compiling==
|
||||
|
||||
To compile the example:
|
||||
# Launch EiffelStudio.
|
||||
# Click '''Add project'''
|
||||
# Browse to ''$ISE_EIFFEL\examples\base\calculator\''
|
||||
# Choose ''calculator.ecf''
|
||||
# Change the target from classic to dotnet
|
||||
# Choose the location where the project will be compiled, by default the same directory containing the configuration file.
|
||||
# Click '''Open'''.
|
||||
|
||||
|
||||
==Running==
|
||||
|
||||
After you launch the sample, the following text appears in a console:
|
||||
<code>
|
||||
*********************************
|
||||
Calculator in reverse Polish form
|
||||
*********************************
|
||||
Allowable operations are:
|
||||
'/': Divide top two numbers on the stack.
|
||||
'0': Empty the stack.
|
||||
'a': Enter operand onto stack.
|
||||
'?': Help.
|
||||
'*': Multiply top two numbers on the stack.
|
||||
'+': Add top two numbers on the stack
|
||||
'q': Quit.
|
||||
'-': Subtract top two numbers on the stack.
|
||||
Enter a number, followed by :
|
||||
</code>
|
||||
|
||||
Enter the first number to be put onto the stack, for example <code>3</code>.
|
||||
|
||||
{{note|Failing to enter a number at this stage will cause the sample to stop. This sample was designed to showcase the use of EiffelBase data structures in the Microsoft .NET environment and is not protected against unexpected entries. }}
|
||||
|
||||
You may then add another number on the stack by entering the character "<code>a</code>":
|
||||
<code>
|
||||
*********************************
|
||||
Calculator in reverse Polish form
|
||||
*********************************
|
||||
Allowable operations are:
|
||||
'/': Divide top two numbers on the stack.
|
||||
'0': Empty the stack.
|
||||
'a': Enter operand onto stack.
|
||||
'?': Help.
|
||||
'*': Multiply top two numbers on the stack.
|
||||
'+': Add top two numbers on the stack
|
||||
'q': Quit.
|
||||
'-': Subtract top two numbers on the stack.
|
||||
Enter a number, followed by : 3
|
||||
|
||||
Accumulator = 3
|
||||
|
||||
Next operation? a
|
||||
Enter a number, followed by :
|
||||
</code>
|
||||
|
||||
Enter a second number, for example <code>2</code>. You can then apply any operation to the two operands such as minus:
|
||||
<code>
|
||||
...
|
||||
Next operation? a
|
||||
Enter a number, followed by : 2
|
||||
|
||||
Accumulator = 2
|
||||
|
||||
Next operation? -
|
||||
|
||||
Accumulator = 1
|
||||
|
||||
Next operation?
|
||||
</code>
|
||||
|
||||
You may use the operation<code> 0</code> to clear the stack at any time. You may use <code>q</code> to quit the program.
|
||||
|
||||
{{tip|You can use the command <code>?</code> to display the list of available operations. }}
|
||||
|
||||
==Under the Hood==
|
||||
|
||||
This sample shows how to leverage EiffelBase data structures in a simple Eiffel system. The root class <eiffel>CALCULATOR</eiffel> first instantiates all ''state'' objects, each corresponding to one possible operation. The state classes all inherit from <eiffel>STATE</eiffel>. They are:
|
||||
* <eiffel>PLUS</eiffel>: Add stack's two top numbers.
|
||||
* <eiffel>MINUS</eiffel>: Substract stack's two top numbers.
|
||||
* <eiffel>MULTIPLY</eiffel>: Multiply stack's two top numbers.
|
||||
* <eiffel>DIVIDE</eiffel>: Divide stack's two top numbers.
|
||||
* <eiffel>EMPTY</eiffel>: Empty stack.
|
||||
* <eiffel>HELP</eiffel>: Prints available commands.
|
||||
* <eiffel>QUESTION</eiffel>: Get number from user.
|
||||
* <eiffel>QUIT</eiffel>: Close application.
|
||||
Each of these classes implement the feature <eiffel>do_one_state</eiffel> from <eiffel>STATE</eiffel> which performs the operation associated with the state.The initial state is <eiffel>QUESTION</eiffel> which asks for the initial number to put in the ''accumulator''. Following states depend on the user input.Every descendant of <eiffel>STATE</eiffel> implement the feature <eiffel>operation</eiffel> which performs the corresponding stack transformation.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
[[Property:title|Console Samples]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|3f2c704c-9784-06a5-4cad-02ad58433b2c]]
|
||||
Console application samples.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
[[Property:title|Samples]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:uuid|c1dbfdb5-c4d0-f674-d818-00d56c431c6e]]
|
||||
In this chapter you will find a series of examples that will permit to you to understand the bases of programming with Dotnet.
|
||||
|
||||
However, you should be familiar with Object-Oriented programming model. Almost all other examples can also be built for .NET if the dotnet target is choosen instead of classic.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
[[Property:title|Threads Samples]]
|
||||
[[Property:weight|2]]
|
||||
[[Property:uuid|62e36a4c-0afd-e143-9a1f-98eab4022e6b]]
|
||||
Samples using .NET threading technology.
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
[[Property:title|Pools]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|ace044b0-1fb7-22a0-6c18-880281ed3b6d]]
|
||||
This sample demonstrates use of the <eiffel>THREAD_POOL</eiffel> (ThreadPool) class. The sample queues up an asynchronous method call that is executed by a thread from the thread pool managed by the Common Language Runtime. The method "does some work" and then sets an event indicating that the work has finished. The main thread waits on the event and then exits.
|
||||
|
||||
==Compiling==
|
||||
|
||||
To compile the example:
|
||||
# Launch EiffelStudio.
|
||||
# Select '''Use existing Ace (control file)''' and click '''OK'''
|
||||
# Browse to ''$ISE_EIFFEL\examples\dotnet\threading\pools\''
|
||||
# Choose the Ace file for the version of the .net framework you are running
|
||||
# Choose the directory where the project will be compiled, by default the same directory containing the Ace file.
|
||||
# Click '''OK'''.
|
||||
|
||||
|
||||
==Running==
|
||||
|
||||
After you launch the sample, the following output appears:
|
||||
<code>
|
||||
Main thread: Queuing an asynchronous operation.
|
||||
Main thread: Performing other operations.
|
||||
WorkItem thread: Performing asynchronous operation
|
||||
Main thread: Waiting for asynchronous operation to complete.
|
||||
</code>
|
||||
|
||||
When the display is finished, the application wait for you to pressed the return key to finished the application.
|
||||
|
||||
==Under the Hood==
|
||||
|
||||
This application shows how to use the thread <eiffel>THREAD_POOL</eiffel>.
|
||||
An asynchronous thread is launched:
|
||||
<code>
|
||||
return := {THREAD_POOL}.queue_user_work_item (create {WAIT_CALLBACK}.make (Current, $async_operation, l_async_operation_done))
|
||||
</code>
|
||||
and is associated to the local variable <code>l_async_operation_done</code>. Both threads perform simultaneously some operations. The main thread wait for the asynchronous thread to complete his own operations
|
||||
<code>
|
||||
return := l_async_operation_done.wait_one
|
||||
</code> to close the application.
|
||||
|
||||
|
||||
This sample uses the following .NET types:
|
||||
* <eiffel>THREAD_POOL</eiffel>
|
||||
* <eiffel>WAIT_CALLBACK</eiffel>
|
||||
* <eiffel>AUTO_RESET_EVENT</eiffel>
|
||||
|
||||
==Notes==
|
||||
|
||||
This sample is translated from the example located in the Samples\Technologies\Threading\Pools subdirectory of the .NET Framework SDK samples directory of Microsoft Visual Studio .NET.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
[[Property:title|Timers]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|325ac6e6-9660-891c-2605-dbeb621649f0]]
|
||||
This sample consist in a command line demonstrating the use of the <eiffel>TIMER</eiffel> (Timer) class to generate a periodic callback to a method. The sample creates a <eiffel>TIMER</eiffel> object and passes to it a delegate object. When the <eiffel>TIMER</eiffel> fires, the delegate is invoked, and a static method is called asynchronously by a worker thread in the thread pool.
|
||||
|
||||
==Compiling==
|
||||
|
||||
To compile the example:
|
||||
# Launch EiffelStudio.
|
||||
# Click '''Add project'''
|
||||
# Browse to ''$ISE_EIFFEL\examplesdotnet\threading\timers\''
|
||||
# Choose ''timers.ecf''
|
||||
# Choose the location where the project will be compiled, by default the same directory containing the configuration file.
|
||||
# Click '''Open'''.
|
||||
|
||||
|
||||
==Running==
|
||||
|
||||
After you launch the sample, the following output appears:
|
||||
<code>
|
||||
Checking for status updates every two seconds
|
||||
<Hit Enter to terminate the sample>
|
||||
Checking Status.
|
||||
Checking Status.
|
||||
Checking Status.
|
||||
Checking Status.
|
||||
Checking Status.
|
||||
...
|
||||
</code>
|
||||
|
||||
When the display is finished, the application wait for you to pressed the return key to finished the application.
|
||||
|
||||
==Under the Hood==
|
||||
|
||||
This application shows how to use the thread <eiffel>TIMER</eiffel>. The timer is launched:
|
||||
<code>
|
||||
create my_timer.make_with_callback (create {TIMER_CALLBACK}.make (Current, $check_status), Void, 0, 2000)
|
||||
</code>
|
||||
and calls the feature <eiffel>check_status</eiffel> that displays the message <code>"Checking Status."</code> every two seconds.
|
||||
|
||||
This sample uses the following .NET types:
|
||||
* <eiffel>TIMER</eiffel>
|
||||
* <eiffel>TIMER_CALLBACK</eiffel>
|
||||
|
||||
==Notes==
|
||||
|
||||
This sample is translated from the example located in the Samples\Technologies\Threading\Timers subdirectory of the .NET Framework SDK samples directory of Microsoft Visual Studio .NET.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
[[Property:title|Calculator: winform]]
|
||||
[[Property:weight|-8]]
|
||||
[[Property:uuid|9d064b1c-e109-35e8-70d5-73feec59fca1]]
|
||||
|
||||
[[Image:calculator|Calculator]]
|
||||
|
||||
<br/>
|
||||
|
||||
|
||||
==Compiling==
|
||||
|
||||
To compile the example:
|
||||
# Launch EiffelStudio.
|
||||
# Select '''Use existing Ace (control file)''' and click '''OK'''
|
||||
# Browse to ''$ISE_EIFFEL\examples\dotnet\winforms\applications\world_calc''
|
||||
# Choose the Ace file for the version of the .net framework you are running
|
||||
# Choose the directory where the project will be compiled, by default the same directory containing the Ace file.
|
||||
# Click '''OK'''.
|
||||
|
||||
|
||||
==Running==
|
||||
|
||||
After launching the application, you will see a window displayed with a similar appearance to the one above. This is a really simple calculator that can take only two operands. So you should enter the first operand, then enter an operator, then the second operand. Click the "Calculate" button to generate the result.
|
||||
|
||||
|
||||
|
||||
==Under the Hood==
|
||||
|
||||
The application uses two local external assemblies, math.dll and parserutils.dll.
|
||||
|
||||
parserutils parses the command line entered, and checks if it is of the form operand1 operator operand2. math.dll calculates the actual result of the command line.
|
||||
|
||||
For the parserutils.dll, we have been obliged to introduce the prefix "PARSER_" to class names in this assembly in order to avoid conflicts between class names from this and other sources.
|
||||
|
||||
This sample uses the following classes:
|
||||
* <span><eiffel>FORM</eiffel></span>
|
||||
* <span><eiffel>BUTTON</eiffel></span>
|
||||
* <span><eiffel>TEXT_FIELD</eiffel></span>
|
||||
* <span><eiffel>LABEL</eiffel></span>
|
||||
|
||||
==Notes==
|
||||
|
||||
This sample is translated from the example located in the Tutorials\resources and localization\worldcalc subdirectory of the.NET Framework SDK samples directory of Microsoft Visual Studio.NET.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
[[Property:title|Date Time Picker]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|9f94f4f3-3a6e-1fe7-f9c0-d9411913ce25]]
|
||||
<div>
|
||||
[[Image:date-time-picker|Hello world form]]
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
==Compiling==
|
||||
|
||||
To compile the example:
|
||||
# Launch EiffelStudio.
|
||||
# Click '''Add project'''
|
||||
# Browse to ''$ISE_EIFFEL\examples\dotnet\winforms\control_reference\date_time_picker_ctrl\''
|
||||
# Choose ''date_time_picker_ctrl.ecf''
|
||||
# Choose the location where the project will be compiled, by default the same directory containing the configuration file.
|
||||
# Click '''Open'''.
|
||||
|
||||
|
||||
==Running==
|
||||
|
||||
After launching the application, you will see a window displayed with a similar appearance to the one above. On the left side of the main window a date is displayed in a <span><eiffel>COMBO_BOX</eiffel></span>. Clicking on this <span><eiffel>COMBO_BOX</eiffel></span> and a <span><eiffel>CALENDAR</eiffel></span> control will appear showing the same date as in the <span><eiffel>COMBO_BOX</eiffel></span>. On the right side of the main window are the controls for selecting the format of the displayed date and the style of the <span><eiffel>CALENDAR</eiffel></span> control.
|
||||
|
||||
Clicking on the "Change font" button and a change font dialog box will appear.
|
||||
|
||||
[[Image:change-font-dialog|change font dialog box]] .
|
||||
|
||||
Clicking on the "Change color" button and a dialog box will appear. This dialog box will permit you to customize the color appearance of the calendar.
|
||||
|
||||
[[Image:dotnet-samples--date-time-picker-change-color-dlg|color dialog box]] .
|
||||
|
||||
|
||||
|
||||
==Under the Hood==
|
||||
|
||||
This application uses several different controls, but tend to show how to parameter the <span><eiffel>CALENDAR</eiffel></span> control using the <span><eiffel>FONT_DIALOG</eiffel></span> and <span><eiffel>COLOR_DIALOG</eiffel></span>.
|
||||
|
||||
When one of the buttons "Change font" or "Change color" is pressed, an event is launched and open the corresponding dialog box.
|
||||
|
||||
When the entry in the <span><eiffel>COMBO_BOX</eiffel></span> format changes, an event is launched and modify the format of the displayed date.
|
||||
|
||||
This sample uses the following classes:
|
||||
* <span><eiffel>CALENDAR</eiffel></span>
|
||||
* <span><eiffel>COMBO_BOX</eiffel></span>
|
||||
* <span><eiffel>BUTTON</eiffel></span>
|
||||
* <span><eiffel>LABEL</eiffel></span>
|
||||
* <span><eiffel>GROUP_BOX</eiffel></span>
|
||||
* <span><eiffel>CHECK_BOX</eiffel></span>
|
||||
|
||||
==Notes==
|
||||
|
||||
This sample is translated from the example located in the QuickStart\winforms\samples\controlReference\dateTimePicker sub-directory of the .NET Framework SDK samples directory of Microsoft Visual Studio .NET.
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
[[Property:title|GDI plus - text]]
|
||||
[[Property:weight|-7]]
|
||||
[[Property:uuid|0c5210da-5512-5ae5-2971-a1a5095737c9]]
|
||||
<div>
|
||||
[[Image:text|Hello world form]]
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
==Compiling==
|
||||
|
||||
To compile the example:
|
||||
# Launch EiffelStudio.
|
||||
# Click '''Add project'''
|
||||
# Browse to ''$ISE_EIFFEL\examples\dotnet\gdi_plus\text\''
|
||||
# Choose ''text.ecf''
|
||||
# Choose the location where the project will be compiled, by default the same directory containing the configuration file.
|
||||
# Click '''Open'''.
|
||||
|
||||
|
||||
==Running==
|
||||
|
||||
After launching the application, you will see a window displayed with a similar appearance to the one above.
|
||||
|
||||
|
||||
|
||||
==Under the Hood==
|
||||
|
||||
This sample show how to draw some text in a window, using different brushes, different fonts, different style and different colors.
|
||||
|
||||
This sample uses the following class:
|
||||
* <eiffel>FORM</eiffel>
|
||||
|
||||
==Notes==
|
||||
|
||||
This sample is translated from the example located in the QuickStart\winforms\samples\gdiplus subdirectory of the .NET Framework SDK samples directory of Microsoft Visual Studio.NET.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
[[Property:title|Winform Samples]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:uuid|24cbeb3c-ba2b-1fea-ccec-89600bbf3256]]
|
||||
The following examples are classified generally from the easiest to the most complex. So, some notions are introduced in the few first examples and will not be explained again in the more complex examples!
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
[[Property:title|MDI]]
|
||||
[[Property:weight|-9]]
|
||||
[[Property:uuid|d1cb553c-8394-9f27-4d75-8d69f8582e49]]
|
||||
<div>
|
||||
[[Image:mdi|Hello world form]]
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
==Compiling==
|
||||
|
||||
To compile the example:
|
||||
# Launch EiffelStudio.
|
||||
# Click '''Add project'''
|
||||
# Browse to ''$ISE_EIFFEL\examples\dotnet\winforms\mdi\''
|
||||
# Choose ''mdi.ecf''
|
||||
# Choose the location where the project will be compiled, by default the same directory containing the configuration file.
|
||||
# Click '''Open'''.
|
||||
|
||||
|
||||
==Running==
|
||||
|
||||
After launching the application, you will see a window displayed with a similar appearance to the one above. The main window contains a menu with three entries (File, Format and Window). This menu can merge with its child windows (Document).
|
||||
|
||||
In the "File" entry, you can add a child window (Document),display the active child window, and exit the program.
|
||||
|
||||
You can change the format of the text displayed in the child windows using the entry "Format" in the menu.
|
||||
|
||||
You can choose the organization (cascade, title horizontal or title vertical) of the child windows using the entry "Window" in the menu.
|
||||
|
||||
|
||||
|
||||
==Under the Hood==
|
||||
|
||||
This application demonstrates how to create a multiple document interface. More information regarding the use of a MDI, is available [[MDI Details|here]] .
|
||||
|
||||
This sample uses the following class:
|
||||
* <span><eiffel>FORM</eiffel></span>
|
||||
|
||||
==Notes==
|
||||
|
||||
This sample is translated from the example located in the QuickStart\winforms\samples\MDI subdirectory of the .NET Framework SDK samples directory of Microsoft Visual Studio .NET.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
[[Property:title|MDI Details]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|f39b5ac5-64ae-781d-8d43-305954ae8d28]]
|
||||
Multiple Document Interface (MDI) applications have a single, primary window (the parent window) that contains a set of windows within its client region (child windows). Each child window is a form that is constrained to appear only within the parent. Children typically share the menu bar, tool bar, and other parts of the parent's interface. Secondary windows like dialog boxes are not constrained to the parent window's client region.
|
||||
|
||||
==Creating an MDI Application==
|
||||
|
||||
You can create an MDI application by following these steps:
|
||||
# Create a '''Form''' ('''MainForm''') that represents the MDI parent window and set its '''IsMdiContainer''' property to '''True'''. The following code demonstrates how to set this property.
|
||||
<code>
|
||||
set_is_mdi_container (True)</code>
|
||||
|
||||
# Create child forms and set the '''MdiParent''' property of each form to reference the parent form. The following code demonstrates setting the MDI parent for an instance of a child form. <br/>
|
||||
<code>
|
||||
doc.set_mdi_parent (Current)</code>
|
||||
|
||||
|
||||
If you have different types of data to display, you can have multiple types of child forms. To display a child form, create an instance of the child form and call its '''Show''' method.
|
||||
|
||||
|
||||
|
||||
==Standard MDI Menus==
|
||||
|
||||
Typically, an MDI application has a '''Windows''' menu that allows the user to arrange the open child windows through tiling or cascading. The '''Windows''' menu also allows you to navigate to any of the open child windows. To create a '''Windows''' menu, add the menu items for tiling and cascading to a '''Windows''' menu in your parent form and set the '''MdiList''' property to '''True''' for the top-level '''Windows''' menu. The following code demonstrates how to create a '''Windows''' menu in an MDIapplication.
|
||||
<code>
|
||||
mi_window: WINFORMS_MENU_ITEM
|
||||
|
||||
...
|
||||
|
||||
mi_window := main_menu.get_menu_items.add ("&Window")
|
||||
dummy := mi_window.get_menu_items.add_string_event_handler ("&Cascade", create {EVENT_HANDLER}.make (Current, $window_cascade_clicked))
|
||||
dummy := mi_window.get_menu_items.add_string_event_handler ("Tile &Horizontal", create {EVENT_HANDLER}.make (Current, $window_tile_h_clicked))
|
||||
dummy := mi_window.get_menu_items.add_string_event_handler ("Tile &Vertical", create {EVENT_HANDLER}.make (Current, $window_tile_v_clicked))
|
||||
mi_window.set_mdi_list (True) -- Adds the MDI Window List to the bottom of the menu
|
||||
...
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
==Child Window Activation==
|
||||
|
||||
If you want your parent form to be notified when a child window is activated by the user, you can register an event-handling method for the '''MdiChildActivate''' event. You can determine which child window is active by using the '''ActiveMdiChild''' property of the '''Form''' class. For example, the following code updates a '''StatusBar''' control on the parent form with the name of the child window.
|
||||
<code>
|
||||
...
|
||||
|
||||
add_mdi_child_activate (create {EVENT_HANDLER}.make (Current, $mdi_child_activated))
|
||||
|
||||
...
|
||||
|
||||
mdi_child_activated (sender: SYSTEM_OBJECT; e: EVENT_ARGS)
|
||||
do
|
||||
status_bar.set_text (active_mdi_child.get_text)
|
||||
end
|
||||
|
||||
...
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
[[Property:title|Menus]]
|
||||
[[Property:weight|-10]]
|
||||
[[Property:uuid|5a32fed4-0b46-51f8-a6b6-fc3eac70a844]]
|
||||
<div>
|
||||
[[Image:menu-principal|Hello world form]]
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
==Compiling==
|
||||
|
||||
==Running==
|
||||
|
||||
After launching the application, you will see a window displayed with a similar appearance to the one above. The main window contains a main menu with two entries (File and Format).
|
||||
|
||||
The main window also has a blue <span>LABEL</span> that contains a context menu, that is the clone of "Format" in the main menu. Right click on the <span>LABEL</span> and a context menu will appear
|
||||
|
||||
[[Image:menu-contextuel|context menu]] .
|
||||
|
||||
|
||||
|
||||
==Under the Hood==
|
||||
|
||||
This application shows how to create a main menu and how to associate a context menu to a control (here to a <span>LABEL)</span>. More information regarding the use of menus, is available [[Menu Details|here]] .
|
||||
|
||||
This sample uses the following classes:
|
||||
* <span>FORM</span>
|
||||
* <span>CONTEXT_MENU</span>
|
||||
* <span>MAIN_MENU</span>
|
||||
* <span>MENU_ITEM</span>
|
||||
* <span>EVENT_HANDLER</span>
|
||||
* <span>LABEL</span>
|
||||
|
||||
==Notes==
|
||||
|
||||
This sample is translated from the example located in the QuickStart\winforms\samples\menus subdirectory of the .NET Framework SDK samples directory of Microsoft Visual Studio.NET.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
[[Property:title|Menu Details]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|81ae03a0-387d-f776-17b9-f4a83c49b75f]]
|
||||
Windows Forms supports menus and context menus. Main menus are displayed on a menu bar that is located immediately below the title bar of a form. The menu bar contains top-level menu items that are used to group related submenu items. For example, by clicking a '''File''' top-level menu item, you can display menu items that are related to file operations. Menu items typically appear as commands for your application (such as '''New''' and '''Open'''),but they can also appear as separator bars and submenu items. You can display a check mark next to a menu item to display the state of a command or a the state of a feature in your application. In Windows Forms, main menus are represented by the '''MainMenu''' control.
|
||||
|
||||
Context menus can be displayed for a specific control or area of your form. They are typically accessed by clicking the right mouse button. In Windows Forms, context menus are represented by the '''ContextMenu''' control.
|
||||
|
||||
'''ContextMenu''' and '''MainMenu''' derive from '''Menu'''. They share many properties, methods, and events.
|
||||
|
||||
==Adding a MainMenu to a Form==
|
||||
|
||||
The following code demonstrates how to add a '''MainMenu''' to a form.
|
||||
<code>
|
||||
main_menu: WINFORMS_MAIN_MENU
|
||||
</code>
|
||||
<code>
|
||||
create main_menu.make
|
||||
set_menu (main_menu)
|
||||
</code>
|
||||
|
||||
==Adding a Context Menu to a Control==
|
||||
|
||||
The following code demonstrates how to <span>create</span> a '''ContextMenu''' and assign it to a control.
|
||||
<code>
|
||||
label_1: WINFORMS_LABEL
|
||||
label_1_context_menu: WINFORMS_CONTEXT_MENU
|
||||
</code>
|
||||
<code>
|
||||
create label_1.make
|
||||
create label_1_context_menu.make
|
||||
label_1.set_context_menu (label_1_context_menu)
|
||||
</code>
|
||||
|
||||
==Adding Menu Items==
|
||||
|
||||
In the following example, a '''File''' menu item is added to the '''MainMenu'''. The '''File''' menu item contains submenu items called '''Open''' and '''Exit'''.
|
||||
<code>
|
||||
mi_file: WINFORMS_MAIN_MENU
|
||||
</code>
|
||||
<code>
|
||||
mi_file := main_menu. get_menu_items.add (("&File").to_cil )
|
||||
dummy := mi_file. get_menu_items.add_menu_item (create {WINFORMS_MENU_ITEM}.make_from_text (("&Open...").to_cil ))
|
||||
dummy := mi_file. get_menu_items.add (("-").to_cil ) -- Gives us a separator
|
||||
dummy := mi_file. get_menu_items.add_menu_item (create {WINFORMS_MENU_ITEM}.make_from_text(("E&xit").to_cil )
|
||||
</code>
|
||||
|
||||
<br/>
|
||||
|
||||
|
||||
The following code demonstrates how to handle the '''Click''' event for both the '''Open''' and '''Exit''' menu items <span>create</span>d in the previous code example.
|
||||
<code>
|
||||
mi_file := main_menu.get_menu_items.add (("&File").to_cil)
|
||||
dummy := mi_file.get_menu_items.add_menu_item (create {WINFORMS_MENU_ITEM}.make_from_text_and_on_click_and_shortcut
|
||||
(("&Open...").to_cil, create {EVENT_HANDLER}.make (Current, $FileOpen_Clicked), feature {WINFORMS_SHORTCUT}.ctrl_O))
|
||||
dummy := mi_file.get_menu_items.add (("-").to_cil) -- Gives us a separator
|
||||
dummy := mi_file.get_menu_items.add_menu_item (create {WINFORMS_MENU_ITEM}.make_from_text_and_on_click_and_shortcut
|
||||
(("E&xit").to_cil, create {EVENT_HANDLER}.make (Current, $FileExit_Clicked), feature {WINFORMS_SHORTCUT}.ctrl_X))
|
||||
</code>
|
||||
|
||||
<br/>
|
||||
|
||||
|
||||
The following example demonstrates how to define shortcut keys for the menu items <span>create</span>d in the previous example.
|
||||
<code>
|
||||
mi_file := main_menu.get_menu_items.add (("&File").to_cil)
|
||||
dummy := mi_file.get_menu_items.add_menu_item (create {WINFORMS_MENU_ITEM}.make_from_text_and_on_click_and_shortcut
|
||||
(("&Open...").to_cil, create {EVENT_HANDLER}.make (Current, $FileOpen_Clicked), feature {WINFORMS_SHORTCUT}.ctrl_O))
|
||||
dummy := mi_file.get_menu_items.add (("-").to_cil) -- Gives us a separator
|
||||
dummy := mi_file.get_menu_items.add_menu_item (create {WINFORMS_MENU_ITEM}.make_from_text_and_on_click_and_shortcut
|
||||
(("E&xit").to_cil, create {EVENT_HANDLER}.make (Current, $FileExit_Clicked), feature {WINFORMS_SHORTCUT}.ctrl_X))
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
==Adding Submenus==
|
||||
|
||||
The following example demonstrates how to <span>create</span> submenus.
|
||||
<code>
|
||||
mi_format: WINFORMS_MAIN_MENU
|
||||
</code>
|
||||
<code>
|
||||
-- Add Format Menu
|
||||
mi_format := main_menu.get_menu_items.add (("F&ormat").to_cil)
|
||||
|
||||
-- Font Face sub-menu
|
||||
create mmi_sans_serif.make_from_text_and_on_click ((("").to_cil).concat_string_string (("&1. ").to_cil,
|
||||
sans_serif_font_family.get_name), create {EVENT_HANDLER}.make (Current, $FormatFont_Clicked))
|
||||
mmi_sans_serif.set_checked (True)
|
||||
mmi_sans_serif.set_default_item (True)
|
||||
create mmi_serif.make_from_text_and_on_click ((("").to_cil).concat_string_string(("&2. ").to_cil,
|
||||
serif_font_family.get_name), create {EVENT_HANDLER}.make (Current, $FormatFont_Clicked))
|
||||
create mmi_mono_space.make_from_text_and_on_click ((("").to_cil).concat_string_string(("&3. ").to_cil,
|
||||
mono_space_font_family.get_name), create {EVENT_HANDLER}.make (Current, $FormatFont_Clicked))
|
||||
|
||||
create l_array_menu_item.make (3)
|
||||
l_array_menu_item.put (0, mmi_sans_serif)
|
||||
l_array_menu_item.put (1, mmi_serif)
|
||||
l_array_menu_item.put (2, mmi_mono_space)
|
||||
dummy := mi_format.get_menu_items.add_string_menu_item_array (("Font &Face").to_cil, l_array_menu_item)
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
==Adding Default Menu Items==
|
||||
|
||||
The following example demonstrates how to specify a default menu item.
|
||||
<code>
|
||||
mmi_sans_serif: WINFORMS_MAIN_MENU
|
||||
</code>
|
||||
<code>
|
||||
create mmi_sans_serif.make_from_text_and_on_click ((("").to_cil).concat_string_string (("&1 ").to_cil,
|
||||
sans_serif_font_family.get_name), create {EVENT_HANDLER}.make (Current, $format_font_clicked))
|
||||
mmi_sans_serif.set_checked (True)
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
==Adding Check Marks to Menu Items==
|
||||
|
||||
The following example demonstrates how to display a check mark next to a menu item. The code also demonstrates how to track which item is checked.
|
||||
<code>
|
||||
mi_medium: WINFORMS_MAIN_MENU
|
||||
</code>
|
||||
<code>
|
||||
create mi_medium.make_from_text_and_on_click (("&Medium").to_cil, create {EVENT_HANDLER}.make (Current, $format_size_clicked))
|
||||
mi_medium.set_checked (True)
|
||||
</code>
|
||||
|
||||
<br/>
|
||||
|
||||
|
||||
==Cloning Menus==
|
||||
|
||||
In many cases, the context menu for a control is a subset of the main menu. You cannot add the same menu items to multiple menus, but you can clone a menu item or set of menu items. The following code demonstrates how to clone the '''Format''' menu <span>create</span>d previously and add it to the context menu of a '''Label'''.
|
||||
<code> mmy := label_1_context_menu.get_menu_items.add_menu_item (mi_format.clone_menu)
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
[[Property:title|Message Box]]
|
||||
[[Property:weight|-11]]
|
||||
[[Property:uuid|cbc37679-43b0-99b8-b8c7-1be8be49882e]]
|
||||
<div>
|
||||
[[Image:hello-world|Hello world form]] <br/>
|
||||
[[Image:message-box|Hello world form]]
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
==Compiling==
|
||||
|
||||
To compile the example:
|
||||
# Launch EiffelStudio.
|
||||
# Click '''Add project'''
|
||||
# Browse to ''$ISE_EIFFEL\examples\dotnet\winforms\hello_world_dlg\''
|
||||
# Choose ''hello_world_dlg.ecf''
|
||||
# Choose the location where the project will be compiled, by default the same directory containing the configuration file.
|
||||
# Click '''Open'''.
|
||||
|
||||
|
||||
==Running==
|
||||
|
||||
After launching the application, you will see a window displayed with a similar appearance to the one above.
|
||||
|
||||
Click on the <span><eiffel>BUTTON</eiffel></span> "Click Me!" and a message box will appear with the text that you have entered in the <span><eiffel>TEXT_BOX</eiffel></span>.
|
||||
|
||||
|
||||
|
||||
==Under the Hood==
|
||||
|
||||
The application shows how to use the very useful <span><eiffel>MESSAGE_BOX</eiffel></span> control.
|
||||
|
||||
An event ( <span><eiffel>EVENT_HANDLER</eiffel>)</span> is associated to a control (here to the <span><eiffel>BUTTON</eiffel></span>). So the line
|
||||
<code>
|
||||
my_button.add_click (create {EVENT_HANDLER}.make (Current, $on_my_button_clicked ))</code>
|
||||
associates a click to <span>my_button</span> to the feature <span><eiffel>on_my_button_clicked</eiffel></span>. So every time the <span>BUTTON</span> <span><eiffel>my_button</eiffel></span> is clicked, the feature <span>on_my_button_clicked</span> is executed.
|
||||
|
||||
|
||||
The feature <span><eiffel>my_button_clicked</eiffel></span> displays a <span><eiffel>MESSAGE_BOX</eiffel></span>
|
||||
<code>
|
||||
{WINFORMS_MESSAGE_BOX}.show (msg)
|
||||
</code>
|
||||
with the text contained in <eiffel>TEXT_BOX</eiffel>
|
||||
<code>
|
||||
msg := "Text is : '"
|
||||
msg.append (my_text_box.text)
|
||||
msg.append ("'")
|
||||
</code>
|
||||
|
||||
This sample uses the following classes:
|
||||
* <span><eiffel>FORM</eiffel></span>
|
||||
* <span><eiffel>BUTTON</eiffel></span>
|
||||
* <span><eiffel>TEXT_BOX</eiffel></span>
|
||||
* <span><eiffel>EVENT_HANDLER</eiffel></span>
|
||||
|
||||
==Notes==
|
||||
|
||||
This sample is translated from the example located in the QuickStart\winforms\samples\accessible subdirectory of the .NET Framework SDK samples directory of Microsoft Visual Studio.NET.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
[[Property:title|Progress bar sample]]
|
||||
[[Property:weight|6]]
|
||||
[[Property:uuid|3b888246-2382-ec0b-ed4c-e1d35fe1bd79]]
|
||||
<div>
|
||||
[[Image:progress-bar|Hello world form]]
|
||||
</div>
|
||||
==Compiling==
|
||||
|
||||
To compile the example:
|
||||
# Launch EiffelStudio.
|
||||
# Click '''Add project'''
|
||||
# Browse to ''$ISE_EIFFEL\examples\dotnet\winforms\control_reference\progress_bar_ctl\''
|
||||
# Choose ''progress_bar_ctl.ecf''
|
||||
# Choose the location where the project will be compiled, by default the same directory containing the configuration file.
|
||||
# Click '''Open'''.
|
||||
|
||||
|
||||
==Running==
|
||||
|
||||
After launching the application, you will see a window displayed with a similar appearance to the one above. On the left of the form you have the <span><eiffel>PROGRESS_BAR</eiffel></span> control, and on the right you have some controls to configure the <span><eiffel>PROGRESS_BAR</eiffel></span>.
|
||||
|
||||
|
||||
|
||||
==Under the Hood==
|
||||
|
||||
The application shows how to configure a <span><eiffel>PROGRESS_BAR</eiffel></span>.
|
||||
|
||||
The feature <span>on_load</span> is redefined and launch a thread (<eiffel>THREAD_START</eiffel>) that simulates the progression of a process.
|
||||
|
||||
This sample uses the following classes:
|
||||
* <span><eiffel>PROGRESS_BAR</eiffel></span>
|
||||
* <span><eiffel>GROUP_BOX</eiffel></span>
|
||||
* <span><eiffel>TRACK_BAR</eiffel></span>
|
||||
* <span><eiffel>COMBO_BOX</eiffel></span>
|
||||
* <span><eiffel>LABEL</eiffel></span>
|
||||
* <eiffel>THREAD_START</eiffel>
|
||||
|
||||
==Notes==
|
||||
|
||||
This sample is translated from the example located in the QuickStart\winforms\samples\controlReference\ProgressBar sub-directory of the .NET Framework SDK samples directory of Microsoft Visual Studio .NET.
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
[[Property:title|Simple data binding]]
|
||||
[[Property:weight|7]]
|
||||
[[Property:uuid|5a0572cb-435c-1558-9e05-7de7e6ba08f1]]
|
||||
<div>
|
||||
[[Image:simple-data-binding|Hello world form]]
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
==Compiling==
|
||||
|
||||
To compile the example:
|
||||
# Launch EiffelStudio.
|
||||
# Click '''Add project'''
|
||||
# Browse to ''$ISE_EIFFEL\examples\dotnet\winforms\data\simple_binding\''
|
||||
# Choose ''simple_binding.ecf''
|
||||
# Choose the location where the project will be compiled, by default the same directory containing the configuration file.
|
||||
# Click '''Open'''.
|
||||
|
||||
|
||||
==Running==
|
||||
|
||||
After launching the application, you will see a window displayed with a similar appearance to the one above.
|
||||
|
||||
This is a list of five customers you can parse, and we display the details of the current customer in the list.
|
||||
|
||||
Clicking on the left button (|<) will bring you on the first customer of the list.
|
||||
|
||||
Clicking on the left button (<) will bring you on the precedent customer in the list.
|
||||
|
||||
Clicking on the right button (>) will bring you on the next customer in the list.
|
||||
|
||||
Clicking on the right button (>|) will bring you on the last customer of the list.
|
||||
|
||||
|
||||
|
||||
==Under the Hood==
|
||||
|
||||
|
||||
|
||||
This sample uses the following class:
|
||||
* <span><eiffel>FORM</eiffel></span>
|
||||
|
||||
==Notes==
|
||||
|
||||
This sample is translated from the example located in the QuickStart\winforms\samples\dataBinding subdirectory of the .NET Framework SDK samples directory of Microsoft Visual Studio.NET.
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
[[Property:title|Simple Hello world form sample]]
|
||||
[[Property:weight|-12]]
|
||||
[[Property:uuid|f4f924a5-20a2-403b-ee1d-a39c77fbb8fd]]
|
||||
<div>
|
||||
[[Image:simple-hello-world|Hello world form]]
|
||||
</div>
|
||||
==Compiling==
|
||||
|
||||
To compile the example:
|
||||
# Launch EiffelStudio.
|
||||
# Click '''Add project'''
|
||||
# Browse to ''$ISE_EIFFEL\examples\dotnet\winforms\hello_world\''
|
||||
# Choose ''hello_world.ecf''
|
||||
# Choose the location where the project will be compiled, by default the same directory containing the configuration file.
|
||||
# Click '''Open'''.
|
||||
|
||||
|
||||
==Running==
|
||||
|
||||
After launching the application, you will see a window displayed with a similar appearance to the one above. This is the simplest windows form we can display. We have just set the title with "HelloWorld".
|
||||
|
||||
|
||||
|
||||
==Under the Hood==
|
||||
|
||||
The application shows how to display a Windows Form under the windows environment. The application inherits the <span><eiffel>FORM</eiffel></span> class, and just set the title with <span><code>"Hello world"</code></span> with the inherited feature <span><eiffel>set_text</eiffel></span>.
|
||||
<code>
|
||||
set_text ("Hello world")
|
||||
</code>
|
||||
|
||||
The application is launched in a <eiffel>PROCESS</eiffel> with this command line:
|
||||
<code>
|
||||
{WINFORMS_APPLICATION}.run_form (Current).
|
||||
</code>
|
||||
|
||||
This sample uses the following classes:
|
||||
* <span><eiffel>FORM</eiffel></span>
|
||||
* <eiffel>WINFORMS_APPLICATION</eiffel>
|
||||
|
||||
==Notes==
|
||||
|
||||
This sample is translated from the example located in the QuickStart\winforms\samples\simple hello world subdirectory of the.NET Framework SDK samples directory of Microsoft Visual Studio.NET.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
[[Property:title|Tree view]]
|
||||
[[Property:weight|9]]
|
||||
[[Property:uuid|28ca95af-120a-f99c-16ba-090dd290c23a]]
|
||||
<div>
|
||||
[[Image:tree-view|Hello world form]]
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
==Compiling==
|
||||
|
||||
To compile the example:
|
||||
# Launch EiffelStudio.
|
||||
# Click '''Add project'''
|
||||
# Browse to ''$ISE_EIFFEL\examples\dotnet\winforms\control_reference\tree_view_ctl\''
|
||||
# Choose ''tree_view_ctl.ecf''
|
||||
# Choose the location where the project will be compiled, by default the same directory containing the configuration file.
|
||||
# Click '''Open'''.
|
||||
|
||||
|
||||
==Running==
|
||||
|
||||
After launching the application, you will see a window displayed with a similar appearance to the one above. On the left of the form you have the <span><eiffel>TREE_VIEW</eiffel></span> control, and on the right you have some controls to configure the <span><eiffel>TREE_VIEW</eiffel></span>.
|
||||
|
||||
|
||||
|
||||
==Under the Hood==
|
||||
|
||||
The application shows how to use a <span><eiffel>TREE_VIEW</eiffel></span> and what are all its different configurations.
|
||||
|
||||
This sample uses the following classes:
|
||||
* <span><eiffel>TREE_VIEW</eiffel></span>
|
||||
* <span><eiffel>GROUP_BOX</eiffel></span>
|
||||
* <span><eiffel>CHECK_BOX</eiffel></span>
|
||||
* <span><eiffel>COMBO_BOX</eiffel></span>
|
||||
* <span><eiffel>LABEL</eiffel></span>
|
||||
|
||||
==Notes==
|
||||
|
||||
This sample is translated from the example located in the QuickStart\winforms\samples\ControlReference\TreeView subdirectory of the .NET Framework SDK samples directory of Microsoft Visual Studio .NET.
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
[[Property:title|Features]]
|
||||
[[Property:weight|0]]
|
||||
[[Property:uuid|3f6c7915-7e08-fc2b-f25a-7d98dff38631]]
|
||||
==ResourceBench Features==
|
||||
* Parse any resource file (created by Microsoft Developer Studio or Borland Resource Workshop).
|
||||
* Show resources by using a tree view control.
|
||||
* Display some resource's informations.
|
||||
* Open and Save a Project (projects are not compatible with upcoming version of ResourceBench).
|
||||
* Generate both Eiffel Classes and resource file.
|
||||
* Let you specify which part of your resource file will be generated.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
[[Property:title|ResourceBench]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|a39ba4ef-460f-eab7-080d-c83d91546484]]
|
||||
Type: Tool <br/>
|
||||
Platform: Any <br/>
|
||||
Version: 5.4 <br/>
|
||||
Availability: Basic Package
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
[[Property:title|Tour]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|5990eda2-2eac-6a40-8e99-5a0c5e9fc15f]]
|
||||
1 - Launch the Resource Bench application. This small dialog box will appear for the initialization process:
|
||||
|
||||
[[Image:retriev]]
|
||||
|
||||
Then you get the main window of Resource Bench:
|
||||
|
||||
[[Image:rb]]
|
||||
|
||||
2 - Open a resource file (why not the example.rc file provided with this release of Resource Bench). Let the application parse the file. You will see a dialog box that informs you of the parsings state. <br/>
|
||||
First, there is the preprocessing of the resource file:
|
||||
|
||||
[[Image:prepro]]
|
||||
|
||||
Then you have the analysis phase:
|
||||
|
||||
[[Image:analyz]]
|
||||
|
||||
3 - After these two steps you will see a hierarchical view of the resource file that show you what resource's type are included in the resource file (Below the result of the "example.rc" file).
|
||||
|
||||
[[Image:collaps]]
|
||||
|
||||
You can expand the tree view to see associated resources for each resource's type.
|
||||
|
||||
[[Image:expand]]
|
||||
|
||||
At this point you are able to:
|
||||
* Select some properties for the Eiffel code generation
|
||||
* Save a project file
|
||||
* Generate Eiffel Code
|
||||
* Regenerate the same resource file (the third point is very useful when you have a Resource Bench Project and do not have the original resource file that permits its creation).
|
||||
|
||||
|
||||
4 - To save a project file, click on the save button of the toolbar or select the save item in the file menu. A standard save dialog box will appear in which you can specify the file name of your project. The file's extension for a Resource Bench project is "*.prb"
|
||||
|
||||
5 - To generate Eiffel Code, select the "Generate Eiffel Code..." item in the Build menu, this dialog box will appear to ask you in which directory Resource Bench must save the Eiffel classes:
|
||||
|
||||
[[Image:browse]]
|
||||
|
||||
6 - To generate a resource file, select the "Generate Resource file..." item in the Build menu, a standard save dialog box will appear to save the file.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user