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

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

our servant. In practice, there is rarely a need to copy-construct or assign servants, so we recommend hiding the copy constructor and default assignment operator for your servant classes.

The implementation of the get_value method simply returns the m_value member:

CORBA::Long MyObject_impl::

get_value() throw(CORBA::SystemException)

{

return m_value;

}

The implementation of get_value is so simple that no error conditions can arise, so it does not throw any exceptions.

Because our servant class inherits virtual functions, it must redeclare those functions exactly as they are declared in the generated skeleton base class, including all exception specifications. Furthermore, the function names, signatures, and exception specifications for servant implementation definitions must exactly match their corresponding declarations. If there are mismatches, they are most likely to hide, rather than override, the inherited skeleton operations; this means that your servant classes inherit pure virtual functions and therefore will not be concrete. Getting these declarations and definitions correct can be error-prone, so many IDL compilers include options that cause them to generate empty servant class declarations and definitions. If your IDL compiler supports such a feature, we highly recommend that you use it when writing your servant classes. If it lacks this feature, we recommend that you cut and paste method declarations and implementations from the generated server-side files to avoid mistakes.

The MyObject_impl class is quite simple, and yet it is complete. Instances of MyObject_impl are fully capable of incarnating CORBA objects of type MyObject.

9.5 Object Incarnation

To use an instance of the MyObject_impl servant class to incarnate a CORBA object, you must create a MyObject_impl servant, create a CORBA object, and register the servant as the incarnation of the CORBA object. Note that merely creating a C++ servant does not imply the creation of a CORBA object; each of the two entities has its own separate, distinct lifetime.

To keep things simple, the following example shows the easiest way to simultaneously create both a C++ servant and a new CORBA object incarnated by that servant:

//First create a servant instance. MyObject_impl servant(42);

//Next, create a new CORBA object and use our new servant

//to incarnate it.

317

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

MyObject_var object = servant._this();

The first line of code creates the servant instance and sets its value to 42. At this point, all we have is a C++ object—no connections between the servant and any CORBA objects have yet been made.

The simple appearance of the second line of code is misleading. This invocation of the _this function of servant implicitly performs all the following steps:

Creates a CORBA object under the Root POA

Registers servant with the Root POA as the implementation for the new object Creates an object reference for the new object

Returns the new object reference

The _this function is supplied by the skeleton class. The following code again shows the generated POA_MyObject class, but this time we have included the _this member function that is generated by the IDL compiler:

class POA_MyObject : public virtual PortableServer::ServantBase{ public:

virtual CORBA::Long get_value()

MyObject_ptr

throw(CORBA::SystemException) = 0;

_this();

// ...

 

};

 

For any skeleton class POA_A representing IDL interface A, the return value of the POA_A::_this function is A_ptr, the C++ object reference type for interface A. Accordingly, in the preceding example, the return type of _this is MyObject_ptr. Because the caller of _this is responsible for ensuring that CORBA::release is eventually invoked on the returned object reference, we have assigned the return value to a MyObject_var in our example on page 355.

Under these circumstances, the CORBA object created by _this is a transient object. A transient CORBA object is bounded by the lifetime of the POA in which it is created. However, _this can provide this form of creation and registration service only if the servant's POA was created with the appropriate policies. The standard set of policies supported by the Root POA were explicitly designed to allow _this to be used in this manner. We explore the details of POA policies in Chapter 11.

9.6 Server main

To complete our simple server application, we must provide a main function such as the following.

#include "my_objectS.hh"

318

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

#include <iostream.h>

int

main(int argc, char * argv[])

{

// Initialize the ORB.

CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);

// Get a reference to the Root POA. CORBA::Object_var obj =

orb->resolve_initial_references("RootPOA");

PortableServer::POA_var poa =

PortableServer::POA::_narrow(obj);

//Activate the Root POA's manager. PortableServer::POAManager_var mgr = poa->the_POA Manager(); mgr->activate();

//Create a MyObject servant and then implicitly create a

//CORBA object and incarnate it with the servant.

MyObject_impl servant(42);

MyObject_var object = servant._this();

//Convert the object reference to a string and write

//it to the standard output.

CORBA::String_var str = orb->object_to_string(object); cout < str < endl;

// Allow the ORB to start processing requests. orb->run();

return 0;

}

This is a completely operational server main, and it works as follows.

Step 1.

We initialize an ORB via the standard CORBA::ORB_init call.

Step 2.

We use the ORB reference returned by ORB_init to invoke resolve_initial_references, which allows you to obtain object references to a small number of well-known interfaces. We use it to get a reference to the Root POA for the ORB, which we in turn use to get a reference to the POAManager for the Root POA. Activating the POAManager allows the Root POA to start processing requests as soon as the ORB starts listening for them.

Step 3.

We create a servant of type MyObject_impl.

Step 4.

We invoke the _this function of the servant to create a new transient CORBA object and incarnate it with the servant. Then we store the returned object reference into a

319

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

MyObject_var so that it will automatically be released when the MyObject_var goes out of scope.

Step 5.

To make the object reference for our new CORBA object available to potential clients, we convert it to a string by passing it to the ORB's object_to_string function. We assign the string returned by this function to a String_var to ensure its cleanup, and we then write it to the application's standard output.

Step 6.

We invoke the ORB::run operation to make the ORB start listening for requests.

The conversion of the object reference to a string (detailed in Section 7.10) al lows clients to obtain the object reference in order to be able to invoke requests on the object. Of course, production applications would never advertise their object references in this manner, instead relying on object reference discovery services such as Naming (see Chapter 18) or Trading (see Chapter 19). However, the string conversion approach suffices for our simple example. A production application would also set up try and catch blocks to handle errors, but we have elided these to keep the example as simple as possible.

9.7 Parameter Passing Rules

Memory management is a key aspect of using the server-side C++ mapping. The simple example we have shown in the previous sections has avoided issues related to memory management in order to focus on servant class definitions.

As with the client-side rules described in Chapter 7, the server-side parameter passing rules are motivated by two overriding requirements.

Location transparency

Memory management must be the same whether or not the client and target object are collocated.

Efficiency

Copying of parameters must be avoided whenever possible, especially when the client and target object are collocated.

Not surprisingly, the server-side parameter passing rules are essentially a mirror image of the client-side rules, thus allowing for efficient dispatch for collocated objects.

The parameter passing rules presented here follow the same order as their presentation in Section 7.14 for the client side. If you are not yet comfortable with the differences between fixedand variable-length types and which IDL types fall into each category, please review Section 7.14.1 before continuing.

320

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

9.7.1 Parameter Passing for Simple Types

Simple types and enumerated types are passed by value or by reference depending on whether the parameter can be changed. Here is an IDL operation that uses a long parameter in all possible directions:

interface Foo {

long long_op(in long l_in, inout long l_inout, out long l_out); };

The corresponding method in the skeleton class has the following signature:

class POA_Foo : public virtual PortableServer::Servan tBase { public:

virtual CORBA::Long long_op(

l_in,

CORBA::Long

CORBA::Long &

l_inout,

CORBA::Long_out

l_out

) throw(CORBA::SystemException) = 0;

// ...

};

An implementation of long_op in a derived servant class might be written as follows:

CORBA::Long

 

Foo_impl::

 

long_op(

l_in,

CORBA::Long

CORBA::Long &

l_inout,

CORBA::Long_out

l_out

) throw(CORBA::SystemException)

{

l_inout = l_in * 2; l_out = l_in / 2; return 99;

}

Values for both l_in and l_inout are passed into this function by the caller. Our implementation modifies the value of l_inout for the server ORB run time to send back to the client. The l_out parameter is uninitialized upon entering this function, so we also set its value. Finally, we send back the return value using a normal C++ return statement.

9.7.2 Parameter Passing for Fixed-Length Complex Types

The rules for passing fixed-length complex types (structures and unions) are similar to those for passing simple types except that in parameters are passed by reference to

321

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

const to avoid copying. Here is an IDL operation that passes a fixed-length structure parameter in all possible directions:

struct Fls {

// Fixed-length struct

long

l_mem;

double

d_mem;

};

interface Foo {

Fls fls_op(in Fls fls_in, inout Fls fls_inout, out Fls fls_out); };

The corresponding method in the skeleton class has the following signature:

class POA_Foo : public virtual PortableServer::ServantBase { public:

virtual Fls fls_op(

const Fls & fls_in,

Fls &

fls_inout,

Fls_out

fls_out

) throw(CORBA::SystemException) = 0;

// ...

};

An implementation of fls_op in a derived servant class might be written as follows:

Fls Foo_impl:: fls_op(

const Fls & fls_in,

Fls &

fls_inout,

Fls_out

fls_out

) throw(CORBA::SystemException)

{

//Use incoming values of fls_in and fls_inout (not shown).

//Modify fls_inout.

fls_inout.l_mem *= 2; fls_inout.d_mem /= 2;

//Initialize fls_out. fls_out.l_mem = 1234; fls_out.d_mem = 5.67e8;

//Create and initialize return value. Fls result = { 4321, -9.87e6 }; return result;

}

The fls_in parameter is a reference to const, thus allowing read-only access to the structure members. Normally our method would first make use of the in and inout values, but to keep the example simple we do not show that here. After we have used the input value for fls_inout, we modify its member values.

322

darr_op(
const Darr darr_in, Darr darr_inout, Darr_out darr_out
) throw(CORBA::SystemException) = 0;
in a derived servant class might be written as follows:

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

Note that as with simple types, inout fixed-length complex types are passed by reference to allow the method to modify them. The fls_out parameter is uninitialized upon entering this method, so we initialize its member values so that the server ORB run time can send it back to the client. Finally, we declare a local Fls instance and statically initialize it, and we return its value by copy via a normal C++ return statement.

9.7.3 Parameter Passing for Arrays with Fixed-Length Elements

IDL arrays map directly to C++ arrays. In C++, arrays are always passed by pointer. Here is an IDL operation that passes arrays of fixed-length types in all possible directions:

typedef double Darr[3];

 

interface Foo {

 

Darr

darr_op(

darr_in,

 

in Darr

 

inout Darr

darr_inout,

 

out Darr

darr_out

};

);

 

 

 

The corresponding method in the skeleton class has the following signature:

class POA_Foo : public virtual PortableServer::ServantBase { public:

virtual Darr_slice *

// ...

};

An implementation of darr_op

Darr_slice *

Foo_impl:: darr_op(

const Darr darr_in, Darr darr_inout, Darr_out darr_out

) throw(CORBA::SystemException)

{

const int array_length = sizeof(darr_in)/sizeof(* darr_in); int i;

//Use incoming values of darr_in and darr_inout (not shown).

//Modify darr_inout.

for (i = 0; i < array_length; i++) { darr_inout[i] *= i;

}

// Initialize darr_out.

323

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

for (i = 0; i < array_length; i++) { darr_out[i] = i * 3.14;

}

// Create and initialize return value. Darr_slice * result = Darr_alloc(); for (i = 0; i < array_length; i++) {

result[i] = i * i;

}

return result;

}

The memory management responsibilities of the servant method are as follows.

The in parameter darr_in is passed as a const Darr, which is effectively the same as a pointer to const CORBA::Double. We are thus allowed to access the values stored in the array but are not allowed to change them. The array is allocated by the caller, and our servant method has no memory management responsibilities for it.

Unfortunately, there are some widely used C++ compilers that for years have been incapable of properly handling the use of const for parameters of multidimensional array types. In practice, this may mean that your servant method may need to declare in multidimensional arrays without using the const keyword. Consult your ORB's documentation to see whether it mentions anything about this problem.

The inout parameter darr_inout is passed as a Darr, which is effectively the same as a pointer to CORBA::Double, allowing us to access and modify the values in the array. As with the in parameter, our servant method has no memory management responsibilities for the array. The caller allocates it and passes it in, and we just read and write its values.

The out parameter darr_out is passed as type Darr_out. This type is a typedef for Darr and is used only for consistency with other out types. An out parameter is uninitialized when passed to a servant method because it passes from server to client. Our example code therefore sets all the values in the array so that the server ORB run time can send it back to the client. Note, though, that just as with the in and inout arrays of fixed-length elements, the caller allocates the array and our servant method has no memory management responsibilities for it.

Because C++ does not allow arrays to be returned by value, the return type of our servant method is a Darr_slice *. As explained in Section 7.14.5, an array slice is a pointer to the array element type; this means that pointer syntax (dereferencing) is not needed in order to index it as an array, thus making it easier to handle. Our servant method dynamically allocates a Darr instance using the Darr_alloc function generated by the IDL compiler, fills in the values, and returns it. The caller of the servant method, which may be the client in the same process for the collocated case or will be the

324

string_op(
const char * s_in, char * & s_inout, CORBA::String_out s_out
) throw(CORBA::SystemException) =

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

ORB itself if the client is remote, is responsible for eventually calling Darr_free on the return value in order to deallocate it.

Allocating the return value using a means other than the generated Darr_alloc function, such as by calling new, is non-portable and may result in application run-time errors when the array is freed.

Return types that are arrays of fixed-length elements represent the only case in the C++ mapping in which a fixed-length type is dynamically allocated. Again, this is because C++ does not allow arrays to be returned by value.

9.7.4 Parameter Passing for Strings and Wide Strings

Because no standard C++ string class existed when the OMG IDL C++ language mapping was defined and because defining another one would have merely added yet another non-standard string to the mix, IDL strings map to char * in C++. Here is an IDL operation that passes string parameters in all possible directions:

interface Foo {

 

string string_op(

s_in,

in string

inout string

s_inout,

out string

s_out

);

 

};

 

The corresponding method in the skeleton class has the following signature:

class POA_Foo : public virtual PortableServer::ServantBase { public:

virtual char *

0;

// ...

};

An implementation of string_oyp in a derived servant class might be written as follows:

char *

 

Foo_impl::

 

string_op(

s_in,

const char *

char * &

s_inout,

CORBA::String_out s_out

) throw(CORBA::SystemException)

{

// Use incoming values of s_in and s_inout (not shown).

325

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

// Modify s_inout.

const char * inout_out_value = "outgoing inout value"; if (strlen(s_inout) < strlen(inout_out_value)) {

CORBA::string_free(s_inout);

s_inout = CORBA::string_dup(inout_out_value); } else {

strcpy(s_inout, inout_out_value);

}

// Initialize s_out.

s_out = CORBA::string_dup("output string");

// Create return value.

return CORBA::string_dup("return string");

}

The memory management responsibilities of the servant method are as follows.

The in parameter s_in is passed as a const char *, so our method cannot change the contents of the string. We use the contents of the in string in a read-only fashion and have no memory management responsibilities for it. For reasons of efficiency, an ORB might avoid copying the in string when unmarshaling it and allow you to directly access the characters from the marshaling buffer, but this does not affect how you write code to use the in string.

The inout parameter s_inout is passed as a char * &, a reference to pointer to char. You can assume that the string has been dynamically allocated with string_alloc or string_dup. You access the string's initial value in the same manner as you access the contents of the in string. To set a value to return to the client, you can either overwrite the string's contents in place or deallocate the string using string_free and allocate a new one. You can use the first approach, overwriting the existing contents, only if the input string is long enough to hold the new contents. The second approach, freeing the original string and allocating a new one, works because the char * pointer is passed by reference, allowing you to set the pointer to point to the newly allocated string. Our example shows both approaches, using strlen to check whether the incoming string is long enough to hold the outgoing string.

Clients must always allocate inout strings dynamically, using string_alloc or string_dup. For a remote object, the server ORB unmarshals the incoming inout string into memory that has been dynamically allocated in the same way and passes a pointer to it to the servant method. After the method returns, the ORB marshals the outgoing string value and then uses string_free to deallocate the memory. This technique works correctly whether the method overwrites the existing value with a new value or deallocates the incoming value and allocates a new string to send back to the client.

326

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

The out parameter s_out is of a class type CORBA::String_out, which for all intents and purposes behaves exactly like a char * &. Because out parameters flow from server to client, our servant method must dynamically allocate the out string using string_alloc or string_dup and assign it to the String_out parameter.

For a collocated client, the out is usually passed back without any interim marshaling or unmarshaling steps. If the client is remote, the server ORB marshals the out string value after the servant method completes; then it deal-locates the string's memory using string_free.

The return string is handled exactly as the out string is. We dynamically allocate it using string_alloc or string_dup and return it to the caller.

Because the C++ mapping prohibits passing null pointers for string parameters, the servant method does not have to check for null char * values passed to it, and it is not allowed to return null pointers as out parameters or return values.

The rules for handling wide strings in servant methods are exactly the same as for strings except that parameter types are CORBA::WChar * instead of char *, and

CORBA::WString_out instead of CORBA:: String_out. Also, wide string allocation and deallocation functions wstring_alloc, wstring_dup, and wstring_free must be used to create and destroy heap-allocated wide string parameters.

9.7.5 Parameter Passing for Variable-Length Complex Types and Type any

Recall that variable-length complex types include sequences as well as structures and unions that (recursively) contain one or more variable-length members. Here is an IDL operation that passes a variable-length structure in all possible directions:

struct Vls {

l_mem;

// Variable-length struct

long

 

string

s_mem;

 

};

 

 

interface Foo {

 

Vls vls_op(

vls_in,

 

in Vls

 

inout Vls

vls_inout,

);

out Vls

vls_out

 

 

};

 

 

The corresponding method in the skeleton class has the following signature:

class POA_Foo : public virtual PortableServer::ServantBase {

327

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

public:

virtual Vls * vls_op(

const Vls & vls_in,

Vls &

vls_inout,

Vls_out

vls_out

) throw(CORBA::SystemException) = 0;

// ...

};

An implementation of vls_op in a derived servant class might be written as follows:

Vls * Foo_impl:: vls_op(

const Vls & vls_in,

Vls &

vls_inout,

Vls_out

vls_out

) throw(CORBA::SystemException)

{

//Use incoming values of vls_in and vls_inout (not shown).

//Modify vls_inout.

vls_inout.l_mem *= 2; vls_inout.s_mem = vls_in.s_mem;

//Initialize vls_out. vls_out = new Vls; vls_out->l_mem = 1234;

vls_out->s_mem = CORBA::string_dup("output string");

//Create and initialize return value.

Vls * result = new Vls; result->l_mem = vls_in.l_mem;

result->s_mem = CORBA::string_dup("return string");

return result;

}

The parameter passing and memory management rules for the vls_iyn parameter and the vls_inout parameter are exactly the same as for fixed-length complex types. The in parameter is passed by reference to const, allowing our method to access but not modify the structure members, whereas the inout parameter is passed by reference to allow both access and modification.

The rules for variable-length out and return parameters, however, differ considerably from those for their fixed-length counterparts. Specifically, you dynamically allocate variable-length out and return values using new and you return them by pointer to the client, which is then responsible for freeing them using delete. In our example, the vls_out parameter is of type Vls_out, which for all intents and purposes is equivalent to a Vls * &. Our method initializes vls_out with a pointer obtained by calling new and then initializes each member of the allocated structure instance so that

328

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

the server ORB run time can send it back to the client. We allocate and initialize the return value in the same way as the out parameter.

Note our use of string_dup to assign string values to the Vls:: s_mem string member. Remember from Section 6.13.2 that structure string members behave similarly to String_var: any char * assigned to any string member is assumed to be dynamically allocated and is adopted by the string member, whereas assignment of a const char * forces a copy.

Special Considerations for Sequences

Sequences are variable-length types, so they follow the memory management rules described here. However, because they supply an overloaded subscript operator for element access (as described in Section 6.14), and because return sequences are dealt with by pointer, developers often make a common indexing mistake within their servant methods. Consider the following IDL operation, which has a sequence return type:

typedef sequence<long> LongSeq;

interface Foo { LongSeq seq_op();

};

An implementation of seq_op in a derived class might erroneously be written as follows:

LongSeq *

Foo_impl::

seq_op() throw(CORBA::SystemException)

{

// Create and initialize the return parameter. LongSeq * result = new LongSeq;

result->length(2);

// wrong

result[0] = 1234;

result[1] = 5678;

// wrong

return result;

 

}

The problem with this implementation is that on the lines marked with the comment "wrong," we are applying the subscript operator to the sequence pointer and not to the sequence itself. The code compiles just fine, so you get no compile-time errors or warnings if you make this mistake. The type resulting from the application of the subscript operator to the pointer to sequence yields a LongSeq for the left-hand side of the assignment. In other words, by applying the subscript operator to a pointer, we are accessing an array of sequences rather than accessing an element of a sequence. Because the left-hand side of the assignment is a sequence, the C++ compiler implicitly converts the right-hand side of the assignment to a sequence using the constructor that allows you to set the maximum size upon creation. The result is that an empty sequence with a buffer

329

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

size of 1234 is assigned to the return value, and another empty sequence with a buffer size of 5678 is assigned to unallocated memory after the return value. If you are lucky, your code will experience a memory access violation when you first test it, but in many cases this mistake will not cause a run-time error. Unless you regularly use memory leak detection tools, which will catch this problem, you will be left wondering why the ORB appears to be changing your two-element return sequence into an empty sequence by the time it is returned to your client application.

To index into the sequence, you must first dereference the pointer to the sequence so that the subscript operator applies to the sequence and not to the pointer:

LongSeq *

Foo_impl::

seq_op() throw(CORBA::SystemException)

{

// Create and initialize the return parameter. LongSeq * result = new LongSeq; result->length(2);

(*result)[0] = 1234;

// correct

(*result)[1] = 5678;

// correct

return result;

 

}

Our code is now correct. The values are assigned to the sequence itself instead of being converted to empty sequences and assigned to unallocated memory.

Another way to avoid this problem is to store the return sequence in a LongSeq_var before initializing it:

LongSeq *

Foo_impl::

seq_op() throw(CORBA::SystemException)

{

// Create and initialize the return parameter. LongSeq_var result = new LongSeq; result->length(2);

result[0]

=

1234;

//

correct

result[1]

=

5678;

//

correct

// To return, take the sequence away from the _var. return result._retn();

}

The LongSeq_var type supplies its own overloaded subscript operator, which forwards the indexing operation to its underlying sequence, so this version of our code is also correct. However, for it to work properly, the dynamically allocated return sequence must be taken away from the LongSeq_var before it goes out of scope; otherwise, the LongSeq_var will destroy the sequence and we will end up returning a dangling sequence pointer. Our example shows how to do this: you simply invoke the _retn function on the LongSeq_var. This instructs it to yield ownership of the sequence pointer. Note that this approach can be used with any dynamically allocated return type

330

varr_op(
const Varr varr_in, Varr_slice * varr_inout, Varr_out varr_out
) throw(CORBA::SystemException) = 0;
in a derived servant class might be written as follows:

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

and not just sequences, and it can be very useful for preventing memory leaks when exceptions are thrown (see Section 9.8).

Finally, note that even though out sequences are also dynamically allocated, this pointer indexing problem does not occur because the LongSeq_out type, like the LongSeq_var type, supplies an overloaded subscript operator. However, if your ORB does not yet implement the use of _out types in method signatures, you can also make the pointer indexing mistake with out sequences.

9.7.6 Parameter Passing for Arrays with Variable-Length Elements

Memory management responsibilities for arrays of variable-length elements are similar to those for other variable-length types. Here is an IDL operation that passes arrays of a variable-length element type in all possible directions:

struct Vls {

// Variable-length struct

long

number;

 

 

string

name;

 

 

};

 

 

 

typedef Vls Varr[3];

// Variable-length array

interface Foo {

 

 

Varr

varr_op(

 

varr_in,

 

in Varr

 

 

inout Varr

varr_inout,

 

out Varr

 

varr_out

};

);

 

 

 

 

 

The corresponding method in the skeleton class has the following signature:

class POA_Foo : public virtual PortableServer::ServantBase { public:

virtual Varr_slice *

// ...

};

An implementation of varr_op

Varr_slice *

Foo_impl:: varr_op(

const Varr varr_in, Varr_slice * varr_inout,

331

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

Varr_out varr_out

) throw(CORBA::SystemException)

{

const int array_length = sizeof(varr_in)/sizeof(*varr_in); int i;

//Use incoming values of varr_in and varr_inout (not shown).

//Modify varr_inout.

varr_inout[0] = varr_in[0];

//Create and initialize varr_out. varr_out = Varr_alloc();

const char * brothers[] = { "John", "Jim", "Rich" }; for (i = 0; i < array_length; i++) {

varr_out[i].number = i + 1; varr_out[i].name = brothers[i];

}

//Create and initialize return value.

Varr_slice * result = Varr_alloc();

const char * sisters[] = { "Teresa", "Lucy", "Michelle" }; for (i = 0; i < array_length; i++) {

result[i].number = i + 1; result[i].name = sisters[i];

}

return result;

}

The memory management rules for the servant method are as follows.

The in parameter varr_in is handled just as with arrays of fixed-length elements. The client allocates and initializes the array, and our method has read-only access to its elements. We thus have no memory management responsibilities for variable-length array in parameters.

Similarly, the inout parameter varr_inout is allocated and initialized by the client, but in this case we are allowed to change its values. The parameter is passed as a Varr_slice *, and that allows us to index the array in a natural manner. Our method has no memory management responsibilities for the inout array.

The out parameter varr_out is passed as a Varr_out, which for all intents and purposes is equivalent to a Varr_slice * &, a reference to a pointer to a Varr_slice. We use the Varr_alloc function to dynamically allocate a Varr instance, and then we fill in its values. The caller of the servant method—either a collocated client or the local ORB if the client is remote—is responsible for eventually calling Varr_free on the out array. Using new or any allocation function other than Varr_alloc is non-portable and could result in application run-time errors when the array is freed.

332

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

For each structure element in the out array, our example assigns a const char * to the name member. Recall that assigning a const char * to any string member of a structure results in the member copying the string.

We allocate and initialize the return value exactly like the out parameter, and the caller is also responsible for making sure that the returned pointer is eventually passed to

Varr_free.

9.7.7 Parameter Passing for Object References

Object references are variable-length types. Because they are like pointers, their parameter passing rules are similar to those for strings. Here is an IDL operation that passes object references in all possible directions:

interface Foo {

 

Foo

ref_op(

ref_in,

 

in Foo

 

inout Foo

ref_inout,

 

out Foo

ref_out

void

);

 

say_hello();

 

};

 

 

The corresponding method in the skeleton class has the following signature:

class POA_Foo : public virtual PortableServer::ServantBase {

public:

 

 

virtual Foo_ptr ref_op(

ref_in,

 

Foo_ptr

 

Foo_ptr & ref_inout,

 

Foo_out

ref_out

virtual void

) throw(CORBA::SystemException) = 0;

say_hello()

 

 

throw(CORBA::SystemException) = 0;

// ...

};

An implementation of ref_op in a derived servant class might be written as follows:

void Foo_impl::

say_hello() throw(CORBA::SystemException)

{

cout < < "Hello!" < < endl;

}

Foo_ptr Foo_impl:: ref_op(

Foo_ptr ref_in, Foo_ptr & ref_inout, Foo_out ref_out

333

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

) throw(CORBA::SystemException)

{

// Use ref_in.

if (!CORBA::is_nil(ref_in)) { ref_in->say_hello();

}

// Use ref_inout.

if (!CORBA::is_nil(ref_inout)) { ref_inout->say_hello();

}

//Modify ref_inout. CORBA::release(ref_inout); ref_inout = _this();

//Initialize ref_out.

Foo_impl * new_servant = new Foo_impl; ref_out = new_servant->_this();

// Create return value. return Foo::_nil();

}

The memory management rules for the servant method are as follows.

The in parameter ref_in is passed by value. Note that it is the object reference, and not the object that the reference refers to, that is passed to the servant method. If it is not nil, ref_in can be used to invoke operations on the referred-to object. We merely use the object reference and have no memory management responsibilities for it.

The inout parameter ref_inout is passed by reference, allowing us to access its incoming value and set it to a new value for the server ORB run time to send back to the client. We must release the incoming object reference before setting it to a new value. In our example, we set ref_inout to the object reference of the target object, which is obtained by invoking the _this function. The return value of _this must be released by the caller, so by assigning it to ref_inout we are correctly passing that responsibility to our caller.

The out parameter is passed as a Foo_out, which for all intents and purposes behaves exactly like a Foo_ptr &. It is uninitialized when passed in, and we must initialize it with a Foo object reference, either nil or non-nil. The object reference we assign to the ref_out parameter becomes the responsibility of the caller.

In our example, we are initializing the out parameter by creating a new Foo_impl servant and using its _this function to implicitly create a new CORBA object. We then assign the return value of _this to ref_out, passing to our caller the responsibility for releasing it. Note that the servant is not created on the stack; doing so would mean that it would be destroyed at the end of the servant method, leaving a dangling pointer registered with the POA. Instead, we allocate the servant on the heap. At some later point we must delete it. We show examples of servant deletion in Section 11.9 when we

334

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

discuss details of POA object deactivation and servant etherealization (the opposite of incarnation).

We handle the return object reference exactly as we handle the out object reference. The caller is responsible for releasing the returned object reference. Our example invokes Foo::_nil() to return a nil object reference.

You may be surprised to see that in references are passed by value as Foo_ptr instead of by reference as const Foo_ptr &. In other words, should the referred-to object be considered const, or should the object reference parameter itself be const?

IDL provides no way to declare that an operation does not modify the state of the object. The reason is simply that IDL is a declarative language that is independent of any particular programming language. Thus, object state is not specified in IDL, so it may vary widely among different implementations of an IDL interface. Furthermore, the C++ concept of const member functions is not a feature common to many programming languages. In short, declaring a parameter of an operation as in is not the equivalent of declaring it as const.

Because in object references are passed by value, however, they are conceptually constant. If the caller is collocated with the servant, the pass-by-value approach means that any changes made to the in object reference itself (but not the object it refers to) by the servant method are never seen by the caller; the servant method changes only its own local copy of the object reference. If the caller is remote, it never sees any changes made to the reference by the servant method because in parameters are sent only from client to server and not back. The pass-by-value approach thus contributes to location transparency for parameter passing.

The use of const for other types of in parameters, such as sequences, structures, unions, and strings, also helps preserve location transparency for collocated clients and objects. If a client invokes an operation on a collocated object, most ORBs avoid marshaling the parameters and instead pass them directly as C++ types. If in parameters are not passed as const, any changes made to them by a collocated servant will be visible to the client. Passing them as reference to const allows them to be passed efficiently in the collocated case without violating the semantics of the in parameter direction.

Finally, note that our example invokes the say_hello operation using both the in and the inout object references. This implies that our server application is also a client of other Foo objects or perhaps even a client of the same Foo object incarnated by this servant. This situation is very common in practice—few CORBA applications are pure clients or pure servers, but instead tend to be clients of some CORBA objects and servers for others. The invocations made from within our earlier servant method are identical in nature to Foo object invocations made by pure clients. In other words, the fact that we are also a server application does not change the nature of these invocations.

335