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.
assert Macroassert
macro as an extremely crude form of error handling. For example, on page 396:
We did this mainly to keep code examples short and to avoid distracting from the main message with lots of error-handling code. However, usingassert(ICP_get(m_anum, "model", buf, sizeof(buf)) == 0);
assert in this way means that if you compile the code with
NDEBUG defined, it will no longer work correctly because the
assertion has a side effect that no longer occurs when the assertion
is disabled.
Quite a few of the code examples in the book rely on assertions not being disabled. A number of people have pointed out to us that using assertions in this way is bad style, and we agree. For your own code, you should ensure that assertions do not have side effects.
delete[] with ostrstreamTheostrstream ostr; ostr << "whatever..." << ends; char * text_p = ostr.str(); // ... delete[] text_p;
str member function passes
responsibility for deallocation of the string to the caller. However,
the use of delete[] to deallocate that string is not portable,
nor is use of delete (although either or both work fine
in many implementations). Instead, explicit deallocation of the string
requires a call to ::operator delete(text_p).
However, another option is to use freeze(0) to return
responsibility for deallocation to the ostrstream,
so memory for the string returned by str is deallocated
when the ostrstream goes out of scope. Therefore, the above code
fragment should read:
ostrstream ostr; ostr << "whatever..." << ends; char * text_p = ostr.str(); // ... ostr.rdbuf()->freeze(0);
The ColorMode enumeration contains
an incorrect enumerator. The enumeration should be:
enum ColorMode { BlackAndWhite, FullColor };
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 first two code examples use the wrong qualified name (Names
instead of NameList). The first
code example should read:
The second code example should read:char * entry_array[MAX_ENTRIES]; // OK char * names_array[NameList::MAX_NAMES]; // May not compile
char * entry_array[MAX_ENTRIES]; // OK char ** names_array = new char *[NameList::MAX_NAMES]; // OK
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 first sentence of the last bullet point misses the word "immediately". The sentence should read:
The mapping does not guarantee that extending the length of a sequence immediately default-constructs the newly created elements.
The code to fill a string sequence buffer contains a memory management bug:
if (len == max) {
// Double size if out of room
char ** tmp = StrSeq::allocbuf(max *= 2);
memcpy(tmp, strvec, len * sizeof(*strvec));
StrSeq::freebuf(strvec);
strvec = tmp;
}
This is incorrect because freebuf deallocates each string
in the buffer. This results in eventual double deallocation of the sequence
elements.
To avoid the problem, the code must copy each string, deallocating the
empty strings that are placed into the buffer by allocbuf:
if (len == max) {
// Double size if out of room
char ** tmp = StrSeq::allocbuf(max *= 2);
for (CORBA::ULong i = 0; i < len; i++) {
CORBA::string_free(tmp[i]);
tmp[i] = CORBA::string_dup(strvec[i]);
}
StrSeq::freebuf(strvec);
strvec = tmp;
}
The memory management rules for allocbuf with string sequences
were changed with the final revision of the C++ mapping for CORBA 2.3.
This makes it highly likely that your ORB will deviate from the behavior
currently prescribed by the standard. In addition, there are still
ongoing discussions as to the correct behavior of the sequence allocation
and deallocation functions for sequences of strings and sequences of
object references.
To keep your code portable, we recommend that you do not use the buffer manipulation functions for sequences of strings and object references until this matter is resolved in the OMG.
The forward declaration for DoubleSeq contains a typo
(calss instead of class).
The code example for the remove template function
incorrectly places the statement to decrement the length of the sequence
in the body of the loop. The example should read:
template<class Seq>
void
remove(Seq & seq, CORBA::ULong idx)
{
for (CORBA::ULong i = idx; i < seq.length() - 1; i++)
seq[i] = seq[i + 1];
seq.length(seq.length() - 1);
}
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 first paragraph refers to the wrong union members. The paragraph should read:
In this example, the union is not initialized after default construction. Calling the modifier function for the memberlong_meminitializes the union by activating that member and setting its value. As a side effect, assigning to a member via the modifier function also sets the discriminator value. The preceding code tests the discriminator value in an assertion to verify that the union works correctly. It also reads the value oflong_memby calling its accessor member function. Because we just set the value to99, the accessor must of course return that value. The code tests this with another assertion.
The class definition for ShippingInfo disagrees with its
IDL definition. The correct class definition is:
class ShippingInfo {
public:
// Other member functions as before...
const Details & packaging_info() const; // Accessor
void packaging_info(const Details &); // Modifier
Details & packaging_info(); // Referent
const TextSeq & other_info() const; // Accessor
void other_info(const TextSeq &); // Modifier
TextSeq & other_info(); // Referent
};
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 third paragraph of section 6.19.2 refers to the wrong identifier names
(StrSeq and StrSeq_var instead of NameSeq
and NameSeq_var.
The paragraph should read:
This generates two C++ types:NameSeq, which is the actual sequence, andNameSeq_var, which is the corresponding memory management wrapper. Here is a code fragment that illustrates the use ofNameSeq_varinstances:
The first sentence of the section Additional T_var Member Functions for Fixed-Length Types should read:
For aT_varfor 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 definitions for _var_type and _ptr_type
appear at the wrong scope. They are incorrectly shown as part
of the CORBA and CCS namespaces, but instead
should appear as nested definitions in each proxy class.
In addition, the signature for the _narrow member function
is incorrect in both the Thermometer and Thermostat
classes: the parameter type of these should be CORBA::Object_ptr.
The correct class definitions for pages 234 and 235 are:
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);
// Member functions for operations of Thermostat here...
typedef Thermostat_var _var_type;
typedef Thermostat_ptr _ptr_type;
};
// ...
}
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_ptrreferencetpnow 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 second code example passes the wrong variable to get_darr4
(d3 instead of in_val).
The code example should read:
Foo_var fv = ...; // Get reference
Darr3 in_val = { 1, 2, 3 };
Darr3_var ret_val;
ret_val = fv->get_darr4(in_val); // Double disaster!!!
The second bullet point mentions the wrong structure member (s
instead of s_mem). The third sentence of the second bullet
point should read:
If the callee wants to modify the string member s_mem of
the structure, it can do so simply by assignment.
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 String_out class contains a
typo. It uses String_free instead of string_free.
The class definition should read:
class String_out {
public:
String_out(char * & s): _sref(s) { _sref = 0; }
String_out(String_var & sv): _sref(sv._sref) {
CORBA::string_free(_sref);
_sref = 0;
}
// Other member functions for assignment,
// dereferencing, and conversion to char *
// and const char * here...
private:
char * & _sref;
};
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 code example that sets the loc union member works only
in standard C++ environments because it relies on the type of a string
literal being const char *. The code on page 337 to set
the loc union member should read:
ss[0].key.loc(CORBA::string_dup("Earth"));
ss[1].key.loc(CORBA::string_dup("HAL"));
This also affects the complete client listing on page 345.
Note that in a standard C++ environment, the call to string_dup
is not necessary because in standard C++, the type of a string literal
is const char *, so the loc union member
will make a deep copy if a string literal is passed.
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 text states:
Because exception specifications are not considered to be part of the signature of a function, the C++ compiler will not check that your servant method has properly reproduced the exception specification from the skeleton method it is overriding.This is incorrect. ISO/IEC C++ requires that the exception specification of a function that is overridden in a derived class must be at least as restrictive as the exception specification of that function in the base class.
Unfortunately, most current compilers do not diagnose this error and permit the overridden function in the derived class to have any exception specification at all. In order to keep your code portable, we recommend that you always reproduce the exception specification for the function in the derived class exactly as it appears in the base class; that way, you avoid nasty surprises as compilers gradually catch up with the ISO/IEC C++ standard.
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 code example at the top of page 393 uses the wrong identifier in
the printf statement (nominal_temp instead
of temp). The statement should read:
printf("nominal_temp: %d\n", temp);
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 definition for the PortableServer module uses the
wrong keyword (namespace instead of module). The
first line of the definition should read:
module PortableServer {
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 POAs,
when it should be talking about POAManagers 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 code example leaves the ICS in an
inconsistent state if the initial temperature setting for a thermostat
is out of range: the device is added to the network with a call to
ICP_online at the beginning of the function
but not removed again if an exception is thrown. The code should
contain the following statement just before the throw
statement near the end of the code example:
ICP_offline(anum);
The remaining_activations
parameter for etherialize is incorrectly commented out. The
signature of the function should read:
void
ThermometerActivator_impl::
etherealize(
const PortableServer::ObjectId & /* oid */,
PortableServer::POA_ptr /* poa */,
PortableServer::Servant servant,
CORBA::Boolean /* cleanup_in_progress */,
CORBA::Boolean remaining_activations
) throw(CORBA::SystemException)
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 text mentions the wrong exception for
failure to establish a connection by a client. If a client cannot
open a connection to a server, it receives a TRANSIENT
exception and not COMM_FAILURE. The third sentence of the
third paragraph on page 622 should read:
In that case, clients receive a TRANSIENT exception when the
TCP/IP connection timer expires.
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.
Steps 3-5 mention the wrong machine name (coco instead of
bobo). (The machine names are correct in Figure 14.1.) Steps
3-5 should read:
3. The server sends messages that inform the repository of its machine name (bobo), the names of the POAs it has created and their port numbers (controllerat 1799), and the fact that it is ready to accept requests.4. The implementation repository constructs a new object reference that contains host
bobo, port number 1799, and the original object key.5. The client opens a connection to
boboat port 1799 and sends the request a second time.
aten_p and atwenty_p have the wrong type. The
first code example should read:
arr10_slice * aten_p = arr10_alloc(); a <<= arr10_forany(aten_p, 1);
The second code example should read:
CORBA::Any a; arr10_slice * aten_p = arr10_alloc(); // Heap-allocate array arr20_slice * atwenty_p = arr20_alloc(); // Heap-allocate array a <<= arr20_forany(aten_p, 1); // Trouble! a <<= arr10_forany(atwenty_p, 1); // Trouble!
The member_name accessor is missing a parameter declaration.
It should read:
const char * member_name(ULong index) const;
The branch of the switch statement for tk_string
and tk_wstring misses curly brackets to create a scope. As
a result, the compiler complains about a jump past an initializer because
the branch contains the initialization for the variable l.
The code for this branch of the switch statement should read:
case CORBA::tk_string:
case CORBA::tk_wstring:
{
CORBA::ULong l = tcp->length();
if (l != 0)
cout << "<" << l << ">";
cout << endl;
break;
}
The code example contains a bug near the
bottom of the page, in the for loop that shows the case labels
and member names. The statement
cout << tcp->member_name() << ":" << endl;
should instead read:
cout << tcp->member_name(i) << ":" << endl;
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 list of type code constants misses an entry for _tc_long:
const CORBA::TypeCode_ptr _tc_long = ...;
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);
DynAny specification was changed substantially after
the first printing was finalized, making it necessary to rewrite the chapter.
You can download a
replacement chapter
in PDF format.
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 last paragraph for the description
of list uses the wrong parameter name (bl
instead of it). The paragraph should read:
If the call tolistreturns all the bindings in the context, theititerator reference is nil. Otherwise,itpoints at an iterator of typeBindingIteratorthat you can use to retrieve the remaining bindings.
The final paragraph says that
TheThis should readAddressproperty has the type codeManufacturing::_tc_AddressType.
TheManufacturerproperty 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 code examples call the export method on the proxy for
the Register interface to export service offers. ISO/IEC C++
has added export as a keyword to C++. This means that, for
ISO/IEC C++ compilers, the code will not compile as shown. Instead of
export, the code should call _cxx_export. (Note
that this will work only if your IDL compiler correctly maps away from
the ISO/IEC C++ keyword.)
The IDL on page 874 shows the type definition for
Preference in the CosTrading scope.
This is incorrect. The definition instead is part of the Lookup
interface, so the correct fully-qualified name is CosTrading::Lookup::Preference.
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 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 read_offer_id helper function returns an empty string
if no previous offer ID was remembered. The code catches the
UnknownOfferId exception to recognize when no previous
offer exists. However, depending on the implementation, a trader may also
raise an IllegalOfferId exception for the attempt to withdraw
a service offer using an empty string as the offer ID.
To be portable across different trader implementations, the code
should catch both UnknownOfferId and IllegalOfferId
as follows:
// Attempt to withdraw the previous offer.
try {
regis->withdraw(offer_id);
} catch (const UnknownOfferId &) {
// Fine, there is no previous offer.
} catch (const IllegalOfferId &) {
// Fine, there is no previous offer.
}
The first code example uses the wrong member name for the assignment
to the name component for the event channel (name instead of
id). The beginning of the example should read:
CosNaming::Name ec_name;
ec_name.length(1);
ec_name[0].id = CORBA::string_dup("event_channel");
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;