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

IT-SC book: Advanced CORBA® Programming with C++

Type Code Parameters for Exceptions

Type codes for exceptions have the same parameters as type codes for structures. The exception name and repository ID are returned by the name and id operations.

The member_count operation returns the number of members of the exception, and the member_name and member_type operations return the name and type code of each exception member.

16.3.3 Type Codes As Values

In Section 7.7 we mention that pseudo-objects cannot be sent as parameters to IDL operations because pseudo-objects are typically implemented as library code and cannot be accessed remotely. The TypeCode pseudo-object is the only exception to this rule. It is legal to send a type code as a parameter to an IDL operation. For example:

#include <orb.idl>

interface TypeStore { exception DuplicateName {}; exception NoSuchType {};

void

add(in string name, in CORBA::TypeCode tc)

 

raises(DuplicateName);

CORBA::TypeCode get(in string name) raises(NoSuchType); void remove(in string name) raises(NoSuchType);

};

The TypeStore interface maintains a table of pairs, with each pair consisting of a name and a type code. The operations allow the client to add, remove, and retrieve type codes. The TypeStore interface is fictitious; we use it here simply to illustrate that type codes are values that can be marshaled over the wire. Note that we include orb.idl in this specification. This is necessary because orb. idl contains definitions in the CORBA module, including the definition for the Type-Code interface.

CORBA intrinsically relies on the ability to marshal type codes—for example, for the transmission of any values (which contain type codes). The Interface Repository, which contains type descriptions that can be read at run time, also relies on the ability to marshal type codes as values. A number of other CORBA services, such as the Trading Service (see Chapter 19), also use type codes. For now, keep in mind that type codes are the only pseudo-object type that can be sent over the wire.

16.4 C++ Mapping for the TypeCode Pseudo-Object

Here is the C++ mapping for the TypeCode interface:

namespace CORBA { // ...

enum TCKind { tk_null, tk_void, tk_short /* , ... */ };

609

IT-SC book: Advanced CORBA® Programming with C++

class TypeCode { public:

class Bounds : public UserException { /* ...*/ }; class BadKind : public UserException { /* .. */ };

TCKind

kind() const;

Boolean

equal(TypeCode_ptr tc) const;

Boolean

equivalent(TypeCode_ptr tc) const;

TypeCode_ptr

get_compact_typecode() const;

const char *

name() const;

const char *

id() const;

ULong

member_count() const;

const char *

member_name() const;

TypeCode_ptr

member_type(ULong index) const;

Any *

member_label(ULong index) const;

TypeCode_ptr

discriminator_type() const;

Long

default_index() const;

ULong

length() const;

TypeCode_ptr

content_type() const;

UShort

fixed_digits() const;

UShort

fixed_scale() const;

};

 

// ...

 

}

Note that TypeCode is a pseudo-object. Pseudo-objects can have a C++ mapping that deviates from the normal rules. In the case of the TypeCode class, strings are returned as const char * instead of as char *. This means that you must not deallocate the result of the name, id, and member_name functions; the returned pointer points at memory internal to the TypeCode instance.

The special-purpose mappings that are permissible for pseudo-objects were initially introduced to make the use of pseudo-objects easier. The idea was that if an object is known to be implemented in a library, the normal memory management rules can be relaxed to gain some efficiency and to relieve the programmer of the burden of having to remember to deallocate variable-length values.

Unfortunately, exceptions to the normal mapping rules end up making life harder instead of easier because for all operations on pseudo-objects, you must remember whether exceptions apply to each operation. After it was realized how much confusion such exceptions created, the OMG imposed a blanket ban on pseudo-objects and introduced locality-constrained objects in their place (see page 435). Locality-constrained objects are like pseudo-objects in that they are implemented in libraries, but locality-constrained objects must follow the standard mapping rules. Unfortunately, for backward

610

IT-SC book: Advanced CORBA® Programming with C++

compatibility, we are stuck with a few pseudo-objects, such as TypeCode, that have exceptions to the normal memory management rules.

Given the TypeCode mapping, we can use the type code contained in an Any value to recursively analyze the type of the value inside the Any. The show_TC function that follows illustrates how to do this. We can call show_TC as follows:

CCS::Thermostat::BtData btd;

CORBA::Any a;

// Insert BtData value into Any a

a < <= btd;

CORBA::TypeCode_var tc; tc = a.type(); show_TC(tc);

// Get type code from Any a

//Print type code contents

This code produces the following output:

struct BtData (IDL:acme.com/CCS/Thermostat/BtData:1.0): requested:

typedef TempType (IDL:acme.com/CCS/TempType:1.0): short

min_permitted:

typedef TempType (IDL:acme.com/CCS/TempType:1.0): short

max_permitted:

typedef TempType (IDL:acme.com/CCS/TempType:1.0): short

error_msg: string

This output matches the IDL definition of BtData from Section 5.3.2:

#pragma prefix "acme.com"

module CCS {

 

// ...

TempType;

typedef short

// ...

 

interface Thermostat : Thermometer { struct BtData {

TempType requested; TempType min_permitted; TempType max_permitted; string error_msg;

}; // ...

}; // ...

};

611

IT-SC book: Advanced CORBA® Programming with C++

The show_TC function is easy to write. To make the output more readable, show_TC indents the output according to the current level of nesting. The indent helper function prints the appropriate number of spaces at the beginning of a line:

//

// Indent to the current level.

//

const int INDENT = 4;

void

indent(int indent_lvl)

{

for (int i = 0; i <INDENT * indent_lvl; i++)

cout.put(' ');

}

show_TC is a simple function: for each possible TCKind value, it prints the parameters shown in Table 16.1. A little complexity arises because we must take care not to get trapped in an infinite loop for recursive structures and unions and must take care to show union label values correctly.

To prevent getting trapped in an infinite recursion, show_TC is overloaded as an outer and an inner version. The outer version is a wrapper function that initializes a list of type codes and then calls the inner version to do the actual work:

//

// Show the contents of a type code.

//

void show_TC(CORBA::TypeCode_ptr tcp)

{

list<CORBA::TypeCode_var> tlist; show_TC(tcp, tlist, 0);

}

The tlist variable is an STL list of type codes seen so far. The outer version of show_TC initializes tlist to an empty list and then calls the inner version of show_TC. The inner version of show_TC accepts the type code to be printed, the list of type codes seen so far, and the current indent level (set to zero on the first call and passed to indent).

The inner version of show_TC takes different actions for different type codes. Here is the first part of the source code:

//

// Show the contents of a type code. 'tcp' is the type code to

612

IT-SC book: Advanced CORBA® Programming with C++

//show, 'tlist' is the list of type codes seen so far,

//'indent_lvl' is the current nesting level. 'tlist' is used

//to prevent getting trapped in an infinite loop for recursive

//structures and unions.

//

void

 

show_TC(

tcp,

CORBA::TypeCode_ptr

list<CORBA::TypeCode_var> & tlist,

int

indent_lvl)

{

 

static const char * const kind_name[] = { "tk_null", "void", "short", "long", "unsigned short", "unsigned long", "float", "double", "boolean", "char", "octet", "any", "CORBA::TypeCode", "CORBA::Principal", "interface", "struct", "union", "enum", "string", "sequence", "array", "typedef",

"exception", "long long", "unsigned long long", "long double", "wchar", "wstring", "fixed"

};

 

indent(indent_lvl);

// Print the TCKind value.

cout < < kind_name[tcp->kind()];

//

//Print name and repository ID for those type codes

//that have these parameters.

//

switch (tcp->kind()) { case CORBA::tk_objref: case CORBA::tk_struct: case CORBA::tk_union: case CORBA::tk_except: case CORBA::tk_enum: case CORBA::tk_alias:

cout < < " " < < tcp->name()

< < " (" < < tcp->id() < < "):" < < endl;

default;

// Do nothing

;

}

 

show_TC contains a static array that maps the TCKind enumerators to strings for printing. After calling indent to set the current indent level, show_TC prints the name of the current type, such as "struct" or "string." Type codes for object references, structures, unions, exceptions, enumerations, and type definitions contain both a name and a repository ID; the function next prints the name and repository for these type codes. Note that we do not use _var types here to deallocate the name and repository ID because the type code retains ownership of the returned strings.

The next few lines of show_TC print the parameters for non-recursive type codes:

//

613

IT-SC book: Advanced CORBA® Programming with C++

//For type codes that have other parameters,

//show the remaining parameters.

//

 

switch (tcp->kind()) {

// No other params to print

default:

cout < < endl;

 

break;

 

//

// For fixed types, show digits and scale.

//

case CORBA::tk_fixed:

cout < < "<" < < tcp->fixed_digits() < < "," < < tcp->fixed_scale() < < ">" < < endl;

break;

//

//For enumerations, show the enumerators.

case CORBA::tk_enum: indent(indent_lvl + 1);

for (CORBA::ULong i = 0; i < tcp->member_count(); i++) { cout < < tcp->member_name(i);

if (i < tcp->member_count() - 1) cout < < ", ";

}

cout < endl; break;

//For strings, show the bound (if any).

//

case CORBA::tk_string: case CORBA::tk_wstring:

{CORBA::ULong l = tcp->length();

if (l != 0)

cout < < "<" < < l < < ">"; cout < < endl;

} break;

//

// For sequences, show the bound (if any) and

//the element type.

case CORBA::tk_sequence:

{

CORBA::ULong l = tcp->length(); if (l != 0)

cout < < "<" < < l < < ">"; cout < < ":" < < endl;

CORBA::TypeCode_var etype = tcp->content_type(); show_TC(etype, tlist, indent_lvl + 1);

}

break;

//For arrays, show the dimension and element type.

//

case CORBA::tk_array:

{

CORBA::ULong l = tcp->length();

cout < < "[" < < l < < "]:" < < endl;

614

IT-SC book: Advanced CORBA® Programming with C++

CORBA::TypeCode_var etype = tcp->content_type(); show_TC(etype, tlist, indent_lvl + 1);

}

break;

//

// For typedefs, show the type of the aliased type.

//

case CORBA::tk_alias:

{

CORBA::TypeCode_var atype = tcp->content_type(); show_TC(atype, tlist, indent_lvl + 1);

}

break;

The default case at the beginning of the switch statement catches type codes that do not have parameters and terminates output with a newline character. The other branches of the switch statement call the member functions appropriate for the TCKind value of the type code according to Table 16.1.

For structures and unions, show_TC must take special action because structures and unions can be recursive. If show_TC were to simply call itself to print structure and union members, it could get trapped in a recursive loop. To avoid this, show_TC uses the list of type codes processed so far in the tlist parameter. Before descending into a structure or union member, show_TC checks whether the member's type code is already in the list. If it is not, show_TC adds the current type code to the list and decomposes it by recursing. If the type code is already in the list, show_TC shows the name and repository ID of the member's type code but does not recurse.

Here is the branch of the switch statement for structures and exceptions:

//

//For structures and exceptions, show the

//names and types of each member.

//

case CORBA::tk_struct: case CORBA::tk_except:

{

//

//Avoid a recursive loop by checking whether we

//have shown this type code before.

//

list<CORBA::TypeCode_var>::iterator where; where = find_if(

tlist.begin(), tlist.end(), EqualTypeCodes(tcp)

);

//

//If we have not seen this type code before, add it

//to the list of type codes processed so far and

//decode the member type codes.

//

if (where == tlist.end()) {

615

IT-SC book: Advanced CORBA® Programming with C++

tlist.push_back(CORBA::TypeCode::_duplicate(tcp)); for (CORBA::ULong i = 0;

i < tcp->member_count(); i++) {

cout < < tcp->member_name(i) < < ":" < < endl; indent(indent_lvl + 1);

CORBA::TypeCode_var mt = tcp->member_type(i); show_TC(mt, tlist, indent_lvl + 2);

}

} else {

cout < < " " < < tcp->name()

< < " (" < < tcp->id() < < ")" < < endl;

}

}

break;

We use the STL find_if algorithm to check whether a type code is already in the list. Because tlist is a simple list, the cost of doing this is O(n). This cost is acceptable because structures and unions rarely nest more than one or two levels, so tlist will typically contain only a few entries. The EqualTypeCodes argument to find_if is a simple function object for type code comparison (see Section 16.5).

Note that exceptions and structures are dealt with in the same branch of the switch statement. This works because exceptions are encoded the same as structures. Exceptions cannot be recursive, so the preceding code simply does not recurse for exceptions. In addition, exceptions (as opposed to structures) can be empty, in which case member_count returns zero and the code prints nothing.

The remainder of show_TC deals with unions. Unions are treated in a similar manner as structures, with tlist preventing infinite recursion. However, for unions, we also need to show the discriminator type and the case labels for each union branch:

//

//For unions, show the discriminator type.

//Then, for each member, show the case label,

//member name, and member type. To show the case

//label, we use the show_label() helper function.

case CORBA::tk_union:

{

//Avoid getting trapped in a recursive loop.

list;ltCORBA::TypeCode_var>::iterator where; where = find_if(

tlist.begin(),

tlist.end(),

EqualTypeCodes(tcp)

);

//Show the members only if we haven't shown this type

//code before.

//

616

IT-SC book: Advanced CORBA® Programming with C++

if (where == tlist.end()) { tlist.push_back(CORBA::TypeCode::_duplicate(tcp)); indent(indent_lvl + 1);

//

//Show discriminator type.

cout < < "Discriminator type:" < < endl; CORBA::TypeCode_var dt;

dt = tcp->discriminator_type(); show_TC(dt, tlist, indent_lvl + 2);

//Show case label, member name, and

//member type for each member.

//

for (CORBA::ULong i = 0;

i ;lt tcp->member_count(); i++) { CORBA::Any_var label = tcp->member_label(i); indent(indent_lvl + 1);

show_label(label); indent(indent_lvl + 2);

cout < < tcp->member_name(i) < < ":" < < endl; CORBA::TypeCode_var mt = tcp->member_type(i); show_TC(mt, tlist, indent_lvl + 3);

}

} else {

cout < < " " < < tcp->name()

< < " (" < < tcp->id() < < ")" < < endl;

}

}

break;

}

}

Note that member_label returns the value of each case label of a union as an Any. To print the label value, show_TC calls the show_label helper function. This function is mostly trivial; it extracts the type code from the passed Any to get the type of the label and extracts the value of the label according to its type using the corresponding operator>>= function:

void

show_label(const CORBA::Any * ap)

{

CORBA::TypeCode_var tc = ap->type(); if (tc->kind() == CORBA::tk_octet) { cout < < "default:" < < endl;

} else {

cout < < "case "; switch (tc->kind()) { case CORBA::tk_short:

CORBA::Short s; *ap >>= s; cout < < s; break;

617

IT-SC book: Advanced CORBA® Programming with C++

case CORBA::tk_long: CORBA::Long l; *ap >>= l;

cout < < l; break;

case CORBA::tk_ushort: CORBA::UShort us; *ap >>= us;

cout < < us; break;

case CORBA::tk_ulong: CORBA::ULong ul; *ap >>= ul;

cout < < ul; break;

case CORBA::tk_boolean: CORBA::Boolean b;

*ap >>= CORBA::Any::to_boolean(b); cout < < (b ? "TRUE" : "FALSE"); break;

case CORBA::tk_char: CORBA::Char c;

*ap >>= CORBA::Any::to_char(c); if (isalnum(c)) {

cout < < "'" < < c < < "'"; } else {

cout < < "'\\" < < setw(3) < < setfill('0') < < oct < < (unsigned)c < < "'";

}

break;

case CORBA::tk_longlong: CORBA::LongLong ll; *ap >>= ll;

cout < < ll; break;

case CORBA::tk_ulonglong: CORBA::ULongLong ull; *ap >>= ull;

cout < < ull; break;

case CORBA::tk_wchar: CORBA::WChar wc;

*ap >>= CORBA::Any::to_wchar(wc); cout < < "'" < < wc < < "'"; break;

case CORBA::tk_enum:

//Oops, problem here... We need the IDL stubs

//to extract the enumerator.

break;

default:

// Union discriminator can't be anything else abort();

}

cout < < ":" < < endl;

}

}

618

IT-SC book: Advanced CORBA® Programming with C++

Most of this code is straightforward. If the type code for a case label has a TCKind value of tk_octet, the corresponding member is the default member of the union. Otherwise, show_label extracts the label value according to the discriminator type indicated by the label's type code. Note that for discriminators of type boolean, char, and wchar, show_label uses the appropriate helper functions on CORBA::Any (to_boolean, to_char, and to_wchar) for the extraction.

One problem arises for union labels of enumerated type. Consider again the union from our climate control system:

union KeyType switch(SearchCriterion) { case ASSET:

AssetType asset_num; case LOCATION:

LocType loc; case MODEL:

ModelType model_num;

};

When show_label is used to decode the type code for a KeyType union, we hit a snag: to extract the label value, we must call an extraction operator that is over-loaded for the enumerated type. For example:

case CORBA::tk_enum: CCS::Controller::SearchCriterion sc;

*ap >>= sc; // No good break;

The problem with this is that we must have the correct overloaded operator linked into the code. This is fine for a version of show_label specifically written for the climate control system; we can simply link the code generated by the IDL compiler. However, suppose we would like to have a generic show_label function that will work for all enumerated types, even those that will be defined in the future. With the extraction functions on type Any we have seen so far, this is impossible. We could try to use the value member function of type Any to get a pointer to the raw value:

case CORBA::tk_enum: const void * val;

val = ap->value(); // No good either...

// Now what? break;

The value member returns a pointer to the value representing the enumerator. However, this does not help. The returned pointer points at data internal to the Any, and we have no idea of the binary layout of that data. (Attempts to cast the memory pointed to by val are not portable and may yield the wrong result.)

619