The CORBA Interface Definition Language (IDL) is used to define interfaces to objects in your network. This chapter introduces the features of CORBA IDL and illustrates the syntax used to describe interfaces.
The first step in developing a CORBA application is to define the interfaces to the objects required in your distributed system. To define these interfaces, you use CORBA IDL.
IDL allows you to define interfaces to objects without specifying the implementation of those interfaces. To implement an IDL interface, you define a C++ class that can be accessed through that interface and then you create objects of that class within an Orbix server application.
In fact, you can implement IDL interfaces using any programming language for which an IDL mapping is available. An IDL mapping specifies how an interface defined in IDL corresponds to an implementation defined in a programming language. CORBA applications written in different programming languages are fully interoperable.
CORBA defines standard mappings from IDL to several programming languages, including C++, Java, and Smalltalk. The Orbix IDL compiler converts IDL definitions to corresponding C++ definitions, in accordance with the standard IDL to C++ mapping.
IDL Modules and Scoping
An IDL module defines a naming scope for a set of IDL definitions. Modules allow you to group interface and other IDL type definitions in logical name spaces. When writing IDL definitions, always use modules to avoid possible name clashes.
The following example illustrates the use of modules in IDL:
// IDL module BankSimple { interface Bank { ... }; interface Account { ... }; };
The interfaces Bank
and Account
are scoped within the module BankSimple
. IDL definitions are available directly within the scope in which you define them. In other naming scopes, you must use the scoping operator (::
) to access these definitions. For example, the fully scoped name of interfaces Bank
and Account
are BankSimple::Bank
and BankSimple::Account
respectively.
IDL modules can be reopened. For example, a module declaration can appear several times in a single IDL specification if each declaration contains different data types. In most IDL specifications, this feature of modules is not required.
Defining IDL Interfaces
An IDL interface describes the functions that an object supports in a distributed application. Interface definitions provide all of the information that clients need to access the object across a network.
Consider the example of an interface that describes objects which implement bank accounts in a distributed application. The IDL interface definition is as follows:
//IDL module BankSimple { // Define a named type to represent money. typedef float CashAmount;// Forward declaration of interface Account.interface Account; interface Bank { ... }; interface Account { // The account owner and balance. readonly attribute string name; readonly attribute CashAmount balance; // Operations available on the account. void deposit (in CashAmount amount); void withdraw (in CashAmount amount); }; };
The definition of interface Account
includes both attributes and operations. These are the main elements of any IDL interface definition.
Attributes in IDL Interface Definitions
Conceptually, attributes correspond to variables that an object implements. Attributes indicate that these variables are available in an object and that clients can read or write their values.
In general, attributes map to a pair of functions in the programming language used to implement the object. These functions allow client applications to read or write the attribute values. However, if an attribute is preceded by the keyword readonly
, then clients can only read the attribute value.
For example, the Account
interface defines the attributes name
and balance
. These attributes represent information about the account which the object implementation can set, but which client applications can only read.
Operations in IDL Interface Definitions
IDL operations define the format of functions, methods, or operations that clients use to access the functionality of an object. An IDL operation can take parameters and return a value, using any of the available IDL data types.
For example, the Account
interface defines the operations deposit()
and withdraw()
as follows:
//IDL module BankSimple { typedef float CashAmount; ... interface Account { // Operations available on the account. void deposit(in CashAmount amount); void withdraw(in CashAmount amount); ... }; };
Each operation takes a parameter and has a void
return type.
Each parameter definition must specify the direction in which the parameter value is passed. The possible parameter passing modes are as follows:
in | The parameter is passed from the caller of the operation to the object. |
out | The parameter is passed from the object to the caller. |
inout | The parameter is passed in both directions. |
Parameter passing modes clarify operation definitions and allow an IDL compiler to map operations accurately to a target programming language.
Raising Exceptions in IDL Operations
IDL operations can raise exceptions to indicate the occurrence of an error. CORBA defines two types of exceptions:
- System exceptions are a set of standard exceptions defined by CORBA.
- User-defined exceptions are exceptions that you define in your IDL specification.
Implicitly, all IDL operations can raise any of the CORBA system exceptions. No reference to system exceptions appears in an IDL specification.
To specify that an operation can raise a user-defined exception, first define the exception structure and then add an IDL raises
clause to the operation definition. For example, the operation withdraw()
in interface Account
could raise an exception to indicate that the withdrawal has failed, as follows:
// IDL module BankExceptions { typedef float CashAmount; ... interface Account { exception InsufficientFunds { string reason; }; void withdraw(in CashAmount amount) raises(InsufficientFunds); ... }; };
An IDL exception is a data structure that contains member fields. In the preceding example, the exception InsufficientFunds
includes a single member of type string.
The raises
clause follows the definition of operation withdraw()
to indicate that this operation can raise exception InsufficientFunds
. If an operation can raise more then one type of user-defined exception, include each exception identifier in the raises
clause and separate the identifiers using commas.
Invocation Semantics for IDL Operations
By default, IDL operations calls are synchronous, that is a client calls an operation and blocks until the object has processed the operation call and returned a value. The IDL keyword oneway
allows you to modify these invocation semantics.
If you precede an operation definition with the keyword oneway
, a client that calls the operation will not block while the object processes the call. For example, you could add a oneway operation to interface Account
that sends a notice to an Account
object, as follows:
module BankSimple { ... interface Account { oneway void notice(in string text); ... }; };
Orbix does not guarantee that a oneway operation call will succeed; so if a oneway operation fails, a client may never know. There is only one circumstance in which Orbix indicates failure of a oneway operation. If a oneway operation call fails before Orbix transmits the call from the client address space, then Orbix raises a system exception.
A oneway operation can not have any out
or inout
parameters and can not return a value. In addition, a oneway operation can not have an associated raises
clause.
Passing Context Information to IDL Operations
CORBA context objects allow a client to map a set of identifiers to a set of string values. When defining an IDL operation, you can specify that the operation should receive the client mapping for particular identifiers as an implicit part of the operation call. To do this, add a context
clause to the operation definition.
Consider the example of an Account
object, where each client maintains a set of identifiers, such as sys_time
and sys_location
that map to information that the operation deposit()
logs for each deposit received. To ensure that this information is passed with every operation call, extend the definition of deposit()
as follows:
// IDL module BankSimple { typedef float CashAmount; ... interface Account { void deposit(in CashAmount amount) context("sys_time", "sys_location"); ... }; };
A context
clause includes the identifiers for which the operation expects to receive mappings.
Note that IDL contexts are rarely used in practice.
Inheritance of IDL Interfaces
IDL supports inheritance of interfaces. An IDL interface can inherit all the elements of one or more other interfaces.
For example, the following IDL definition illustrates two interfaces, called CheckingAccount
and SavingsAccount
, that inherit from interface Account
:
// IDL module BankSimple{ interface Account { ... }; interface CheckingAccount : Account { readonly attribute overdraftLimit; boolean orderChequeBook (); }; interface SavingsAccount : Account { float calculateInterest (); }; };
Interfaces CheckingAccount
and SavingsAccount
implicitly include all elements of interface Account
.
An object that implements CheckingAccount
can accept invocations on any of the attributes and operations of this interface, and on any of the elements of interface Account
. However, a CheckingAccount
object may provide different implementations of the elements of interface Account
to an object that implements Account
only.
The following IDL definition shows how to define an interface that inherits both CheckingAccount
and SavingsAccount
:
// IDL module BankSimple { interface Account { ... }; interface CheckingAccount : Account { ... }; interface SavingsAccount : Account { ... }; interface PremiumAccount : CheckingAccount, SavingsAccount { ... }; };
Interface PremiumAccount
is an example of multiple inheritance in IDL. Figure 3.1 on page 59 illustrates the inheritance hierarchy for this interface.
If you define an interface that inherits from two interfaces which contain a constant, type, or exception definition of the same name, you must fully scope that name when using that constant, type, or exception. An interface can not inherit from two interfaces that include operations or attributes that have the same name.
Figure 3.1: Multiple Inheritance of IDL Interfaces
The Object Interface Type
IDL includes the pre-defined interface Object
, which all user-defined interfaces inherit implicitly. The operations defined in this interface are described in the Orbix C++ Edition Programmer's Reference.
While interface Object
is never defined explicitly in your IDL specification, the operations of this interface are available through all your interface types. In addition, you can use Object
as an attribute or operation parameter type to indicate that the attribute or operation accepts any interface type, for example:
// IDL interface ObjectLocator { void getAnyObject (out Object obj); };
Note that it is not legal IDL syntax to inherit interface Object
explicitly.
Forward Declaration of IDL Interfaces
In an IDL definition, you must declare an IDL interface before you reference it. A forward declaration declares the name of an interface without defining it. This feature of IDL allows you to define interfaces that mutually reference each other.
For example, IDL interface Bank
includes an operation of IDL interface type Account
, to indicate that Bank
stores a reference to an Account
object. If the definition of interface Account
follows the definition of interface Bank
, you must forward declare Account
as follows:
// IDL module BankSimple { // Forward declaration of Account. interface Account; interface Bank { Account create_account (in string name); Account find_account (in string name); }; // Full definition of Account. interface Account { ... }; };
The syntax for a forward declaration is the keyword interface
followed by the interface identifier.
Overview of the IDL Data Types
In addition to IDL module, interface, and exception types, there are three general categories of data type in IDL:
This section examines each category of IDL types in turn and also describes how you can define new data type names in IDL.
IDL Basic Types
The following table lists the basic types supported in IDL.
The any
data type allows you to specify that an attribute value, an operation parameter, or an operation return value can contain an arbitrary type of value to be determined at runtime. Type any
is described in detail in Chapter 12, "The Any Data Type" on page 239.
IDL Complex Types
This section describes the IDL data types enum, struct, union, string, sequence, array, and fixed.
Enum
An enumerated type allows you to assign identifiers to the members of a set of values, for example:
// IDL module BankSimple { enum Currency {pound, dollar, yen, franc}; interface Account { readonly attribute CashAmount balance; readonly attribute Currency balanceCurrency; ... }; };
In this example, attribute balanceCurrency
in interface Account
can take any one of the values pound
, dollar
, yen
, or franc
.
Struct
A struct data type allows you to package a set of named members of various types, for example:
// IDL module BankSimple{ struct CustomerDetails { string name; short age; }; interface Bank { CustomerDetails getCustomerDetails (in string name); ... }; };
In this example, the struct CustomerDetails
has two members. The operation getCustomerDetails()
returns a struct of type CustomerDetails
that includes values for the customer name and age.
Union
A union data type allows you to define a structure that can contain only one of several alternative members at any given time. A union saves space in memory, as the amount of storage required for a union is the amount necessary to store its largest member.
All IDL unions are discriminated. A discriminated union associates a label value with each member. The value of the label indicates which member of the union currently stores a value.
For example, consider the following IDL union definition:
// IDL struct DateStructure { short Day; short Month; short Year; }; union Date switch (short) { case 1: string stringFormat; case 2: long digitalFormat; default: DateStructure structFormat; };
The union type Date
is discriminated by a short value. For example, if this short value is 1
, then the union member stringFormat
stores a date value as an IDL string. The default label associated with the member structFormat
indicates that if the short value is not 1
or 2
, then the structFormat
member stores a date value as an IDL struct.
Note that the type specified in parentheses after the switch
keyword must be an integer, char, boolean or enum type and the value of each case
label must be compatible with this type.
String
An IDL string represents a character string, where each character can take any value of the char basic type.
If the maximum length of an IDL string is specified in the string declaration, then the string is bounded. Otherwise the string is unbounded.
The following example shows how to declare bounded and unbounded strings:
// IDL module BankSimple { interface Account { // A bounded string with maximum length 10. attribute string<10> sortCode; // An unbounded string. readonly attribute string name; ... }; };
Sequence
In IDL, you can declare a sequence of any IDL data type. An IDL sequence is similar to a one-dimensional array of elements.
An IDL sequence does not have a fixed length. If the sequence has a fixed maximum length, then the sequence is bounded. Otherwise, the sequence is unbounded.
For example, the following code shows how to declare bounded and unbounded sequences as members of an IDL struct:
// IDL module BankSimple { interface Account { ... }; struct LimitedAccounts { string bankSortCode<10>; // Maximum length of sequence is 50. sequence<Account, 50> accounts; }; struct UnlimitedAccounts { string bankSortCode<10>; // No maximum length of sequence. sequence<Account> accounts; }; };
A sequence must be named by an IDL typedef
declaration before it can be used as the type of an IDL attribute or operation parameter. Refer to "Defining Data Type Names and Constants" on page 68 for details. The following code illustrates this:
// IDL module BankSimple { typedef sequence<string> CustomerSeq; interface Account { void getCustomerList(out CustomerSeq names); ... }; };
Arrays
In IDL, you can declare an array of any IDL data type. IDL arrays can be multi-dimensional and always have a fixed size. For example, you can define an IDL struct with an array member as follows:
// IDL module BankSimple { ... interface Account { ... }; struct CustomerAccountInfo { string name; Account accounts[3]; }; interface Bank { getCustomerAccountInfo (in string name, out CustomerAccountInfo accounts); ... }; };
In this example, struct CustomerAccountInfo
provides access to an array of Account
objects for a bank customer, where each customer can have a maximum of three accounts.
An array must be named by an IDL typedef
declaration before it can be used as the type of an IDL attribute or operation parameter. The IDL typedef
declaration allows you define an alias for a data type, as described in "Defining Data Type Names and Constants" on page 68.
The following code illustrates this:
// IDL module BankSimple { interface Account { ... }; typedef Account AccountArray[100]; interface Bank { readonly attribute AccountArray accounts; ... }; };
Note that an array is a less flexible data type than an IDL sequence, because an array always has a fixed length. An IDL sequence always has a variable length, although it may have an associated maximum length value.
Fixed
The fixed data type allows you to represent number in two parts: a digit and a scale. The digit represents the length of the number, and the scale is a non-negative integer that represents the position of the decimal point in the number, relative to the rightmost digit.
module BankSimple { typedef fixed<10,4> ExchangeRate; struct Rates { ExchangeRate USRate; ExchangeRate UKRate; ExchangeRate IRRate; }; };
In this case, the ExchangeRate
type has a digit of size 10, and a scale of 4. This means that it can represent numbers up to (+/-)999999.9999.
The maximum value for the digits is 31, and scale cannot be greater than digits. The maximum value that a fixed type can hold is equal to the maximum value of a double
.
Scale can also be a negative number. This means that the decimal point is moved scale digits in a rightward direction, causing trailing zeros to be added to the value of the fixed. For example, fixed <3,-4>
with a numeric value of 123
actually represents the number 1230000
. This provides a mechanism for storing numbers with trailing zeros in an efficient manner.
Note: Fixed<3, -4>
can also be represented as fixed<7, 0>
.
Constant fixed types can also be declared in IDL. The digits and scale are automatically calculated from the constant value. For example:
module Circle { const fixed pi = 3.142857; };
This yields a fixed type with a digits value of 7
, and a scale value of 6
.
IDL Pseudo Object Types
CORBA defines a set of pseudo object types that ORB implementations use when mapping IDL to some programming languages. These object types have interfaces defined in IDL but do not have to follow the normal IDL mapping for interfaces and are not generally available in your IDL specifications.
You can use only the following pseudo object types as attribute or operation parameter types in an IDL specification:
CORBA::NamedValue CORBA::Principal CORBA::TypeCode
To use any of these three types in an IDL specification, include the file orb.idl
in the IDL file as follows:
// IDL #include <orb.idl> ...
This statement indicates to the IDL compiler that types NamedValue
, Principal
, and TypeCode
may be used. The file orb.idl
should not actually exist in your system. Do not name any of your IDL files orb.idl
.
Defining Data Type Names and Constants
IDL allows you to define new data type names and constants. This section describes how to use each of these features of IDL.
Data Type Names
The typedef
keyword allows you define a meaningful or more simple name for an IDL type. The following IDL provides a simple example of using this keyword:
// IDL module BankSimple { interface Account { ... }; typedef Account StandardAccount; };
The identifier StandardAccount
can act as an alias for type Account
in subsequent IDL definitions. Note that CORBA does not specify whether the identifiers Account
and StandardAccount
represent distinct IDL data types in this example.
Constants
IDL allows you to specify constant data values using one of several basic data types. To declare a constant, use the IDL keyword const
, for example:
// IDL module BankSimple { interface Bank { const long MaxAccounts = 10000; const float Factor = (10.0 - 6.5) * 3.91; ... }; };
The value of an IDL constant cannot change. You can define a constant at any level of scope in your IDL specification.