VisiBroker for C++ Developer’s Guide : Using the Dynamic Invocation Interface

Using the Dynamic Invocation Interface
The developers of most client programs know the types of the CORBA objects their code will invoke, and they include the compiler-generated stubs for these types in their code. By contrast, developers of generic clients cannot know what kinds of objects their users will want to invoke. Such developers use the Dynamic Invocation Interface (DII) to write clients that can invoke any method on any CORBA object from knowledge obtained at runtime.
What is the dynamic invocation interface?
The Dynamic Invocation Interface (DII) enables a client program to invoke a method on a CORBA object whose type was unknown at the time the client was written. The DII contrasts with the default static invocation, which requires that the client source code include a compiler-generated stub for each type of CORBA object that the client intends to invoke. In other words, a client that uses static invocation declares in advance the types of objects it will invoke. A client that uses the DII makes no such declaration because its programmer does not know what kinds of objects will be invoked. The advantage of the DII is flexibility; it can be used to write generic clients that can invoke any object, including objects whose interfaces did not exist when the client was compiled. The DII has two disadvantages:
The DII is purely a client interface. Static and dynamic invocations are identical from an object implementation's point of view.
You can use the DII to build clients like these:
Bridges or adapters between script environments and CORBA objects. For example, a script calls your bridge, passing object and method identifiers and parameter values. Your bridge constructs and issues a dynamic request, receives the result, and returns it to the scripting environment. Such a bridge could not use static invocation because its developer could not know in advance what kinds of objects the script environment would want to invoke.
Generic object testers. For example, a client takes an arbitrary object identifier, looks up its interface in the interface repository (see “Using Interface Repositories”), and then invokes each of its methods with artificial argument values. Again, this style of generic tester could not be built with static invocation.
Note
Clients must pass valid arguments in DII requests. Failure to do so can produce unpredictable results, including server crashes. Although it is possible to dynamically type-check parameter values with the interface repository, it is expensive. For best performance, ensure that the code (for example, script) that invokes a DII-using client can be trusted to pass valid arguments.
Introducing the main DII concepts
The dynamic invocation interface is actually distributed among a handful of CORBA interfaces. Furthermore, the DII frequently offers more than one way to accomplish a task--the trade-off being programming simplicity versus performance in special situations. As a result, DII is one of the more difficult CORBA facilities to grasp. This section is a starting point, a high-level description of the main ideas.
To use the DII you need to understand these concepts, starting from the most general:
Request objects
Any and Typecode objects
Request sending options
Reply receiving options
Using request objects
A Request object represents one invocation of one method on one CORBA object. If you want to invoke two methods on the same CORBA object, or the same method on two different objects, you need two Request objects. To invoke a method you first need the target reference, an object reference representing the CORBA object. Using the target reference, you create a Request, populate it with arguments, send the Request, wait for the reply, and obtain the result from the Request.
There are two ways to create a Request. The simpler way is to invoke the target object's _request method, which all CORBA objects inherit. This does not, in fact, invoke the target object. You pass _request the IDL name of the method you intend to invoke in the Request, for example, “get_balance.” To add argument values to a Request created with _request, you invoke the Request's add_value method for each argument required by the method you intend to invoke. To pass one or more Context objects to the target, you must add them to the Request with its ctx method.
Although not intuitively obvious, you must also specify the type of the Request's result with its result method. For performance reasons, the messages exchanged between the VisiBroker ORBs do not contain type information. By specifying a place holder result type in the Request, you give the VisiBroker ORB the information it needs to properly extract the result from the reply message sent by the target object. Similarly, if the method you are invoking can raise user exceptions, you must add place holder exceptions to the Request before sending it.
The more complicated way to create a Request object is to invoke the target object's _create_request method, which, again, all CORBA objects inherit. This method takes several arguments which populate the new Request with arguments and specify the types of the result and user exceptions, if any, that it may return. To use the _create_request method you must have already built the components that it takes as arguments. The potential advantage of the _create_request method is performance. You can reuse the argument components in multiple _create_request calls if you invoke the same method on multiple target objects.
Note
There are two overloaded forms of the _create_request method: one that includes ContextList and ExceptionList parameters, and one that does not. If you want to pass one or more Context objects in your invocation, and/or the method you intend to invoke can raise one or more user exceptions, you must use the _create_request method that has the extra parameters.
Encapsulating arguments with the Any type
The target method's arguments, result, and exceptions are each specified in special objects called Anys. An Any is a generic object that encapsulates an argument of any type. An Any can hold any type that can be described in IDL. Specifying an argument to a Request as an Any allows a Request to hold arbitrary argument types and values without making the compiler complain of type mismatches. (The same is true of results and exceptions.)
An Any consists of a TypeCode and a value. A value is just a value, and a TypeCode is an object that describes how to interpret the bits in the value (that is, the value's type). Simple TypeCode constants for simple IDL types, such as long and Object, are built into the header files produced by the idl2cpp compiler. TypeCodes for IDL constructed types, such as structs, unions, and typedefs, have to be constructed. Such TypeCodes can be recursive because the types they describe can be recursive.
Consider a struct consisting of a long and a string. The TypeCode for the struct contains a TypeCode for the long and a TypeCode for the string. The idl2cpp compiler will generate TypeCodes for the constructed types in an IDL file if the compiler is invoked with the -type_code_info option. However, if you are using the DII, you need to obtain TypeCodes at runtime. You can get a TypeCode at runtime from an interface repository (see “Using Interface Repositories”) or by asking the VisiBroker ORB to create one by invoking ORB::create_struct_tc or ORB::create_exception_tc.
If you use the _create_request method, you need to put the Any-encapsulated target method arguments in another special object called an NVList. No matter how you create a Request, its result is encoded as an NVList. Everything said about arguments in this paragraph applies to results as well. “NV” stands for named value, and an NVList consists of a count and number of items, each of which has a name, a value, and a flag. The name is the argument name, the value is the Any encapsulating the argument, and the flag denotes the argument's IDL mode (for example, in or out). The result of Request is represented a single named value.
Options for sending requests
Once you create and populate a Request with arguments, a result type, and exception types, you send it to the target object. There are several ways to send a Request,
The simplest is to call the Request's invoke method, which blocks until the reply message is received.
More complex, but not blocking, is the Request's send_deferred method. This is an alternative to using threads for parallelism. For many operating systems the send_deferred method is more economical than spawning a thread.
If your motivation for using the send_deferred method is to invoke multiple target objects in parallel, you can use the VisiBroker ORB object's send_multiple_requests_deferred method instead. It takes a sequence of Request objects.
Use the Request's send_oneway method if, and only if, the target method has been defined in IDL as oneway.
You can invoke multiple oneway methods in parallel with the VisiBroker ORB's send_multiple_requests_oneway method.
Options for receiving replies
If you send a Request by calling its invoke method, there is only one way to get the result: use the Request object's env method to test for an exception, and if none, extract the NamedValue from the Request with its result method. If you used the send_oneway method, then there is no result. If you used the send_deferred method, you can periodically check for completion by calling the Request's poll_response method which returns a code indicating whether the reply has been received. If, after polling for a while, you want to block waiting for completion of a deferred send, use the Request's get_response method.
If you have sent Requests with the send_multiple_requests_deferred method, you can find out if a particular Request is complete by invoking that Request's get_response method. To learn when any outstanding Request is complete, use the VisiBroker ORB's get_next_response method. To do the same thing without risking blocking, use the VisiBroker ORB's poll_next_response method.
Steps for invoking object operations dynamically
To summarize, here are the steps that a client follows when using the DII,
1
Make sure the-type_code_info option is passed to the idl compiler so that type codes are generated for IDL interfaces and types.
2
3
Create a Request object for the target object.
4
5
6
Example programs for using the DII
Several example programs that illustrate the use of the DII are included in the following directory:
<install_dir>/examples/vbe/bank_dynamic
These example programs are used to illustrate DII concepts in this section.
Compile these example programs with the VIS_INCLUDE_IR flag, and add the typecode generation option.
Obtaining a generic object reference
When using the DII, a client program does not have to use the traditional bind mechanism to obtain a reference to the target object, because the class definition for the target object may not have been known to the client at compile time.
The code sample below shows how your client program can use the bind method offered by the VisiBroker ORB object to bind to any object by specifying its name. This method returns a generic CORBA::Object.
. . .
CORBA::Object_var account;
try {
// initialize the ORB.
CORBA::ORB_ptr orb = CORBA::ORB_init(argc, argv);
} catch (const CORBA::Exception& e)
cout << "Failure during ORB_init" << endl;
cout << e << endl;
}
. . .
try {
// Request ORB to bind to object supporting the account interface.
account = orb->bind("IDL:Account:1.0");
} catch (const CORBA::Exception& excep)
cout << "Error binding to account" << endl;
cout << excep << endl;
}
cout << "Bound to account object" << endl;
. . .
Creating and initializing a request
When your client program invokes a method on an object, a Request object is created to represent the method invocation. The Request object is written, or marshaled, to a buffer and sent to the object implementation. When your client program uses client stubs, this processing occurs transparently. Client programs that wish to use the DII must create and send the Request object themselves.
Note
There is no constructor for this class. The Object's _request method or Object's _create_request method are used to create a Request object.
Request class
The following code sample shows the Request class. The target of the request is set implicitly from the object reference used to create the Request. The name of the operation must be specified when the Request is created.
class Request {
public:
CORBA::Object_ptr target() const;
const char* operation() const;
CORBA::NVList_ptr arguments();
CORBA::NamedValue_ptr result();
CORBA::Environment_ptr env();
void ctx(CORBA::Context_ptr ctx);
CORBA::Context_ptr ctx() const;
CORBA::Status invoke();
CORBA::Status send_oneway();
CORBA::Status send_deferred();
CORBA::Status get_response();
CORBA::Status poll_response();
. . .
};
};
Ways to create and initialize a DII request
Once you have issued a bind to an object and obtained an object reference, you can use one of two methods for creating a Request object.
The following sample shows the methods offered by the CORBA::Object class.
class Object {
. . .
CORBA::Request_ptr _request(Identifier operation);
CORBA::Status _create_request(
CORBA::Context_ptr ctx,
const char *operation,
CORBA::NVList_ptr arg_list,
CORBA::NamedValue_ptr result,
CORBA::Request_ptr request,
CORBA::Flags req_flags);
CORBA::Status _create_request(
CORBA::Context_ptr ctx,
const char *operation,
CORBA::NVList_ptr arg_list,
CORBA::NamedValue_ptr result,
CORBA::ExceptionList_ptr eList,
CORBA::ContextList_ptr ctxList,
CORBA::Request_out request,
CORBA::Flags req_flags);
. . .
};
Using the create_request method
You can use the _create_request method to create a Request object, initialize the Context, the operation name, the argument list to be passed, and the result. Optionally, you can set the ContextList for the request, which corresponds to the attributes defined in the request's IDL. The request parameter points to the Request object that was created for this operation.
Using the _request method
The code sample in “Example of creating a Request object” shows the use of the _request method to create a Request object, specifying only the operation name. After creating a float request, calls to its add_in_arg method add an input parameter Account name. Its result type is initialized as an Object reference type via a call to set_return_type method. After a call has been made, the return value is extracted with the result's call to the result method. The same steps are repeated to invoke another method on an Account Manager instance with the only difference being in-parameters and return types.
The req, an Any object is initialized with the desired account name and added to the request's argument list as an input argument. The last step in initializing the request is to set the result value to receive a float.
Example of creating a Request object
A Request object maintains ownership of all memory associated with the operation, the arguments, and the result so you should never attempt to free these items. The following code sample is an example of creating a request object.
. . .
CORBA::NamedValue_ptr
result;
CORBA::Any_ptr resultAny;
CORBA::Request_var req;
CORBA::Any customer;
. . .
try {
req = account->_request("balance");

// Create argument to request
customer <<= (const char *) name;
CORBA::NVList_ptr arguments = req->arguments();
arguments->add_value("customer", customer, CORBA::ARG_IN);
// Set result
result = req->result();
resultAny = result->value();
resultAny->replace(CORBA::_tc_float, &result);
} catch(CORBA::Exception& excep) {
. . .
Setting the context for the request
Though it is not used in the example program, the Context object can be used to contain a list of properties, stored as NamedValue objects, that will be passed to the object implementation as part of the Request. These properties represent information that is automatically communicated to the object implementation.
class Context {
public:
const char *context_name() const;
CORBA::Context_ptr parent();
CORBA::Status create_child(const char *name, CORBA::Context_ptr&);
CORBA::Status set_one_value(const char *name, const CORBA::Any&);
CORBA::Status set_values(CORBA::NVList_ptr);
CORBA::Status delete_values(const char *name);
CORBA::Status get_values(
const char *start_scope,
CORBA::Flags,
const char *name,
CORBA::NVList_ptr&) const;
};
Setting arguments for the request
The arguments for a Request are represented with a NVList object, which stores name-value pairs as NamedValue objects. You can use the arguments method to obtain a pointer to this list. This pointer can then be used to set the names and values of each of the arguments.
Note
Always initialize the arguments before sending a Request. Failure to do so will result in marshaling errors and may even cause the server to abort.
Implementing a list of arguments with the NVList
This class implements a list of NamedValue objects that represent the arguments for a method invocation. Methods are provided for adding, removing, and querying the objects in the list. The following code sample is an example of the NVList class:
class NVList {
public:
. . .
CORBA::Long count() const;
CORBA::NamedValue_ptr add(Flags);
CORBA::NamedValue_ptr add_item(const char *name, CORBA::Flags flags);
CORBA::NamedValue_ptr add_value(
const char *name,
const CORBA::Any *any,
CORBA::Flags flags);
CORBA::NamedValue_ptr add_item_consume(char *name, CORBA::Flags flags);
CORBA::NamedValue_ptr add_value_consume(
char *name,
CORBA::Any *any,
CORBA::Flags flags);
CORBA::NamedValue_ptr item(CORBA::Long index);
CORBA::Status remove(CORBA::Long index);
. . .
};
Setting input and output arguments with the NamedValue Class
This class implements a name-value pair that represents both input and output arguments for a method invocation request. The NamedValue class is also used to represent the result of a request that is returned to the client program. The name property is simply a character string and the value property is represented by an Any class. The following code sample is an example of the NamedValue class.
class NamedValue {
public:
const char *name() const;
CORBA::Any *value() const;
CORBA::Flags flags() const;
};
The following table describes the methods in the NamedValue class.
Returns a pointer to an Any object representing the item's value that you can then use to initialize the value. For more information, see “Passing type safely with the Any class”.
Passing type safely with the Any class
This class is used to hold an IDL-specified type so that it may be passed in a type-safe manner.
Objects of this class have a pointer to a TypeCode that defines the contained object's type and a pointer to the contained object. Methods are provided to construct, copy, and release an object as well as initialize and query the object's value and type. In addition, streaming operators methods are provided to read and write the object from and to a stream. The following code sample is an example of defining this class.
class Any {
public:
. . .
CORBA_TypeCode_ptr type();
void type(CORBA_TypeCode_ptr tc);
const void *value() const;
static CORBA::Any_ptr _nil();
static CORBA::Any_ptr _duplicate(CORBA::Any *ptr);
static void _release(CORBA::Any *ptr);
. . .
}
Representing argument or attribute types with the TypeCode class
This class is used by the Interface Repository and the IDL compiler to represent the type of arguments or attributes. TypeCode objects are also used in a Request object to specify an argument's type, in conjunction with the Any class.
TypeCode objects have a kind and parameter list property. The following code sample is an example of the TypeCode class.
The following table shows the kinds and parameters for the TypeCode objects.
repository_id, interface_name
repository_id, alias_name, TypeCode
length, TypeCode
repository_id, enum-name, enum-id1, enum-id2, ... enum-idn
repository_id, exception_name, StructMembers
digits, scale
id, name
repository_id, interface_id
TypeCode, maxlen
repository_id, struct-name, {member1, TypeCode1}, {membern, TypeCoden}
repository_id, union-name, switch TypeCode,{label-value1, member-name1, TypeCode1}, {labell-valuen, member-namen, TypeCoden}
repository_id, value_name, boxType
repository_id, value_name, typeModifier, concreteBase, members
TypeCode class:
class _VISEXPORT CORBA_TypeCode {
public:
. . .
// For all CORBA_TypeCode kinds
CORBA::Boolean
equal(CORBA_TypeCode_ptr tc) const;
CORBA::Boolean equivalent(CORBA_TypeCode_ptr tc) const;
CORBA_TypeCode_ptr get_compact_typecode() const;
CORBA::TCKind kind() const // . . .
// For tk_objref, tk_struct, tk_union, tk_enum, tk_alias and tk_except
virtual const char* id() const; // raises(BadKind);
virtual const char *name() const; // raises(BadKind);
// For tk_struct, tk_union, tk_enum and tk_except
virtual CORBA::ULong member_count() const;
// raises((BadKind));
virtual const char *member_name(CORBA::ULong index) const;
// raises((BadKind, Bounds));
// For tk_struct, tk_union and tk_except
virtual CORBA_TypeCode_ptr member_type(CORBA::ULong index) const;
// raises((BadKind, Bounds));
// For tk_union
virtual CORBA::Any_ptr member_label(CORBA::ULong index) const;
// raises((BadKind, Bounds));
virtual CORBA_TypeCode_ptr discriminator_type() const;
// raises((BadKind));
virtual CORBA::Long default_index() const;
// raises((BadKind));
// For tk_string, tk_sequence and tk_array
virtual CORBA::ULong length() const;
// raises((BadKind));
// For tk_sequence, tk_array and tk_alias
virtual CORBA_TypeCode_ptr content_type() const;
// raises((BadKind));


// For tk_fixed
virtual CORBA::UShort fixed_digits() const;
// raises (BadKind)
virtual CORBA::Short fixed_scale() const;
// raises (BadKind)
// for tk_value
virtual CORBA::Visibility
member_visibility(CORBA::ULong index) const;
// raises(BadKind, Bounds);
virtual CORBA::ValueModifier type_modifier() const;
// raises(BadKind);
virtual CORBA::TypeCode_ptr concrete_base_type() const;
// raises(BadKind);
};
Sending DII requests and receiving results
The Request class, as discussed in “Creating and initializing a request”, provides several methods for sending a request once it has been properly initialized.
Invoking a request
The simplest way to send a request is to call its invoke method, which sends the request and waits for a response before returning to your client program. The return_value method returns a pointer to an Any object that represents the return value. The following code sample shows how to send a request with invoke.
try {
. . .
// Create request that will be sent to the account object
request = account->_request("balance");
// Set the result type
request->set_return_type(CORBA::_tc_float);
// Execute the request to the account object
request->invoke();
// Get the return balance
CORBA::Float balance;
CORBA::Any& balance_result = request->return_value();
balance_result >>= balance;
// Print out the balance
cout << "The balance in " << name << "'s account is $" << balance << endl;
} catch(const CORBA::Exception& e) {
cerr << e << endl;
return 1;
}
return 0;
. . .
Sending a deferred DII request with the send_deferred method
A non-blocking method, send_deferred, is also provided for sending operation requests. It allows your client to send the request and then use the poll_response method to determine when the response is available. The get_response method blocks until a response is received. The following codes show how these methods are used. The following sample shows you how to use the send_deferred and poll_response methods to send a deferred DII request.
. . .
try {
// Create request that will be sent to the manager object
CORBA::Request_var request = manager->_request("open");
// Create argument to request
CORBA::Any customer;
customer <<= (const char *) name;
CORBA::NVList_ptr arguments = request->arguments();
arguments->add_value( "name" , customer, CORBA::ARG_IN );
// Set result type
request->set_return_type(CORBA::_tc_Object);
// Creation of a new account can take some time
// Execute the deferred request to the manager object
request->send_deferred();
VISPortable::vsleep(1);
while (!request->poll_response()) {
cout << " Waiting for response..." << endl;
VISPortable::vsleep(1); // Wait one second between polls
}
request->get_response();
// Get the return value
CORBA::Object_var account;
CORBA::Any& open_result = request->return_value();
open_result >>= CORBA::Any::to_object(account.out());
. . .
}
Sending an asynchronous DII request with the send_oneway method
The send_oneway method can be used to send an asynchronous request. Oneway requests do not involve a response being returned to the client from the object implementation.
Sending multiple requests
A sequence of DII Request objects can be created using array of Request objects. A sequence of requests can be sent using the VisiBroker ORB methods send_multiple_requests_oneway or send_multiple_requests_deferred. If the sequence of requests is sent as oneway requests, no response is expected from the server to any of the requests.
The following code sample shows how two requests are created and then used to create a sequence of requests. The sequence is then sent using the send_multiple_requests_deferred method.
. . .
// Create request to balance
try {
req1 = account->_request("balance");
// Create argument to request
customer1 <<= (const char *) "Happy";
CORBA::NVList_ptr arguments = req1->arguments();
arguments->add_value("customer", customer1, CORBA::ARG_IN);
// Set result
. . .
} catch(const CORBA::Exception& excep) {
cout << "Error while creating request" << endl;
cout << excep << endl;
}
// Create request2 to slowBalance
try {
req2 = account->_request("slowBalance");
// Create argument to request
customer2 <<= (const char *) "Sleepy";
CORBA::NVList_ptr arguments = req2->arguments();
arguments->add_value("customer", customer2, CORBA::ARG_IN);
// Set result
. . .
} catch(const CORBA::Exception& excep) {
cout << "Error while creating request" << endl;
cout << excep << endl;
}
// Create request sequence
CORBA::Request_ptr reqs[2];
reqs[0] = (CORBA::Request*) req1;
reqs[1] = (CORBA::Request*) req2;
CORBA::RequestSeq reqseq((CORBA::ULong)2, 2, (CORBA::Request_ptr *) reqs);
// Send the request
try {
orb->send_multiple_requests_deferred(reqseq);
cout << "Send multiple deferred calls are made..." << endl;
} catch(const CORBA::Exception& excep) {
. . .
Receiving multiple requests
When a sequence of requests is sent using send_multiple_requests_deferred, the poll_next_response and get_next_response methods are used to receive the response the server sends for each request.
The VisiBroker ORB method poll_next_response can be used to determine if a response has been received from the server. This method returns true if there is at least one response available. This method returns false if there are no responses available.
The VisiBroker ORB method get_next_response can be used to receive a response. If no response is available, this method will block until a response is received. If you do not wish your client program to block, use the poll_next_response method to first determine when a response is available and then use the get_next_response method to receive the result. The following code sample shows an example of sending multiple requests and receiving the results:
class CORBA {
class ORB {
. . .
typedef sequence <Request_ptr> RequestSeq;
void send_multiple_requests_oneway(const RequestSeq &);
void send_multiple_requests_deferred(const RequestSeq &);
Boolean poll_next_response();
Status get_next_response();
. . .
};
};
Using the interface repository with the DII
One source of the information needed to populate a DII Request object is an interface repository (IR) (see , “Using Interface Repositories”). The following example uses an interface repository to get obtain the parameters of an operation. Note that the example, atypical of real DII applications, has built-in knowledge of a remote object's type (Account) and the name of one of its methods (balance). An actual DII application would get that information from an outside source, for example, a user.
Binds to any Account object.
Looks up the Account's balance method in the IR and builds an operation list from the IR OperationDef.
Creates argument and result components and passes these to the _create_request method. Note that the balance method does not return an exception.
Invokes the Request, extracts and prints the result.
// acctdii_ir.C
// This example illustrates IR and DII
#include <iostream.h>
#include "corba.h"
int main(int argc, char* const* argv) {
CORBA::ORB_ptr orb;
CORBA::Object_var account;
CORBA::NamedValue_var result;
CORBA::Any_ptr resultAny;
CORBA::Request_var req;
CORBA::NVList_var operation_list;
CORBA::Any customer;
CORBA::Float acct_balance;
try {
// use argv[1] as the account name, or a default.
CORBA::String_var name;
if (argc == 2)
name = (const char *) argv[1];
else
name = (const char *) "Default Name";
try {
// Initialize the ORB.
orb = CORBA::ORB_init(argc, argv);
} catch(const CORBA::Exception& excep) {
cout << "Failure during ORB_init" << endl;
cout << excep << endl;
exit(1);
}
cout << "ORB_init succeeded" << endl;
// Unlike traditional binds, this bind is called off of "orb"
// and returns a generic object pointer based on the interface name
try {
account = orb->bind("IDL:Account:1.0");
} catch(const CORBA::Exception& excep) {
cout << "Error binding to account" << endl;
cout << excep << endl;
exit(2);
}
cout << "Bound to account object" << endl;
// Obtain Operation Description for the "balance" method of
// the Account
try {
CORBA::InterfaceDef_var intf = account->_get_interface();
if (intf == CORBA::InterfaceDef::_nil()) {
cout << "Account returned a nil interface definition. " << endl;
cout << " Be sure an Interface Repository is running and" << endl;
cout << " properly loaded" << endl;
exit(3);
}
CORBA::Contained_var oper_container = intf->lookup("balance");
CORBA::OperationDef_var oper_def =
CORBA::OperationDef::_narrow(oper_container);
orb->create_operation_list(oper_def, operation_list.out());

} catch(const CORBA::Exception& excep) {
cout << "Error while obtaining operation list" << endl;
cout << excep << endl;
exit(4);
}
// Create request that will be sent to the account object
try {
// Create placeholder for result
orb->create_named_value(result.out());
resultAny = result->value();
resultAny->replace( CORBA::_tc_float, &result);
// Set the argument value within the operation_list
CORBA::NamedValue_ptr arg = operation_list->item(0);
CORBA::Any_ptr anyArg = arg->value();
*anyArg <<= (const char *) name;

// Create the request
account->_create_request(CORBA::Context::_nil(),

"balance",
operation_list,
result,
req.out(),
0);

} catch(const CORBA::Exception& excep) {
cout << "Error while creating request" << endl;
cout << excep << endl;
exit(5);
}
// Execute the request
try {
req->invoke();
CORBA::Environment_ptr env = req->env();
if ( env->exception() ) {
cout << "Exception occurred" << endl;
cout << *(env->exception()) << endl;
acct_balance = 0;
} else {
// Get the return value;
acct_balance = *(CORBA::Float *)resultAny->value();
}
} catch(const CORBA::Exception& excep) {
cout << "Error while invoking request" << endl;
cout << excep << endl;
exit(6);
}
// Print out the results
cout << "The balance in " << name << "'s account is $";
cout << acct_balance << "." << endl;
} catch ( const CORBA::Exception& excep ) {
cout << "Error occurred" << endl;
cout << excep << endl;
}