Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Advanced CORBA Programming wit C++ - M. Henning, S. Vinoski.pdf
Скачиваний:
65
Добавлен:
24.05.2014
Размер:
5 Mб
Скачать

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

CCS::TempType t;
t = tmstat->get_nominal(); t = thermo->get_nominal();

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