Part II: Core CORBA
Chapter 4. The OMG Interface Definition Language
4.7 User-Defined Types
4.8.4 Operation Definitions
An operation definition can occur only as part of an interface definition. An operation definition must contain
A return result type An operation name
Zero or more parameter declarations
interface simple { void op(); };
The operation op requires no parameters and does not return a value. Because op does not transmit any data between client and server, its only purpose can be to change the state of the target object as a side effect. Such operations are rare, and you should be wary if you find yourself writing definitions like this one. Typically, there are better ways to achieve the desired state that do not require the client to make a separate call, such as implementing the behavior of op as part of another operation that accepts or returns a value.
The void return type must be specified. It is illegal to leave it out:
interface Simple {
op(); // Error, missing return type };
Here is a more interesting interface containing a number of operations:
interface Primes {
typedef unsigned long prime;
prime next_prime(in long n);
void next_prime2(in long n, out prime p); void next_prime3(inout long n);
};
Directional Attributes
Notice that the parameter lists for the three operations are qualified with one of three directional attributes:
in
The in attribute indicates that the parameter is sent from the client to the server.
out
The out attribute indicates that the parameter is sent from the server to the client.
inout
The inout attribute indicates a parameter that is initialized by the client and sent to the server. The server can modify the parameter value, so, after the operation completes, the client-supplied parameter value may have been changed by the server.
Directional attributes are necessary for two reasons. Directional attributes are required for efficiency.
Without directional attributes, there would be no way for the IDL compiler to work out whether a parameter value is sent from the client to the server or vice versa. This in turn would mean that all parameters would have to be transmitted over the network in both directions just in case they are required (and even if they are not initialized).
Directional attributes enable some saving in transmission cost. An in parameter is sent only from the client to the server, and an out parameter is sent only from the server to the client. Only inout parameters are transmitted in both directions.
Directional attributes determine responsibility for memory management.
As you will see in Section 7.14, memory management for operation parameters varies with the direction and type of parameter. Directional attributes control whether the client or the server is responsible for allocating and deallocating memory for parameters.
Style of Definition
The final three operations on interface Primes all achieve the same thing. Each operation, given some number as a starting point, returns the first prime number that is larger than the starting point. For example, next_prime of 2 is 3, and next_prime
of 26 is 29. Note that the starting point is a signed integer, and that permits negative starting points. For all starting points less than 2, next_prime returns 2. However, each operation offers a different style of interaction.
next_prime accepts the starting point n as an in parameter and returns the prime as the return value.
next_prime2 accepts the starting point n as an in parameter and returns the prime in the out parameter p. The value of p need not be initialized by the client but is modified to contain the result when next_prime2 returns.
next_prime3 uses the single inout parameter n to communicate both the starting point and the result. The client initializes the parameter, and the operation overwrites it with the result.
You would never write an interface like Primes, which offers three operations with identical semantics. Instead, you would decide which style of interaction you wanted to offer to clients. The question is, which style is best, and how do you choose it? Here are some guidelines.
If an operation accepts one or more in parameters and returns a single result, the result should be returned as the return value.
This style is simple and familiar to programmers.
If an operation has several return values of equal importance, all values should be returned as parameters, and the return type of the operation should be .
By making all return values out parameters, you emphasize that none of them is "special" (whereas if one value is returned as the return value and the others are out parameters, you can easily create the impression that the return value is somehow more important).
If an operation returns several values but one of the values is of special importance, make the special value the return value and return the remainder as out parameters.
This style of interaction is most often found on iterator operations. For example:
boolean get_next(out ValueType value);
This operation is used to incrementally retrieve a result set one value at a time. The return value is special because it is not part of the actual result. Instead, it indicates when the set of values is exhausted. Using the return value to indicate the terminating condition is useful for loop control. It allows the caller to write code along the following lines:
while (get_next(val)) { // Process val }
This code is more natural and easier to read than code that tests a Boolean out parameter to detect the terminating condition.
Treat inout parameters with caution.
By using an inout parameter, the designer of the interface assumes that the caller will never want to keep the original value and that it is OK to overwrite it. Therefore, inout
parameters dictate interface policy. If the client wants to keep the original value, it must make a copy first, and that can be inconvenient.
In C++, the equivalent of IDL inout is passing a value by reference. This is typically done for efficiency reasons (pass by reference saves copying the data). IDL inout
parameters do not provide the same savings because on-the-wire transmission forces data copying in both directions. The only saving of inout is in the amount of temporary buffer space required, because clients and servers require only a single block of memory to hold the data before and after the call. Because of this, inout parameters are typically used only for very large values, when local memory consumption becomes an issue.
Overloading
Let's look at the Primes interface once more. A C++ programmer would likely have written it as follows:
typedef unsigned long prime; prime next_prime(in long n);
void next_prime(in long n, out prime p); // Error void next_prime(inout long n); // Error };
Unfortunately, this is not legal IDL. Operation names are scoped by their enclosing interface and must be unique within that interface, so overloading of operations is impossible. This restriction was introduced because overloading makes it difficult to map IDL to a non-OO language such as C. For C, overloaded functions would have to use some form of name mangling (which is fine for a compiler but not very nice for a human developer).
Anonymous Types
Parameters and return values for operations must be declared using a named type. Anonymous types are illegal as a return type and in parameter declarations:
sequence<long> get_longs(); // Error, anonymous type void get_octets(out sequence<octet> s); // Error, anonymous type
Because anonymous types create awkward language mappings, you should make it a habit always to use named types, even when anonymous types are legal. (They are legal as sequence and array elements and as structure, union, and exception member definitions.)
Constant Operations
Unlike C++, IDL does not distinguish between operations for read and write access. The following is in error:
SomeType read_value() const; // Error, illegal const qualifier
As a consequence, if a client has a reference to an object, it can invoke all operations on that object whether or not they modify object state. (On ORBs that provide it, you can use the CORBA Security Service to create read-only access for specific operations.)