|
|
|
Copyright © 2003-2010 ZeroC, Inc. |
7.2 The C++ Client
We now have seen enough of the client-side C++ mapping to develop a complete client to access our remote file system. For reference, here is the Slice definition once more:module Filesystem {
interface Node {
idempotent string name();
};
exception GenericError {
string reason;
};
sequence<string> Lines;
interface File extends Node {
idempotent Lines read();
idempotent void write(Lines text) throws GenericError;
};
sequence<Node*> NodeSeq;
interface Directory extends Node {
idempotent NodeSeq list();
};
};To exercise the file system, the client does a recursive listing of the file system, starting at the root directory. For each node in the file system, the client shows the name of the node and whether that node is a file or directory. If the node is a file, the client retrieves the contents of the file and prints them.#include <Ice/Ice.h>
#include <Filesystem.h>
#include <iostream>
#include <iterator>
using namespace std;
using namespace Filesystem;
static void
listRecursive(const DirectoryPrx& dir, int depth = 0)
{
// ...
}
int
main(int argc, char* argv[])
{
int status = 0;
Ice::CommunicatorPtr ic;
try {
// Create a communicator
//
ic = Ice::initialize(argc, argv);
// Create a proxy for the root directory
//
Ice::ObjectPrx base
= ic‑>stringToProxy("RootDir:default ‑p 10000");
if (!base)
throw "Could not create proxy";
// Down‑cast the proxy to a Directory proxy
//
DirectoryPrx rootDir = DirectoryPrx::checkedCast(base);
if (!rootDir)
throw "Invalid proxy";
// Recursively list the contents of the root directory
//
cout << "Contents of root directory:" << endl;
listRecursive(rootDir);
} catch (const Ice::Exception& ex) {
cerr << ex << endl;
status = 1;
} catch (const char* msg) {
cerr << msg << endl;
status = 1;
}
// Clean up
//
if (ic)
ic‑>destroy();
return status;
}1. Ice/Ice.hThis file is always included in both client and server source files. It provides definitions that are necessary for accessing the Ice run time.2. Filesystem.hThis is the header that is generated by the Slice compiler from the Slice definitions in Filesystem.ice.3. iostream4. iteratorThe implementation of listRecursive uses an STL iterator.3. The structure of the code in main follows what we saw in Chapter 3. After initializing the run time, the client creates a proxy to the root directory of the file system. For this example, we assume that the server runs on the local host and listens using the default protocol (TCP/IP) at port 10000. The object identity of the root directory is known to be RootDir.4. The client down-casts the proxy to DirectoryPrx and passes that proxy to listRecursive, which prints the contents of the file system.Most of the work happens in listRecursive:// Recursively print the contents of directory "dir" in
// tree fashion. For files, show the contents of each file.
// The "depth" parameter is the current nesting level
// (for indentation).
static void
listRecursive(const DirectoryPrx& dir, int depth = 0)
{
string indent(++depth, '\t');
NodeSeq contents = dir‑>list();
for (NodeSeq::const_iterator i = contents.begin();
i != contents.end();
++i) {
DirectoryPrx dir = DirectoryPrx::checkedCast(*i);
FilePrx file = FilePrx::uncheckedCast(*i);
cout << indent << (*i)‑>name()
<< (dir ? " (directory):" : " (file):") << endl;
if (dir) {
listRecursive(dir, depth);
} else {
Lines text = file‑>read();
for (Lines::const_iterator j = text.begin();
j != text.end();
++j) {
cout << indent << "\t" << *j << endl;
}
}
}
}The function is passed a proxy to a directory to list, and an indent level. (The indent level increments with each recursive call and allows the code to print the name of each node at an indent level that corresponds to the depth of the tree at that node.) listRecursive calls the list operation on the directory and iterates over the returned sequence of nodes:1. The code does a checkedCast to narrow the Node proxy to a Directory proxy, as well as an uncheckedCast to narrow the Node proxy to a File proxy. Exactly one of those casts will succeed, so there is no need to call checkedCast twice: if the Node is-a Directory, the code uses the DirectoryPrx returned by the checkedCast; if the checkedCast fails, we know that the Node is-a File and, therefore, an uncheckedCast is sufficient to get a FilePrx.In general, if you know that a down-cast to a specific type will succeed, it is preferable to use an uncheckedCast instead of a checkedCast because an uncheckedCast does not incur any network traffic.2. The code prints the name of the file or directory and then, depending on which cast succeeded, prints "(directory)" or "(file)" following the name.• If it is a file, the code calls the read operation on the file to retrieve the file contents and then iterates over the returned sequence of lines, printing each line.Figure 7.1. A small file system.Contents of root directory:
README (file):
This file system contains a collection of poetry.
Coleridge (directory):
Kubla_Khan (file):
In Xanadu did Kubla Khan
A stately pleasure‑dome decree:
Where Alph, the sacred river, ran
Through caverns measureless to man
Down to a sunless sea.• The client makes more remote procedure calls than strictly necessary; with minor redesign of the Slice definitions, many of these calls can be avoided.
|
|