Hello World C++ Client

Our source for the client is contained in client.cpp. What follows is a line-by-line analysis of that code. Because there is a good bit of overlap, much of what is presented here is discussed in more detail in our explanation of our Hello World C++ server.


First, we include the header file that contains the definitions for the stub class that defines our World interface. Note the 'C' suffix of the basename indicating that the file is required by client applications.
#include "HelloC.h"
	
We wrap the contents of our main function within a "try block".
main (int argc, char** argv)
{
    try {
	
We initialize the ORB, forwarding the parameters from main() so that ORB_init may remove from argv those options that pertain to the ORB runtime.
            // Initialize the ORB
        CORBA_ORB_var orb = CORBA::ORB_init(argc, argv);
	
Here is a good example of why you should always call ORB_init first. We expect the number of arguments to be two: the program name (client), and a stringified IOR (Inter-operable Object Reference). Had we checked this value prior to calling ORB_init, we would've prevented the user from specifying any ORB-specific parameters on the command line.
            // Expect IOR as command-line argument
        if (argc != 2)
        {
            cerr << "Usage: client IOR" << endl;
            throw 0; // neat trick used to exit try block
        }
	
Similarly to the way we called object_to_string in the server, we use the ORB method, string_to_object, to convert the command line parameter back to a valid object reference. Note the return type, CORBA::Object: all CORBA objects inherit from CORBA::Object.
            // Destringify the IOR
        CORBA::Object_var obj = orb->string_to_object(argv[1]);
	
If the passed string is not a valid IOR, an exception could be raised. Even if that doesn't happen, we still want to ensure that the reference is not nil. The only portable way to do that is with the CORBA::is_nil method.
        if (CORBA::is_nil(obj))
        {
            cerr << "Nil World reference" << endl;
            throw 0;
        }
	
Since obj is of type CORBA::Object we need to "narrow" it to the type we expect: a World object. We talked about the _narrow() method in the C++ server tutorial. Now is probably a good time to talk about the _var suffix. For just about any type T in CORBA, there are corresponding T_var and T_ptr types. A T_ptr behaves exactly like a C++ pointer. In fact, it is most often implemented as a T*. For the most part, a T_var also behaves like a C++ pointer, except that it's a little "smarter" -- sometimes too smart for its own good! The T_var classes were developed to make dynamic memory management a little less error prone, because they release the memory they "own" when they go out of scope. Most of the time, you can subscribe to the policy of, "When in doubt, use a T_var." An important exception has to do with function parameters: declaring parameters to be of type T_var can be very expensive because an assignment of one T_var to another implies a deep copy of a T, and you probably don't want to do that. Even when you think you do, you probably don't: you probably will want to pass a T_ptr and _duplicate() it instead. I know this isn't very helpful. The truth is, it's very confusing. I wish you the best of luck.
            // Narrow an Object to a World
        World_var world = World::_narrow(obj);
        if (CORBA::is_nil(world))
        {
            cerr << "Invalid World reference" << endl;
            throw 0;
        }
	
Finally!! We get to invoke a method on our CORBA object! Recall that it returns a dynamically-allocated string, so we'll let a CORBA::String_var worry about freeing the memory when we're done. Note that we can't tell from looking at this code whether we're invoking a local or a remote function. You'll have to decide whether that's good or bad...
            // Say hello to the world
        CORBA::String_var s = world->hello();
	
Let's echo the World object's response to stdout.
        cout << "World said \"" << s << "\"" << endl;
	
This completes our try block, so we'll include the obligatory catch block. This is identical to the catch block in our C++ server tutorial.
    }
    catch (const CORBA::Exception& e)
    {
        const char* err = e._id();
        cerr << "Client caught CORBA exception: " << err << endl;
        return 100;
    }
	
This block will catch all those nifty null exceptions resulting from those clever "throw 0" commands above.
    catch (...)
    {
            // Logical error; message already displayed
        return 1;
    }
	
Upon success, return 0 and exit.
    return 0;
}
	

So much for the source code. You're probably ready to build and run the C++ client and server now.

Back to... [ Corba Tutorials ]


Jim Crossley
Last modified: Tue Mar 23 17:55:19 EST 1999