- •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++
Why Create Nil References?
Clients create nil references mainly to indicate "not there" or "optional" semantics, much as a C++ null pointer can be used to mean "not there." For example, the CORBA Event Service (see Chapter 20) allows a client optionally to pass an object reference and thereby be informed of disconnection from an event channel. If the client passes a non-nil reference, it indicates that it wants to be informed of disconnection. If the client passes a nil reference, it indicates that it does not want to know about disconnection. Simplified, the corresponding IDL looks something like this:
interface Callback { void disconnect();
};
interface Channel {
SomeType register_me(in Callback c); // ...
};
If the client does not care about disconnection, it can pass a nil reference to register_me:
Channel_ptr ch = ...; // Get a channel reference...
//Tell the channel we don't want to know about disconnects Callback_ptr nil_cb = Callback::_nil();
SomeType st = ch->register_me(nil_cb);
//Use channel for other things...
By passing a nil reference, the client conveys the "not there" semantics (there is no callback object for the server to use).
We discuss this callback pattern in more detail in Section 20.3.
7.6 Semantics of _ptr References
As you saw in the preceding section, _ptr references act as handles to an underlying proxy. In this section, we examine the semantics of _ptr references in more detail and consider how inheritance affects the use of _ptr references.
7.6.1 Mapping for Proxies and _ptr References
Consider part of the IDL for the climate control system:
// ...
module CCS {
// ...
typedef short TempType;
213
IT-SC book: Advanced CORBA® Programming with C++
interface Thermometer {
readonly attribute TempType temperature; // ...
};
interface Thermostat : Thermometer { TempType get_nominal();
// ...
}; // ...
};
Following is one possible way for an ORB to map these interfaces to proxy classes and their associated _ptr references:
namespace CORBA { class Object; class Object_var;
typedef Object * Object_ptr; class Object {
public:
static Object_ptr _duplicate(Object_ptr p); static Object_ptr _nil();
static Object_ptr _narrow(Object_ptr p); // Other member functions here...
typedef Object_var _var_type; typedef Object_ptr _ptr_type;
};
Boolean is_nil(Object_ptr p); // ...
}
namespace CCS { // ...
class Thermometer; class Thermometer_var;
typedef Thermometer * Thermometer_ptr;
class Thermometer : public virtual CORBA::Object { public:
static Thermometer_ptr _duplicate(Thermometer_ptr p); static Thermometer_ptr _nil();
static Thermometer_ptr _narrow(CORBA::Object_ptr p);
// Member functions for attributes of Thermometer here...
typedef Thermometer_var _var_type; typedef Thermometer_ptr _ptr_type;
};
class Thermostat; class Thermostat_var;
typedef Thermostat * Thermostat_ptr;
class Thermostat : public virtual Thermometer { public:
static Thermostat_ptr _duplicate(Thermostat_ptr p); static Thermostat_ptr _nil();
static Thermostat_ptr _narrow(CORBA::Object_ptr p);
214
IT-SC book: Advanced CORBA® Programming with C++
// Member functions for operations of Thermostat here...
typedef Thermostat_var _var_type; typedef Thermostat_ptr _ptr_type;
}; // ...
}
Before we launch into the details of this mapping, we need to note that the C++ mapping specification does not require the precise mapping shown. For example, an ORB could choose to implement a _ptr reference as a class instead of a C++ pointer. However, the mapping requires that a compliant ORB must preserve the semantics of the mapping just shown. This means that even if a _ptr reference is not implemented as a C++ pointer, it must behave as if it were a C++ pointer.
The C++ mapping deliberately phrases its requirements this way to give ORB vendors maximum freedom in how they implement an ORB for particular environments. At the same time, the mapping guarantees source code portability among different ORBs. All the code examples shown in this book are fully compliant with the mapping and therefore are portable. We also point out constructs that happen to work with many ORBs but nevertheless are nonportable.
Note that we delay until Section 18.14.1 discussion of the _var_type and _ptr_type definitions that appear at the end of each proxy class.
7.6.2 Inheritance and Widening
In the mapping shown in the preceding section, Thermometer inherits from
CORBA::Object, and Thermostat inherits from Thermometer. In other words, the inheritance structure of the proxy classes mirrors the inheritance of the IDL interfaces. Also note that _ptr references are C++ pointers to the corresponding proxy class. (If they are not implemented as actual pointers, they behave as if they were C++ class instance pointers.) This means that _ptr references, like C++ pointers, support implicit widening. For example:
CCS::Thermostat_ptr tmstat = ...; |
// |
Get Thermostat ref... |
||
CCS::Thermometer_ptr thermo = tmstat; // |
OK, compatible assignment |
|||
CORBA::Object_ptr |
o1 |
= tmstat; |
// |
OK too |
CORBA::Object_ptr |
o2 |
= thermo; |
// OK too |
|
These assignments are widening assignments. C++ standard conversions ensure that a pointer to a derived class is assignment-compatible with a pointer to a base class. This reflects the fact that inheritance expresses an is-a relationship. A thermostat is-a thermometer, so it makes sense to treat it as one.
Because all IDL interfaces implicitly inherit from Object, proxy classes form a singlerooted inheritance tree with CORBA::Object at the root. It follows that _ptr
215
IT-SC book: Advanced CORBA® Programming with C++
references of any type can be widened to Object_ptr, as shown by the last two assignments.
The preceding assignments create the situation shown in Figure 7.4 in the client.
Figure 7.4 Effect of widening _ptr assignments.
The first assignment to tmstat creates the proxy with a reference count of 1 (we assume that the reference was obtained from an ORB API call). Note that the assignments that follow do not affect the reference count. Ordinary assignment between _ptr references is a shallow assignment. Given the mapping for _ptr references, this makes sense because each of the preceding assignments simply assigns a C++ pointer.
The client now holds four separate _ptr references that all denote the same (possibly remote) thermostat object. The C++ type system ensures that the thermostat part of the object can be accessed only via a Thermostat_ptr but not via a
Thermometer_ptr or Object_ptr:
// OK, read nominal temperature
// Compile-time error, cannot access // derived part via base reference
t = o1->get_nominal(); // Compile time error too
Because the reference count on the proxy is still 1, a single call to CORBA::release on any one of the references deallocates the proxy and leaves all references dangling:
CORBA::release(thermo); |
// or CORBA::release(tmstat); |
//or CORBA::release(o1);
//or CORBA::release(o2);
//Cannot use tmstat, thermo, o1, or o2 from here on...
The client code can also make explicit copies during the assignments. For example:
CCS::Thermostat_ptr tmstat = ...; // Get Thermostat reference...
CCS::Thermometer_ptr thermo
= CCS::Thermometer::_duplicate(tmstat); CORBA::Object_ptr o1 = CCS::Thermometer::_duplicate(tmstat);
216
IT-SC book: Advanced CORBA® Programming with C++
CORBA::Object_ptr o2 = CORBA::Object::_duplicate(thermo);
This code creates the same picture as before but with a reference count of 4 on the proxy (see Figure 7.5). Of course, the client now must call CORBA::release once on each reference to deallocate the proxy.
Figure 7.5 Effect of widening _ptr assignments with explicit copying.
The preceding code example also uses widening assignments. For example, the assignment
CORBA::Object_ptr o1 = CCS::Thermometer::_duplicate(tmstat);
uses widening in two places. For the call to _duplicate, the actual argument tmstat is of type Thermostat_ptr, which is widened to the formal parameter type
Thermometer_ptr. The return value from _duplicate of type Thermometer_ptr is widened to CORBA::Object_ptr during the assignment. This code works because of the C++ standard conversion from pointer-to-derived to pointer-to-base.
7.6.3 Narrowing Conversions
C++ type rules make the following illegal:
CCS::Thermometer_ptr thermo = ...; // Get Thermometer ref...
CCS::Thermostat_ptr tmstat = thermo; // Compile-time error
The attempt to assign a thermometer reference to a thermostat reference is rejected by the compiler. C++, being a statically type-safe language, rejects the assignment from a pointer-to-base to a pointer-to-derived because it cannot guarantee that, at run time, the base pointer will really point at a derived object of the correct type. We know that thermo does point at a thermostat, so you may be tempted to write something such as this:
CCS::Thermostat_ptr tmstat
= (CCS::Thermostat_ptr)thermo; // Disastrous!!!
217
IT-SC book: Advanced CORBA® Programming with C++
This code may compile and may even happen to do the right thing at run time, but nevertheless it has completely undefined behavior. In the presence of multiple inheritance with virtual base classes, such a sledgehammer cast will get you into trouble eventually. The C++ mapping is crafted very carefully to make casts unnecessary; if you find yourself writing a cast, take it as a strong indication that you are doing something wrong.
7.6.4 Type-Safe Narrowing
To allow you to safely down-cast a reference at run time, the IDL compiler generates a static member function called _narrow:
CCS::Thermometer_ptr thermo = ...; // Initialize...
// Try type-safe down-cast
CCS::Thermostat_ptr tmstat = CCS::Thermostat::_narrow(thermo); if (CORBA::is_nil(tmstat)) {
//thermo is not of type Thermostat
}else {
//thermo *is a* Thermostat, tmstat is now a valid reference
CORBA::release(tmstat); |
// _narrow() calls _duplicate()! |
The code initializes thermo to point to some object. Because thermo is of type Thermometer, it can denote either a thermometer or a thermostat depending on the actual type of the object thermo is initialized to. The call to CCS::Thermostat::_narrow performs a run-time test on the reference, and it returns a non-nil reference only if the actual type of thermo matches the expected type Thermostat. If the actual type is not compatible with the expected type, _narrow returns a nil reference. This mechanism is very similar to a C++ dynamic_cast, which serves the same purpose for C++ types.
Note that _narrow calls _duplicate. Conceptually, _narrow does not return the original reference converted to the new type but instead returns a copy that is converted to the new type. This means that you must release a reference returned from _narrow; otherwise, you will suffer a resource leak.
Depending on the exact type being narrowed to, _narrow may need to contact the server. If the server is registered for automatic start-up, calling _narrow may therefore start the server as a side effect. It follows that _narrow may raise exceptions if it is unable to contact the server (see Section 7.15.2). Note that the C++ mapping cannot use a C++ dynamic_cast instead of _narrow because of the need to contact the server.
218
IT-SC book: Advanced CORBA® Programming with C++
7.6.5 Illegal Uses of _ptr References
To avoid unduly restricting ORB implementers, a number of uses of _ptr references are explicitly flagged as having undefined behavior in the C++ mapping. Do not use these constructs even if they happen to work in your implementation. In other implementations, they may give incorrect results, or, if _ptr references are implemented as classes, these constructs will not even compile.
Comparison for equality or inequality
CORBA::Object_ptr o1 = ...; |
|
CORBA::Object_ptr o2 = ...; |
// Undefined behavior! |
if (o1 == o2) |
|
...; |
// Undefined behavior! |
if (o1 != o2) |
|
...; |
|
The outcome of these comparisons is completely undefined and may or may not yield the expected result (see Section 7.11.3 for a portable way to compare references). Applying relational operators to references
CORBA::Object_ptr o1 |
= ...; |
CORBA::Object_ptr o2 |
= ...; |
if (o1 < o2) |
// Undefined behavior! |
...; |
// <, <=, >, and >= have no meaning |
Applying arithmetic operators to references
CORBA::Object_ptr o1 = ...; |
|
CORBA::Object_ptr o2; |
// Meaningless! |
o2 = o1 + 5; |
|
ptrdiff_t diff = o2 - o1; |
// Meaningless! |
Conversion of _ptr references to and from void *
CORBA::Object_ptr o = ...; |
// Undefined! |
void *v = (void *)o; |
|
o = (CORBA::Object_ptr)v; |
// Ditto! |
Down-casts other than with _narrow
CCS::Thermostat_ptr tmstat = ...; |
// Get reference |
|
CORBA::Object_ptr o = tmstat; |
// OK |
|
CCS::Thermostat_ptr tmstat2; |
|
|
tmstat2 = dynamic_cast<CORBA::Object_ptr>(o); |
// Bad! |
|
tmstat2 = static_cast<CORBA::Object_ptr>(o); |
// Bad! |
|
tmstat2 = reinterpret_cast<CORBA::Object_ptr>(o); |
// Bad! |
|
tmstat2 = (CORBA::Object_ptr)o; |
|
// Bad! |
219
