@(#)DIFFERENCES 1.1 95/09/13 Note -- this documentation was distributed with the original SunSoft IIOP reference implementation and has very little relevance to what's in TAO. We include it here only for "historical" reference, i.e., you should disregard most of what's in this document. Doug Schmidt ---------------------------------------- [ NOTE that the CORBA 2.0 specifications have now been published, but are not yet sufficiently available that this document can usefully just list any differences between that specification and this software. As a rule, there are no differences between this software and CORBA 2.0 in areas where CORBA 2.0 has specified an interface. ] This document summarizes known differences between what is implemented in this software and currently available OMG specifications. As a rule, these establish compatibility with revised specifications that are currently being prepared for publication. However, in some cases the differences resolve problems that are currently being addressed by ORB taskforces. When those revised CORBA specifications are published, this document will be updated to reflect any remaining differences from them. The reason for those changes is that different specifications were adopted at the same time and there were in some cases subtle differences which need to be resolved. Minor errata have also been fixed. None of these changes are substantial, with the exception of the TypeCode interpreter API which was added to the C++ mapping. Also, note that the goal of this software is fidelity to the IIOP, so that issues relating (in particular) to the C++ language mapping or other OMG specifications were judged to be of less importance at this time. DIFFERENCES from IIOP in UNO Spec (95-3-10) ------------------------------------------- Some changes to the original specification (94-9-32) for the IIOP protocol were found to be necessary; most related to the subsequently adopted Interface Repository (IFR) specification (94-11-7). Others resulted from correction of minor editorial errors, and experience gained as multiple teams implement to that specification. The bulk of those changes have been incorporated into the 95-3-10 document, but these few have not. CDR TYPECODE INDIRECTION An additional constraint on the use of typecode indirection has been identified. Specifically, the typecode to which an indirection points be "self-sufficient", and may not point "outside of itself" for any further indirections. For example, always indirections occur within encapsulations, so it's OK for the indirection to point anywhere in that encapsulation, or at the encoded TCKind enum value immediately before the encapsulation's length. For typecode encapsulations nested inside other typecode encapsulations, the indirection may point no further than the outermost encapsulation. Also, when an indirection points to another typecode, the byte order of that other typecode must be deduced from the encoded value found there. This is straightforward for encoded TCKind values, all of which fit into a single byte: if the first byte of the word is zero, then the encoding is big-endian, else it's little-endian. Indirecting to another indirection is not allowed, since the byte order of the encoded offset can't consistently be deduced. MISCELLANY The type GIOP::Version is assumed to be identical to IIOP::Version. As part of the editorial separation of GIOP from its Internet version IIOP, this typedef was accidentally omitted. This implementation of IIOP supports the OMG-IDL Primitive Data Types as defined in Appendix A of 94-9-32, but with TCKind numbers following the new tk_exception and tk_alias codes. (That is, rather than using range 21-25, these TCKind values are in the range 23-27.) These data types are currently the topic of an RFP in process in the OMG, and are not currently defined as part of a current OMG specification. Clients and servers using these extended data types are relying on CORBA extensions that are not yet standardized; and should be prepared to change. CORBA 2.0/CORE Differences -------------------------- As of this writing, the new CORBA 2.0/CORE document has not yet been made available. This is a modification of the CORBA 1.2 document, with the addition of (mandatory) extensions from the UNO (94-9-32) specification: notably DSI and some new object reference operations. This summarizes differences between the as-yet-unpublished CORBA 2.0/CORE document and the original text in the UNO specification. DYNAMIC SKELETON INTERFACE Since the new "tk_except" typecodes now include the exception ID (this is the repository ID describing the exception's type) the "exception" operation used to report an exception through a ServerRequest no longer includes the exception ID. Since the OperationDef entries for attributes are no longer found in the interface repositories, the ServerRequest convenience operation to return this has been removed. Application programs (such as bridges) must compare the operation name string with the prefixes "_get_" and "_set_" to see if any given operation applies to an attribute. If an operation is one of the two for which an OMG-IDL "attribute" declaration is shorthand, then the application must search the interface repository for that attribute. (There are in fact three categories of operations on which an object adapter and implementation collaborate to handle: user defined operations, user defined attributes, and operations defined by CORBA such as "is_a" and "get_implementation". Some object adapters, like the BOA, handle this last category directly, rather than exposing it to applications.) The "non_existent" objref operation accidentally used attribute syntax; this has been corrected. All operations on CORBA::Object references now use normal operation syntax in their (pseudo) IDL definitions, and are uniformly prefixed with underscores as part of their C++ mapping. DIFFERENCES from IDL C++ Mapping (94-9-14) ------------------------------------------ There are four basic ways in which the IIOP framework does not comply with the OMG's IDL C++ mapping specification, beyond the use of the CORBA2 module/namespace rather than the CORBA module/namespace. (CORBA2 is used so that this software can be linked with existing ORBs, which should be using the CORBA module/namespace already.) These ways are beyond the portability-derived use of the "class" construct (instead of the C++ namespace construct), and the use of CORBA2::Environment (rather than C++ exceptions), both of which are accomodated by the mapping: (1) This framework does not attempt to be complete. Completeness is not required to implement the Internet IOP, so time was not spent in providing a complete mapping. (2) In some cases the API provided is not the one in the mapping. For example CORBA2::Environment is not always passed in all operations. This was done to promote ease of implementation and coding. (3) Implementation details are generally exposed. This isn't so much a noncompliance issue (it's not specified that such details must be hidden) as an issue of allowing noncompliant applications. The effort required to actively prevent use of implementation details by higher level code would be nontrivial, and could slow down IIOP code, so no time was applied to hiding such details. (However, note that when the software was modified to use COM, standard C++ techniques were used to hide most such details.) (4) The C++ mapping needs completion in some areas. Notably, 94-9-14 does not satisfy requirements to provide support for for all data types in an "Any" or exception without needing any precompiled data type support routines. The TypeCode interpreter addresses this issue. Some additional operations were not adopted at the time 94-9-14 was defined; both 94-9-32 and 94-11-7 added new ORB interfaces which are not found in the initial C++ mapping document. With respect to this fourth issue, descriptions of the API extensions used is provided later in this file. The additional CORBA operations are not described since their mapping is straightforward if it isn't given in those documents. ACCESS TO ALL DATA IN "ANY" AND EXCEPTIONS The "void *" value in an "Any", and any "CORBA2::Exception *" value, may always be passed to the TypeCode interpreter (see next). This allows access to all data held within an "Any" and an Exception. The "void *" value in an Any with an exception TypeCode is of type CORBA2::Exception *". Due to the way CORBA is specified, it is not possible to tell whether an "Any" holding an exception denotes a user or standard (system-defined) exception except by exhaustive comparison against exception IDs. Also, _all_ legal OMG-IDL data types may be held in an "Any", and may be manipulated using the TypeCode interpreter. This satisfies a language mapping requirement that DII (and DSI) be able to manipulate all OMG-IDL data types without requiring compiler generated support for them. TYPECODE INTERPRETER In any given C/C++ execution environment there is a binary standard for how data is represented. While that standard will differ between execution environments, there are a very limited number of ways in which those representations differ. Those differences are encapsulated only in the TypeCode interpreter, so that portable applications using the dynamic typing infrastructure in the ORB can completely ignore how it's done in any particular environment. The APIs in this software distribution have been suggested for adoption as part of the OMG specification suite. The two APIs are nonstatic member functions in the C++ mapping's TypeCode pseudo-object, and are augmented by a single new enumeration type. unsigned long size() This returns the size of an instance of the type that is described by the TypeCode. For example, when invoked on the typecode constant CORBA::_tc_Short, the value returned is sizeof(CORBA::Short); and when invoked on the typecode for a structure, it is the size of that structure (including any internal and tail padding needed). When invoked on a sequence typecode, it returns a value that does not include the size for any embedded buffer. enum traverse_status {TRAVERSE_STOP, TRAVERSE_CONTINUE }; This is a data type used in the traverse() member function. It allows data type traversal to be terminated early for non-exceptional conditions, and eliminates the confusion that some similar APIs have created when they use a single boolean value (does TRUE mean to stop, or to continue?). traverse_status traverse ( const void *value1; const void *value2 traverse_status visit ( TypeCode_ptr tc, const void *visit_value1, const void *visit_value2, void *visit_context ), void *context ); (In the current language mapping, CORBA2::Environment references are passed as the final parameter to the 'traverse' and 'visit' routines for use when reporting exceptions.) The pointers "value1" and "value2" point to instances of the data type described by the typecode (or are null pointers). For each constituent of that data type (e.g. structure member) the visit() routine is called once. The constituent's type is described by "tc"; "visit_value1" points to the constituent of that type in "value1" (assuming the traverse routine was not passed a null pointer) and similarly for "visit_value2". The "visit_context" parameter is the context parameter passed to the traverse() routine, and can point to whatever data is needed by the visit() routine. Members are traversed in first-to-last order, as defined in the IDL specification for the data type. So for example, the visit routine for a structure could print out each element on a IO stream passed through the context parameter, perhaps in a neatly formatted form intended for human consumption. The visit() function may choose to recursively traverse() each element. So for example if "tc->kind()" in a visit routine returned CORBA::tk_struct, the contents of that struct would be ignored unless the visit routine invoked traverse() using the typecode and values passed to it. If the visit() routine returns TRAVERSE_CONTINUE, succeeding constituents of the data type are visited in turn; if it returns TRAVERSE_STOP, the traverse() routine returns that value to its caller. Of course, if a visit() routine calls traverse() itself, it may choose to ignore TRAVERSE_STOP. The traverse() routine is a powerful tool. It is used in the IIOP code itself in several ways; look at such use for tutorial details. You could implement data value comparison and "debug" data dumping as simple exercises in the use of these APIs. The "marshaling interpreter" (marshal.cc) uses it to encode and decode values according to the IIOP protocol specification. The "Any" code (any.cc) uses traverse() both to free embedded pointers, and to make "deep" copies of any data structure given its TypeCode. Only that "deep copy" visit routine uses "value2" as anything other than a null pointer; it allows efficient construction of "deep copies" without needing extra space for temporary values or coroutine stacks. (A general purpose two-value comparison could also use "value2".) Most uses of the API only manipulate a single data value at a time; no realistic need has yet been seen for manipulating more than two data values at once. With respect to the OMG C and C++ mappings, it is clear that this style API must be provided for the C mapping, but some people have noted that a "purer" object oriented style API could also be provided in C++. That style would use another internal visit routine, performing the requisite "switch" over the fixed number of TCKind values, and then make a virtual function call to an instance of a C++ class whose private state was the "context" and whose member functions matched the arms of the switch. MEMORY ALLOCATION In order to dynamically manipulate instances of arbitrary data types, applications need to be able to allocate and free memory. The OMG C++ mapping only says how to do this for data types which have static C++ interfaces defined, which is clearly inadequate for using constructed types with the DII/DSI/Any family of interfaces. This infrastructure requires the standard "malloc" and "free" primitives to be used, and cast to the appropriate type. Data allocated using malloc will normally be sized according to TypeCode::size(), and then be stored inside an Any. When the Any is deleted, if the ORB deallocates the memory it always uses "free" (including for any nested pointers, and correctly handling cases such as arrays and sequences). Note that to support implementations where C and C++ language bindings share the same ORB infrastructure, this solution is inadequate. This is because the C binding's CORBA_free() interface would have no way to determine the type of the data being freed. Instead, typed allocation APIs will need to be used even when using the dynamically typed CORBA subsystem ... a TypeCode::malloc() routine would suffice, if it returned memory that was internally tagged with that TypeCode. In such a case, the CORBA_free() routine could use that TypeCode to "deep free" data as required, and C++ "new" and "delete" operators would need to know about the internal tagging for all those data types. Such tagged allocation would need to be used for all data that was to be freed by the ORB. (Having the C mapping require use of "typed free" routines, instead of the CORBA_free interface, is sufficient to eliminate this problem.) PASSING EXCEPTIONS THROUGH THE DII The C++ mapping's Dynamic Invocation Interface (DII) has key omissions in that it doesn't say how to access to user-defined exception values, and implicitly requires ORBs either to pass "excess" data on the wire or else to consult an interface repository to deal with exceptions or (deprecated) "context" strings. This software changes the DII specification in two ways to support the requirement for DII users to be able to see user-defined exceptions, yet not to violate the OMG-IDL type model by reporting illegal exceptions: * The Object::_create_request() member function, through which DII users provide all the information included in an IDL operation's signature, has an additional parameter. That parameter is a sequence of exception typecodes, describing the user-defined exceptions that the operation can return. (The standard exceptions defined in CORBA may always be returned.) When any other exception is returned, the client's ORB reports that the object's type definition has been violated by the server's ORB. * The TypeCode interpreter (see above) may be used to examine all exception values reported through a CORBA::Environment. A new Exception::id() operation may be used to determine which typecode should be used. Note that a number of portability and specification problems have been identified in the current C++ mapping for DII, e.g. for details of memory management. Later versions of this code may attempt to closely comply with an improved mapping for DII, to the extent that the interpretation used here differs from that more unambiguous specification. Since there is no efficient way to distinguish typecodes for user defined exceptions from ones for system-defined ("standard") exceptions (comparing the exception ID against all the system defined ones is inefficient :-) a new enum type "ExceptionType" is defined. (An analogous type is already defined for the C language mapping.) This is used to report exceptions through DII and DSI.