PreviousNext

Representation

The DCE IDL compiler supports a feature to allow a network representation of a data type to differ from the representation used by the application. This feature is invoked by using the represent_as attribute on a data type in the Attribute Configuration File (ACF). Applying this attribute to an IDL data type allows the network representation of a data type to be isolated within the generated stubs. The programmer is required to supply four conversion routines when using this feature. The function signatures for these four routines are generated by the IDL compiler. Their purpose is to convert an RPC argument from the application presented type to the network type, convert from the network type to the presented type, and to free memory used by the network and presented types. Presumably, a class library designer could supply the four conversion routines along with the IDL generated stub routines as a library. In this way, the application programmer need not be aware of how the data is transmitted across the wire nor that the conversions take place.

For example, consider the C++ String class which is commonly supplied by C++ compiler vendors or easily implemented by the programmer. The IDL compiler has no notion of a String class since it is not a primitive or constructed IDL type. The class definition must be made known to the IDL compiler by using the include directive to include the class definition into the generated header file. But the DCE runtime does not know how to marshal the String type since its internals are hidden and, in fact, could very well differ in its implementation between vendors.

To allow a String type to be passed as an RPC argument, a network type for a String object is defined in the IDL file to be an array of characters with the string attribute applied. An ACF file is then created for the interface to apply the represent_as attribute to the network type. The following code fragment is for the IDL file represent an IDL character array as a String class in an application:

[ uuid(c5a7c094-c5e3-11ce-bac2-08002be415b2) ]
IText {
typedef [string,unique] char * net_string; /* 1 */
static net_string toUpper([in] net_string s); /* 2 */
static net_string toLower([in] net_string s); /* 3 */
}

The following code is an ACF definition for a String type:


{
include "String"; /* 4 */

typedef [represent_as(String)] net_string; /* 5 */
}

The code is described as follows:

1. A net_string is defined to be a unique pointer to a string

2. The static toUpper( ) operation takes a String argument and returns another one

3. The static toLower( ) operation takes a String argument and returns another one

4. Include the String.h file into the idl-generated header file

5. The network type net_string is presented as the C++ String type in the application

Using automatic binding, the client application would invoke the static toUpper( ) operation as follows:

String s1("Hello, World"); // create a local String object
String s2 = IText::toUpper(s1); // RPC call returns another
// String object

Note that a unique attribute is specified for the net_string type. Unique pointers should always be used when the represent_as attribute is applied to a pointer type.

The routines to convert between the network type and the presented type are automatically invoked by the DCE runtime during the marshalling and unmarshalling process. The IDL compiler generates a function signature to free the presented data type. In this example, this routine would be named net_string_free_local(String *). The purpose of this routine is to free the memory occupied by the stack variable in the server stub that represents the RPC parameter. But since the C++ compiler will generate code to delete local stack objects when the server stub routine is exited, this routine should not free its argument.

The represent_as attribute is properly used when the data comprising the C++ object can be represented by some primitive or constructed IDL data type. The object's data must be accessible by the application. The overhead involved with using the represent_as attribute is the conversion from one type to the other and the freeing of memory.

It may not always be advantageous to use the represent_as attribute to pass a C++ object as an RPC parameter. Consider the case presented earlier where a generic Shape class is used in a class hierarchy with the more specific Square, Circle, and Cylinder shapes derived from it. An application may wish to pass a Shape object as an RPC parameter. Using the represent_as feature would require the conversion routines to convert from a shape to some NDR structure that can be defined in IDL. However, this is complicated by the fact that one IDL type may not be sufficient to represent all possible shapes. To solve this, a discriminated union of different shape types could be defined. But it is also very possible that the internals of the classes are not exposed to the application. The user may have no knowledge of what data types are needed to represent even the simplest shapes such as a square. Furthermore, as new shapes are introduced into the application, the conversion routines would also require extensions to handle the new shapes. An object oriented application should be extensible without requiring such overhead.

Another drawback to using conversion routines is efficiency. Consider a common C++ Stack class and a distributed implementation of a reverse Polish notation algorithm. The algorithm maintains a stack of operands. When an operator is processed, the required number of operands are popped off the stack, the operation is performed on them, and the result is pushed back onto the stack. For this example, assume that the algorithm supports the plus( ), minus( ), multiply( ) and divide( ) binary operations. In order to illustrate the distributed nature of the algorithm, we can further assume that the client reads an equation in reverse Polish notation from standard input and maintains the stack locally, but the binary operations are implemented within a remote server process. Hence, the server process needs access to the same stack as the client. Simply passing the stack to the server process in its entirety would be inefficient since only the top two elements need to be accessed per operation. A large stack would quickly degrade the performance of the algorithm, especially since the stack would have to be passed as both an input and output parameter.