Last updated: 28 January 2004
A number of people have submitted comments, constructive criticism, and feedback since the first printing. Our thanks go to everyone who helped us weed out bugs and make this a better book. All the people who contributed are listed in the acknowledgments to our readers.
These pages contain the following statement:
This is incorrect and will not compile. The correct statement is:ostr->rdbuf().freeze(0);
ostr.rdbuf()->freeze(0);
The code to check the number of arguments should follow the call to
ORB_init
instead of preceding it. This is necessary if
additional ORB-specific options are passed on the commmand line.
The members of type Adata
and Bdata
of the
ABunion
are swapped. The union definition should read:
union ABunion switch (StructType) { case A_TYPE: struct Acontents { Adata data; sequence<ABunion, 1> nested; // Contained Bdata } A_member; case B_TYPE: struct Bcontents { Bdata data; sequence<ABunion, 1> nested; // Contained Adata } B_member; };
The second repository identifier in the list near the top of the pages
is shown as IDL:CCS/Tempttype:1.0
. It should be
IDL:CCS/TempType:1.0
.
The prefix increment and decrement operators of class Fixed
have the wrong signature. The correct signature is:
Fixed & operator++(); Fixed & operator--();
The return type of comparison operators was changed in the C++ mapping
after the publication date of this printing (from int
to
Boolean
). The new signatures are:
Boolean operator<(const Fixed &, const Fixed &); Boolean operator>(const Fixed &, const Fixed &); Boolean operator<=(const Fixed &, const Fixed &); Boolean operator>=(const Fixed &, const Fixed &); Boolean operator==(const Fixed &, const Fixed &); Boolean operator!=(const Fixed &, const Fixed &);
The fifth line of the code example should read:
r = f1.round(1); // 0.4
The call to StrArray_copy
on the third-last line of the
code example passes the address of the array instead of the array itself.
The call should read:
StrArray_copy(x, sp1); // Copy contents of sp1 into x
The return type of operator[]
is incorrect. The operators
return a reference to the element type of the sequence (not the sequence
type itself). Assuming an element type TE
, the correct
declarations are:
TE & operator[](CORBA::ULong); // For sequences const TE & operator[](CORBA::ULong) const; // For sequences
The first sentence of the section Additional T_var
Member Functions for Fixed-Length Types should read:
For aT_var
for a fixed-length structure or union of typeT
, the IDL compiler generates the following:
The return type T &
of the T_var
assignment
operator is incorrect. The correct signature is:
T_var & operator=(const T &)
The code example to illustrate incorrect use of down-casts uses incorrect types. The example should read:
CCS::Thermostat_ptr tmstat = ...; // Get reference CORBA::Object_ptr o = tmstat; // OK CCS::Thermostat_ptr tmstat2; tmstat2 = dynamic_cast<CCS::Thermostat_ptr>(o); // Bad! tmstat2 = static_cast<CCS::Thermostat_ptr>(o); // Bad! tmstat2 = reinterpret_cast<CCS::Thermostat_ptr>(o); // Bad! tmstat2 = (CCS::Thermostat_ptr)o; // Bad! tmstat2 = CCS::Thermostat::_narrow(o); // OK
The second code example has the variable p
in the wrong scope.
The example should read:
CCS::Thermometer_ptr p = ...; // Get reference { CCS::Thermometer_var v; v = p; // v takes ownership // Use v... } // No leak here - v's destructor calls // release and p now dangles.
The last sentence of Section 7.12.4 should read:
On the other hand, the_ptr
referencetp
now dangles, because ownership passed toseq[1]
during the assignment.
The operation name in the IDL example at the bottom of the page should
be long_op
instead of op_long
.
The final statement of the second code example uses an incorrect variable
(ret_val
instead of out_val
). The final statement
should read:
cout << "out_val: " << out_val << endl;
The type definition for Varr_slice
should read:
typedef Vls Varr_slice;
The IDL at the top of the page should read:
interface Foo { Foo ref_op( in Foo ref_in, inout Foo ref_inout, out Foo ref_out ); };
The example for using _var
types to hide differences
in parameter rules is wrong. (It uses inout
parameters
instead of out
parameters.) We have provided
PDF with change bars for the affected pages.
The C++ mapping for _var
types is under-specified in CORBA 2.3
because it does not show how _var
types for fixed-length
underlying types should work. On 10 January 2000, the OMG C++
Mapping RTF voted to clarify the situation by specifying the
mapping for _var
types for
fixed-length underlying types. (Note that the implementation shown
is not binding in the sense that its internals may differ in each ORB;
however, the semantics must be the same.) The upshot of this mapping is that,
once initialized (which is always via a deep copy), a _var
variable for a fixed-length underlying type behaves like a value of the
underlying type.
The BadTemp
exception is used with the wrong scoped name.
The correct scope is CCS::Thermostat::BadTemp
.
The example code tries to down-cast a system exception to a BadTemp
exception, which cannot possibly work. The correct code is:
try { tmstat_ref->set_nominal(500); } catch (const CORBA::Exception & e) { // Check what sort of exception it is... const CORBA::SystemException * se; const CCS::Thermostat::BadTemp * bt; if (se = CORBA::OBJECT_NOT_EXIST::_downcast(&e)) { // It is an OBJECT_NOT_EXIST exception } else if (bt = CCS::Thermostat::BadTemp::_downcast(&e)) { // It is a BadTemp exception } else { // It is some other exception } // Do not deallocate se here -- the exception // still owns the memory pointed to by se or bt. }
The example code tries to down-cast a system exception to a BadTemp
exception, which cannot possibly work. The correct code is:
try { tmstat_ref->set_nominal(500); } catch (const CORBA::Exception & e) { // Check what sort of exception it is... const CORBA::SystemException * se; const CCS::Thermostat::BadTemp * bt; if (se = dynamic_cast<const CORBA::OBJECT_NOT_EXIST *>(&e)) { // It is an OBJECT_NOT_EXIST exception } else if (bt = dynamic_cast<const CCS::Thermostat::BadTemp *>(&e)) { // It is a BadTemp exception } else { // It is some other exception } }
The first definition in the code example should read:
const int array_length = sizeof(Darr)/sizeof(*Darr);
The first definition in the second code example should read:
const int array_length = sizeof(Varr)/sizeof(*Varr);
The declaration of the set_nominal
function uses the wrong
scope for the BadTemp
exception. The correct declaration is:
virtual CCS::TempType set_nominal(CCS::TempType new_tmpe) throw(CORBA::SystemException, CCS::Thermostat::BadTemp) = 0;
The BadTemp
exception is used with the wrong scoped name in
three places. The correct scope is CCS::Thermostat::BadTemp
.
The code example deletes the previously allocated vls_out
pointer and then throws an exception without changing the pointer value.
This is a grey area in the C++ mapping: it is not clear whether the
skeleton will attempt to delete the value of an out
parameter
when an exception is thrown. (Currently, we are aware of skeleton
implementions that call delete
on the parameter when an
exception is thrown and others that do not.)
To make the code portable for either kind of skeleton implementation, it is
necessary to set the vls_out
argument to zero before throwing
the exception:
catch (const CORBA::Exception &) { delete vls_out.ptr(); vls_out = 0; // Required for portability delete result; throw; }
The same situation arises for inout
parameters, which also
need to be set to zero before throwing an exception.
Note that there is no need to set the return value to zero for either kind of skeleton implementation.
Instead of writing code such as the example on page 380/381, we recommend
that you use a _var
type to hold an allocated
out
or inout
parameter and call its
_retn
member function once no more exceptions can be thrown,
as shown on page 381/382. This is both simpler and correct for either kind
of skeleton implementation.
The type name on the second last line is incorrect. It should be
MyLegacyClass
instead of MyLegacyType
.
The definition of the m_anum
protected member misses a
const
qualifier. It should read:
const CCS:AssetType m_anum; // My asset number
The StrFinder
function contains a memory leak. The two calls
to strcmp
should read:
andreturn strcmp(CORBA::String_var(p.second->location()), m_str) == 0;
return strcmp(CORBA::String_var(p.second->model()), m_str) == 0;
The same error appears on page 417.
The last statement of the code example passes an incorrect parameter to
the find
algorithm:
where = find_if( ++where, m_assets.end(), StrFinder(sc, slist[i].key.loc()) );
The statement should read:
where = find_if( ++where, m_assets.end(), StrFinder(sc, search_str) );
The IDL definitions for the AdapterAlreadyExists
and
InvalidPolicy
exceptions should be in the scope of
the POA
interface.
The IDL definitions for the ServantAlreadyActive
,
ObjectAlreadyActive
, and WrongPolicy
exceptions
should be in the scope of the POA
interface.
The call to activate_object
incorrectly passes the servant instead of the address of the servant. The call should read:
poa->activate_object(&ctrl_servant);
The last bullet point on the page finishes with the parenthetical remark
(the servant manager must be a ServantLocator
)
The parenthetical remark is incorrect and should be deleted.
The transition labelled F
should be deleted from the diagram
and the transition table.
The description in the second paragraph talks about POA
s,
when it should be talking about POAManager
s instead.
Destruction of a POAManager
is automatic and occurs when the
last POA
using the POAManager
is destroyed.
The code example makes a number of calls to find_POA
and then
activates the POA manager. However, the POA manager is created in the holding
state, so the calls to find_POA
will not be dispatched and
the code deadlocks.
To fix the problem, the statements
must be moved to precede the calls to// Activate our POAManager. PortableServer::POAManager_var mgr = root_poa->the_POAManager(); mgr->activate();
find_POA
.
The call to push_front(oid)
at the end of the
incarnate
function should precede the instantiation of
the new servant.
The third paragraph following the IDL example states:
Note that the size of the structure CD in Figure 13.2 is 13 bytes...This should read:
Note that the size of the structure CD in Figure 13.2 is 14 bytes...
The last line states that the components
field
is used only by IIOP 1.1. This is incorrect; the components
field is used by both IIOP 1.1 and IIOP 1.2.
The first sentence states that each profile contains information for one protocol. This is only partially correct because a single profile, if it is a multicomponent profile, can contain information for more than one protocol.
Using a multicomponent profile to hold information for more than one protocol is useful if the object key is the same for both protocols. In that case, using a single multicomponent profile avoids having to write two copies of the same object key into an IOR.
The member_name
accessor is missing a parameter declaration.
It should read:
const char * member_name(ULong index) const;
The code dealing with wide characters incorrectly extracts the wide character
from an Any
value (because it omits to use the
to_wchar
helper). The code should read:
case CORBA::tk_wchar: CORBA::WChar wc; *ap >>= CORBA::Any::to_wchar(wc); cout << "'" << wc << "'"; break;
The final three statements of the code example incorrectly use
>>
instead of >>=
for extraction. They
should read:
foo_any >>= foo_p; // Succeeds aof_any >>= foo_p; // Succeeds in 2.3, undefined in 2.2 bar_any >>= foo_p; // Fails in 2.3, undefined in 2.2
The signatures for create_exception_tc
,
create_interface_tc
, and create_string_tc
have an extraneous underscore. The correct signatures are:
TypeCode create_exception_tc( in RepositoryId id, in Identifier name, in StructMemberSeq members ); TypeCode create_interface_tc( in RepositoryId id, in Identifier name ); TypeCode create_string_tc( in unsigned long bound );
The type code for the Node
structure is correct,
but TypeCode::equal
does not return true when
comparing this type code to the _tc_Node
type code
that is generated by the IDL compiler. (Only TypeCode::equivalent
returns true for the comparison.)
To get TypeCode::equal
to return true when comparing the
struct_tc
in the example with the generated
_tc_Node
type code, the second-last statement of the example
should read:
members[1].type = orb->create_sequence_tc(0, rec_tc);
The IDL for DynArray
contains incorrect signatures
for get_elements_as_dyn_any
and
set_elements_as_dyn_any
. The correct signatures are:
DynAnySeq get_elements_as_dyn_any(); void set_elements_as_dyn_any(in DynAnySeq value) raises(TypeMismatch, InvalidValue);
The first code fragment uses incorrect module names in two places.
(CORBA
and DynamicAny
are swapped.) The correct
code is:
// Make an Any containing the value 20 as a long. // CORBA::Any an_any; an_any <<= (CORBA::Long)20; // Create a DynAny from the Any. // DynamicAny::DynAny_var da = daf->create_dyn_any(an_any); // Use da... // Destroy the DynAny again. // da->destroy();
The example code requires an additional call at the end:
error_msg->destroy();
The first two sentences of the second paragraph should read:
resolve_initial_references
cannot return a nil reference,
unless someone has misconfigured the ORB.
The final paragraph says that
TheThis should readAddress
property has the type codeManufacturing::_tc_AddressType
.
TheManufacturer
property has the type codeManufacturing::_tc_AddressType
.
The code to create a service type for the controller uses property modes
that disagree with the ones shown in Table 19.1 on page 838 for the
Supports
and MaxDevices
properties.
The code for these two properties should read:
props[3].name = CORBA::string_dup("Supports"); props[3].value_type = CORBA::TypeCode::_duplicate( Airconditioning::_tc_ModelType ); props[3].mode = ServiceTypeRepository::PROP_READONLY; props[4].name = CORBA::string_dup("MaxDevices"); props[4].value_type = CORBA::TypeCode::_duplicate( CORBA::_tc_ulong ); props[4].mode = ServiceTypeRepository::PROP_NORMAL;
The expression near the bottom of the page is missing a *
operator and should read:
min(12.3 * mem_size + 4.6 * file_size)
The final line of the first bullet point mentions the
limiting_follow_policy
. This should read
limiting_follow_rule
instead.
The code example at the bottom of the page lost its last line. The following statement should appear after the last line on page 947:
any <<= event_data;
The code example near the bottom of the page omits to connect the consumer
to the event channel, which results in a Disconnected
exception
when the consumer calls pull
. The correct code is:
// Assume the "channel" variable refers to our event channel. CosEventChannelAdmin::ConsumerAdmin_var consumer_admin = channel->for_consumers(); // Obtain a ProxyPullSupplier from the ConsumerAdmin and connect. CosEventChannelAdmin::ProxyPullSupplier_var supplier = consumer_admin->obtain_pull_supplier(); supplier->connect_pull_consumer( CosEventComm::PullConsumer::_nil() ); bool done; // Now enter our GUI event loop. do { check_for_thermostat_event(supplier); done = check_for_gui_event(); } while (!done);
The second paragraph states that even for requests made locally, they must be dispatched on the POA's single thread instead of the caller's thread.
Different ORB vendors have interpreted the wording in the specification
differently, so the semantics of the SINGLE_THREAD_MODEL
policy with respect to collocated calls are currently under discussion.
Until this matter is settled in the OMG Core Revision Task Force, you
must assume that it is implementation-dependent whether the call is
dispatched on the POA's single thread or the caller's thread.
The implementation of preinvoke
should include a call to
servant->_add_ref()
before returning, and
postinvoke
should include a corresponding
call to servant->_remove_ref()
. These calls are necessary
to ensure that the servant remains in existence for the duration of
the operation.
The implementation of ICP_set
does not always correctly
NUL-terminate the location string. The correct implementation is:
extern "C" int ICP_set(unsigned long id, const char * attr, const void * value) { // Look for id in state map StateMap::iterator pos = dstate.find(id); if (pos == dstate.end()) return -1; // No such device // Change either location or nominal temp, depending on attr. if (strcmp(attr, "location") == 0) { pos->second.location = (const char *)value; if (pos->second.location.size() >= MAXSTR) pos->second.location[MAXSTR - 1] = '\0'; } else if (strcmp(attr, "nominal_temp") == 0) { if (pos->second.type != thermostat) return -1; // Must be thermostat short temp; memcpy(&temp, value, sizeof(temp)); if (temp < MIN_TEMP || temp > MAX_TEMP) return -1; pos->second.nominal_temp = temp; } else { return -1; // No such attribute } return 0; // OK }
The implementation of ~ICP_Persist
does not always correctly
NUL-terminate the location string. A call to c_str
is missing
in the loop that writes the device attributes:
db << i->second.location.c_str() << endl;