- •Advanced CORBA® Programming with C++
- •Review
- •Dedication
- •Preface
- •Prerequisites
- •Scope of this Book
- •Acknowledgments
- •Chapter 1. Introduction
- •1.1 Introduction
- •1.2 Organization of the Book
- •1.3 CORBA Version
- •1.4 Typographical Conventions
- •1.5 Source Code Examples
- •1.6 Vendor Dependencies
- •1.7 Contacting the Authors
- •Part I: Introduction to CORBA
- •Chapter 2. An Overview of CORBA
- •2.1 Introduction
- •2.2 The Object Management Group
- •2.3 Concepts and Terminology
- •2.4 CORBA Features
- •2.5 Request Invocation
- •2.6 General CORBA Application Development
- •2.7 Summary
- •Chapter 3. A Minimal CORBA Application
- •3.1 Chapter Overview
- •3.2 Writing and Compiling an IDL Definition
- •3.3 Writing and Compiling a Server
- •3.4 Writing and Compiling a Client
- •3.5 Running Client and Server
- •3.6 Summary
- •Part II: Core CORBA
- •Chapter 4. The OMG Interface Definition Language
- •4.1 Chapter Overview
- •4.2 Introduction
- •4.3 Compilation
- •4.4 Source Files
- •4.5 Lexical Rules
- •4.6 Basic IDL Types
- •4.7 User-Defined Types
- •4.8 Interfaces and Operations
- •4.9 User Exceptions
- •4.10 System Exceptions
- •4.11 System Exceptions or User Exceptions?
- •4.12 Oneway Operations
- •4.13 Contexts
- •4.14 Attributes
- •4.15 Modules
- •4.16 Forward Declarations
- •4.17 Inheritance
- •4.18 Names and Scoping
- •4.19 Repository Identifiers and pragma Directives
- •4.20 Standard Include Files
- •4.21 Recent IDL Extensions
- •4.22 Summary
- •Chapter 5. IDL for a Climate Control System
- •5.1 Chapter Overview
- •5.2 The Climate Control System
- •5.3 IDL for the Climate Control System
- •5.4 The Complete Specification
- •Chapter 6. Basic IDL-to-C++ Mapping
- •6.1 Chapter Overview
- •6.2 Introduction
- •6.3 Mapping for Identifiers
- •6.4 Mapping for Modules
- •6.5 The CORBA Module
- •6.6 Mapping for Basic Types
- •6.7 Mapping for Constants
- •6.8 Mapping for Enumerated Types
- •6.9 Variable-Length Types and _var Types
- •6.10 The String_var Wrapper Class
- •6.11 Mapping for Wide Strings
- •6.12 Mapping for Fixed-Point Types
- •6.13 Mapping for Structures
- •6.14 Mapping for Sequences
- •6.15 Mapping for Arrays
- •6.16 Mapping for Unions
- •6.17 Mapping for Recursive Structures and Unions
- •6.18 Mapping for Type Definitions
- •6.19 User-Defined Types and _var Classes
- •6.20 Summary
- •Chapter 7. Client-Side C++ Mapping
- •7.1 Chapter Overview
- •7.2 Introduction
- •7.3 Mapping for Interfaces
- •7.4 Object Reference Types
- •7.5 Life Cycle of Object References
- •7.6 Semantics of _ptr References
- •7.7 Pseudo-Objects
- •7.8 ORB Initialization
- •7.9 Initial References
- •7.10 Stringified References
- •7.11 The Object Pseudo-Interface
- •7.12 _var References
- •7.13 Mapping for Operations and Attributes
- •7.14 Parameter Passing Rules
- •7.15 Mapping for Exceptions
- •7.16 Mapping for Contexts
- •7.17 Summary
- •Chapter 8. Developing a Client for the Climate Control System
- •8.1 Chapter Overview
- •8.2 Introduction
- •8.3 Overall Client Structure
- •8.4 Included Files
- •8.5 Helper Functions
- •8.6 The main Program
- •8.7 The Complete Client Code
- •8.8 Summary
- •Chapter 9. Server-Side C++ Mapping
- •9.1 Chapter Overview
- •9.2 Introduction
- •9.3 Mapping for Interfaces
- •9.4 Servant Classes
- •9.5 Object Incarnation
- •9.6 Server main
- •9.7 Parameter Passing Rules
- •9.8 Raising Exceptions
- •9.9 Tie Classes
- •9.10 Summary
- •Chapter 10. Developing a Server for the Climate Control System
- •10.1 Chapter Overview
- •10.2 Introduction
- •10.3 The Instrument Control Protocol API
- •10.4 Designing the Thermometer Servant Class
- •10.5 Implementing the Thermometer Servant Class
- •10.6 Designing the Thermostat Servant Class
- •10.7 Implementing the Thermostat Servant Class
- •10.8 Designing the Controller Servant Class
- •10.9 Implementing the Controller Servant Class
- •10.10 Implementing the Server main Function
- •10.11 The Complete Server Code
- •10.12 Summary
- •Chapter 11. The Portable Object Adapter
- •11.1 Chapter Overview
- •11.2 Introduction
- •11.3 POA Fundamentals
- •11.4 POA Policies
- •11.5 POA Creation
- •11.6 Servant IDL Type
- •11.7 Object Creation and Activation
- •11.8 Reference, ObjectId, and Servant
- •11.9 Object Deactivation
- •11.10 Request Flow Control
- •11.11 ORB Event Handling
- •11.12 POA Activation
- •11.13 POA Destruction
- •11.14 Applying POA Policies
- •11.15 Summary
- •Chapter 12. Object Life Cycle
- •12.1 Chapter Overview
- •12.2 Introduction
- •12.3 Object Factories
- •12.4 Destroying, Copying, and Moving Objects
- •12.5 A Critique of the Life Cycle Service
- •12.6 The Evictor Pattern
- •12.7 Garbage Collection of Servants
- •12.8 Garbage Collection of CORBA Objects
- •12.9 Summary
- •Part III: CORBA Mechanisms
- •Chapter 13. GIOP, IIOP, and IORs
- •13.1 Chapter Overview
- •13.2 An Overview of GIOP
- •13.3 Common Data Representation
- •13.4 GIOP Message Formats
- •13.5 GIOP Connection Management
- •13.6 Detecting Disorderly Shutdown
- •13.7 An Overview of IIOP
- •13.8 Structure of an IOR
- •13.9 Bidirectional IIOP
- •13.10 Summary
- •14.1 Chapter Overview
- •14.2 Binding Modes
- •14.3 Direct Binding
- •14.4 Indirect Binding via an Implementation Repository
- •14.5 Migration, Reliability, Performance, and Scalability
- •14.6 Activation Modes
- •14.7 Race Conditions
- •14.8 Security Considerations
- •14.9 Summary
- •Part VI: Dynamic CORBA
- •Chapter 15 C++ Mapping for Type any
- •15.1 Chapter Overview
- •15.2 Introduction
- •15.3 Type any C++ Mapping
- •15.4 Pitfalls in Type Definitions
- •15.5 Summary
- •Chapter 16. Type Codes
- •16.1 Chapter Overview
- •16.2 Introduction
- •16.3 The TypeCode Pseudo-Object
- •16.4 C++ Mapping for the TypeCode Pseudo-Object
- •16.5 Type Code Comparisons
- •16.6 Type Code Constants
- •16.7 Type Code Comparison for Type any
- •16.8 Creating Type Codes Dynamically
- •16.9 Summary
- •Chapter 17. Type DynAny
- •17.1 Chapter Overview
- •17.2 Introduction
- •17.3 The DynAny Interface
- •17.4 C++ Mapping for DynAny
- •17.5 Using DynAny for Generic Display
- •17.6 Obtaining Type Information
- •17.7 Summary
- •Part V: CORBAservices
- •Chapter 18. The OMG Naming Service
- •18.1 Chapter Overview
- •18.2 Introduction
- •18.3 Basic Concepts
- •18.4 Structure of the Naming Service IDL
- •18.5 Semantics of Names
- •18.6 Naming Context IDL
- •18.7 Iterators
- •18.8 Pitfalls in the Naming Service
- •18.9 The Names Library
- •18.10 Naming Service Tools
- •18.11 What to Advertise
- •18.12 When to Advertise
- •18.13 Federated Naming
- •18.14 Adding Naming to the Climate Control System
- •18.15 Summary
- •Chapter 19. The OMG Trading Service
- •19.1 Chapter Overview
- •19.2 Introduction
- •19.3 Trading Concepts and Terminology
- •19.4 IDL Overview
- •19.5 The Service Type Repository
- •19.6 The Trader Interfaces
- •19.7 Exporting Service Offers
- •19.8 Withdrawing Service Offers
- •19.9 Modifying Service Offers
- •19.10 The Trader Constraint Language
- •19.11 Importing Service Offers
- •19.12 Bulk Withdrawal
- •19.13 The Admin Interface
- •19.14 Inspecting Service Offers
- •19.15 Exporting Dynamic Properties
- •19.16 Trader Federation
- •19.17 Trader Tools
- •19.18 Architectural Considerations
- •19.19 What to Advertise
- •19.20 Avoiding Duplicate Service Offers
- •19.21 Adding Trading to the Climate Control System
- •19.22 Summary
- •Chapter 20. The OMG Event Service
- •20.1 Chapter Overview
- •20.2 Introduction
- •20.3 Distributed Callbacks
- •20.4 Event Service Basics
- •20.5 Event Service Interfaces
- •20.6 Implementing Consumers and Suppliers
- •20.7 Choosing an Event Model
- •20.8 Event Service Limitations
- •20.9 Summary
- •Part VI: Power CORBA
- •Chapter 21. Multithreaded Applications
- •21.1 Chapter Overview
- •21.2 Introduction
- •21.3 Motivation for Multithreaded Programs
- •21.4 Fundamentals of Multithreaded Servers
- •21.5 Multithreading Strategies
- •21.6 Implementing a Multithreaded Server
- •21.7 Servant Activators and the Evictor Pattern
- •21.8 Summary
- •22.1 Chapter Overview
- •22.2 Introduction
- •22.3 Reducing Messaging Overhead
- •22.4 Optimizing Server Implementations
- •22.5 Federating Services
- •22.6 Improving Physical Design
- •22.7 Summary
- •Appendix A. Source Code for the ICP Simulator
- •Appendix B. CORBA Resources
- •Bibliography
IT-SC book: Advanced CORBA® Programming with C++
Figure 4.2 Development process for different development environments.
Because only the stubs are used by the client, the client developer simply ignores the skeleton generated by the IDL compiler or suppresses the skeleton code generation.
4.4 Source Files
The IDL specification defines a number of rules for the naming and contents of IDL source files.
4.4.1 File Naming
The names of source files containing IDL definitions must end in .idl. For example, CCS.idl is a valid source file name. An IDL compiler is free to reject source files having other file name extensions.
For file systems that are case-insensitive (such as DOS), the case of the file name extension is ignored, so CCS.IDL is legal. For file systems that are case-sensitive (such as UNIX), the extension must be in lowercase and CCS.IDL is not legal.
4.4.2 File Format
IDL is a free-form language. This means that IDL allows free use of spaces, horizontal and vertical tab stops, form feeds, and newline characters (any of these characters serves as a token separator). Layout and indentation do not carry semantics, so you can choose any textual style you prefer. You may wish to follow the style we have used for the IDL examples throughout this book. These examples follow the OMG style guide for IDL.
62
IT-SC book: Advanced CORBA® Programming with C++
4.4.3 Preprocessing
IDL source files are preprocessed. The preprocessor can be implemented as part of the compiler, or it can be an external program. However, its behavior is identical to the C++ preprocessor. This means that the usual C++ rules for lexical translation phases apply: the preprocessor maps source file characters onto the source character set, replaces trigraphs, concatenates lines ending in a backslash, replaces comments with white space, and so on. The most common use of the preprocessor is for #include directives. This permits an IDL definition to use types defined in a different source file. You may also want to use the preprocessor to guard against double inclusion of a file:
#ifndef _MYMODULE_IDL_ #define _MYMODULE_IDL_
module MyModule { /* ... */ };
#endif /* _MYMODULE_IDL_ */
Another frequent use of the preprocessor is to control the repository IDs that are generated by the compiler with #pragma directives. We look at the #pragma directives specified by CORBA in Section 4.19.
4.4.4 Definition Order
IDL constructs, such as modules, interfaces, or type definitions, can appear in any order you prefer. However, identifiers must be declared before they can be used.
4.5 Lexical Rules
IDL's lexical rules are almost identical to those of C++ except for some differences in identifiers.
4.5.1 Comments
IDL definitions permit both the C and the C++ style of writing comments:
/*
* This is a legal IDL comment. */
// This IDL comment extends to the end of this line.
4.5.2 Keywords
IDL uses a number of keywords, which must be spelled in lowercase. For example, interface and struct are keywords and must be spelled as shown. There are three
63
IT-SC book: Advanced CORBA® Programming with C++
exceptions to this lowercase rule: Object, TRUE, and FALSE are all keywords and must be capitalized as shown.
4.5.3 Identifiers
Identifiers begin with an alphabetic character followed by any number of alphabetics, digits, or underscores. Unlike C++ identifiers, IDL identifiers cannot have a leading underscore (but see also Section 4.21.5). In addition, IDL identifiers cannot contain non-English letters, such as Å, because that would make it very difficult to map IDL to target languages that lack support for such characters.
Case Sensitivity
Identifiers are case-insensitive but must be capitalized consistently. For example, TimeOfDay and TIMEOFDAY are considered the same identifier within a naming scope. However, IDL enforces consistent capitalization. After you have introduced an identifier, you must capitalize it consistently throughout; otherwise, the compiler will reject it as illegal. This rule exists to permit mappings of IDL to languages (such as Pascal) that ignore case in identifiers as well as to languages (such as C++) that treat differently capitalized identifiers as distinct.
Identifiers That Are Keywords
IDL permits you to create identifiers that happen to be keywords in one or more implementation languages. For example, while is a perfectly good IDL identifier but of course is a keyword in many implementation languages. Each language mapping defines its own rules for dealing with IDL identifiers that are keywords. The solution typically involves using a prefix to map away from the keyword. For example, the IDL identifier while is mapped to _cxx_while in C++.
This rule for dealing with keywords is workable but results in hard-to-read source code. Identifiers such as package, then, import, PERFORM, and self will clash with some implementation language or other. To make life easier for developers (possibly yourself), you should try to avoid IDL identifiers that are likely to be implementation language keywords.
4.6 Basic IDL Types
IDL provides a number of built-in basic types, and they are shown in Table 4.1.
Table 4.1. IDL basic types.
Type |
|
|
Range |
Size |
short |
-215 |
to 215 |
-1 |
= 16 bits |
long |
-231 |
to 231 |
-1 |
= 32 bits |
unsigned short |
0 to 216-1 |
|
= 16 bits |
|
unsigned long |
0 to 232-1 |
|
= 32 bits |
|
float |
IEEE single-precision |
= 32 bits |
64
IT-SC book: Advanced CORBA® Programming with C++
double |
IEEE double-precision |
= 64 bits |
char |
ISO Latin-1 |
= 8 bits |
string |
ISO Latin-1, except ASCII NUL |
Variable-length |
boolean |
TRUE or FALSE |
Unspecified |
octet |
0–255 |
= 8 bits |
any |
Run-time identifiable arbitrary type |
Variable-length |
The CORBA specification requires that language mappings preserve the size of these types as shown. The value ranges shown in Table 4.1 need not be maintained by all language mappings, but CORBA requires implementations to document any deviations from the specified ranges. (The C++ mapping preserves all value ranges.)
These requirements may sound confusing. For example, when you look at the size requirements, you will find that IDL specifies only a lower bound instead of an exact size. The reason is that some CPU architectures do not have, for example, an 8-bit character type or a 16-bit integer type; on such CPUs, these types are mapped to a type larger than 8 or 16 bits. Similarly, some language mappings cannot preserve the full range of all types; for example, Java does not have unsigned integers and maps both unsigned long and long to Java int. To avoid restricting the possible target environments and languages, the CORBA specification leaves the size and range requirements for IDL basic types loose.
All the basic types (except octet) are subject to changes in representation as they are transmitted between clients and servers. For example, a long value undergoes byte swapping when sent from a big-endian to a little-endian machine. Similarly, characters undergo translation in representation if they are sent from an EBCDIC to an ASCII implementation. What happens if a character does not have a precise match in the target character set is implementation-dependent. For example, the EBCDIC character ¬ does not have an ASCII equivalent. An ORB might translate EBCDIC ¬ into ASCII ~, or it might raise a DATA_CONVERSION exception (see Section 4.10) to indicate that translation is impossible. Characters may also change in size (not all architectures use 8- bit characters). However, these changes are transparent to the programmer and do exactly what is required.
Table 4.1 does not include a pointer type. There are a number of good reasons for this. Pointer types are used much less in object-oriented programming than in non-OO languages.
Some implementation languages (such as COBOL and Java) do not support pointers. Pointers would complicate the implementation of marshaling for ORB vendors and would incur additional run-time costs.
As you will see in Section 4.8.2, the lack of pointers is no great hardship. IDL uses object references to achieve what in a non-OO environment would normally be done with a pointer. In effect, object references are pointers. However, object references can denote
65
IT-SC book: Advanced CORBA® Programming with C++
only objects but cannot point to data. IDL supports recursive data types, such as trees, without introducing a data pointer type (see Section 4.7.8).
CORBA recently extended IDL to support additional numeric and character types. Because many ORBs do not yet provide these types, we cover them separately in
Section 4.21.
4.6.1 Integer Types
IDL does not have a type int, so there are no guessing games as to its range. An IDL short is mapped to at least a 2-byte type, and IDL long is mapped to at least a 4-byte type.
Some languages (notably Java) do not support unsigned types. Because of this, unsigned short and unsigned long map to Java short and int, respectively. This means that a Java programmer must ensure that large unsigned IDL values are treated correctly when represented as Java signed values.
4.6.2 Floating-Point Types
These types follow the IEEE specification for singleand double-precision floating-point representation [7]. If an implementation cannot support IEEE format floating-point values, it must document how it deviates from the IEEE specification.
4.6.3 Characters
IDL characters support the ISO Latin-1 character set [8], which is a superset of ASCII. The bottom 128 character positions (0–127) are identical to ASCII. The top 128 character positions (128–255) are taken up by characters such as Å, ß, and Ç. This arrangement allows most European languages to be used with an 8-bit character set. Recently, IDL was extended to support wide characters and strings. This permits use of arbitrary wide character sets, such as Unicode.
4.6.4 Strings
IDL strings support the ISO Latin-1 character set with the exception of ASCII NUL (0). Disallowing NUL inside IDL strings is a concession to C and C++; the notion of NULterminated strings is so deeply ingrained in C and C++ that allowing embedded NUL characters would make the use of IDL strings impossibly difficult in these languages.
IDL strings can be bounded or unbounded. An unbounded string has the IDL type string and can grow to any length. A bounded string type specifies an upper limit on the length of the string. For example, string<10> is a string type that permits only strings of up to ten characters.
66
IT-SC book: Advanced CORBA® Programming with C++
The bound of a string does not include any terminating NUL character, so the string "Hello" will fit into a string of type string<5>. (Many programming languages do not represent strings as NUL-terminated arrays, so the concept of NUL termination does not apply to IDL.)
Most C and C++ ORB implementations ignore bounded strings and treat them as if they were unbounded. This limitation arises because C and C++ do not support bounded strings natively, and emulating bounded string support would result in awkward language mappings. As a C++ programmer, you are made responsible for enforcing the bound at run time.
4.6.5 Booleans
Boolean values can have only the values TRUE and FALSE. IDL makes no requirement as to how these values are to be represented in particular languages nor about the size of a Boolean value.
4.6.6 Octets
The IDL type octet is an 8-bit type that is guaranteed not to undergo any changes in representation as it is transmitted between address spaces. This guarantee permits exchange of binary data so that it is not tampered with in transit. All other IDL types are subject to changes in representation during transmission.
4.6.7 Type any
Type any is a universal container type. A value of type any can hold a value of any other IDL type, such as long or string, or even another value of type any. Type any can also hold object references or user-defined complex types, such as arrays or structures.
Type any is useful when you do not know at compile time what IDL types you will eventually need to transmit between client and server. Type any is IDL's equivalent of what in C++ is typically achieved with a void * or a stdarg variable argument list. However, type any is substantially safer because it is self-describing (you can find out at run time what type of value is contained in an any). Manipulation of values of type any is type-safe; attempts to, for example, extract a float as a string return an error indication. As a result, careless misinterpretation of a value as the wrong type is much less likely than it is with the completely type-unsafe mechanism of using a void *.
We look at type any and its C++ mapping in detail in Chapter 15.
67