Examining the Systems<ToolKit> Library

Systems<ToolKit> is a library that builds on the expressive foundation of the C++ Standard Library. It also adds iostream support to distributed C++ applications using sockets (and pipes, in the UNIX version) for IPC connections. Mike uses Systems<ToolKit> to develop a pair of distributed client/server applications for environments such as the Internet.


November 01, 1996
URL:http://www.drdobbs.com/cpp/examining-the-systemstoolkit-library/184409999

November 1996: Examining the Systems <ToolKit> Library

Examining the Systems<ToolKit> Library

Extending the C++ Standard Library

Michael J. Vilot

Michael is a software design consultant specializing in object-oriented design and C++. He can be contacted at http:// www.objects.destek.com/.


This is an interesting year for software developers using C++. After international public review and discussion, the ANSI and ISO C++ Committees are revising their first official document: the Committee Draft (CD). At the same time, C++ compiler and class-library vendors are releasing tools with features defined in that proposed definition of Standard C++.

In this article, I'll examine one such library-ObjectSpace's Systems<ToolKit>. As a relative newcomer to the C++ class-library market, ObjectSpace has the daunting task of making inroads against established vendors such as Rogue Wave. However, the compelling mix of important features and effective design in this tool may make it an ideal candidate for achieving such a goal. Here, I'll examine why Systems<ToolKit> deserves consideration as an essential element in every serious C++ programmer's library. To illustrate Systems<ToolKit>'s use, I'll develop a pair of distributed client/server applications for environments such as the Internet. These examples were tested with Microsoft Visual C++ and Sun C++ on the Windows 95 and Solaris UNIX platforms, respectively. (Systems<ToolKit> is available for Sun, HP, IBM, SGI UNIX, and Windows 95/NT.)

Library Context and Overview

Modern software toolkits typically have three major components:

Figure 1 is an OOD-class category diagram (see Grady Booch's Object-Oriented Analysis and Design, Benjamin/Cummings, 1994) that illustrates the essential structure of this design. This design uses a pattern Booch and I introduced in the article "Component Library Design Considerations" (C++ Report, July/August 1992). Although the details vary with each application, this pattern applies across a wide spectrum of software developed today. Each of the four categories has a specific role in the design:

Systems<ToolKit> focuses on the two lower-level categories, providing both System Access and Foundation components. A key benefit of its System Access components is the encapsulation of platform-specific details, such as the particulars of multithreading or network access. Its main value as a Foundation component is in providing efficient and reliable templates, classes, and functions that eliminate the need to handcraft low-level data structures and algorithms.

A consistent interface across platforms is especially useful for those developing distributed applications. Figure 2 illustrates a typical design for a two-tier client/server system. In this design, the client applications typically run on a PC, using Windows for both User Interface and System components. C++ developers often use a large class library (such as MFC) to help cope with the inherent complexities of these components. However, MFC is highly GUI oriented, specific to the Windows environment, and does not translate well to other platforms. (The cross-platform version of MFC, Visual C++ for the Macintosh, incurs significant performance penalties. These usually are severe enough to preclude this approach as a viable option for deploying commercial-quality applications, Word 6 for Macintosh notwithstanding.)

Server applications, on the other hand, typically run on a UNIX system. Their focus is not on a GUI, but on high-performance access to DBMS products such as those from Oracle and Sybase. Their user interface is usually the program-to-program connection established across the network, using a communication protocol such as TCP/IP (see UNIX Network Programming, by W.R. Stevens, Prentice Hall, 1990).

In this context, Systems<ToolKit> is especially useful. Figure 3 illustrates the major class categories in the product (the fourth category, UNIX Components, is not part of the Windows version).

Library Contents

Systems<ToolKit>'s Foundation components (Figure 1) are in the Standard Library and Systems Components categories, plus the Time & Date components from the Infrastructure category. As with ObjectSpace's STL<ToolKit> tool, Systems<ToolKit> provides a reasonable (though incomplete) approximation of the forthcoming C++ Standard Library (see my article "The C++ Standard Library," DDJ, August 1995).

The library provides enhanced support for both the STL and String components in its Standard Library category. For STL, these take the form of helper algorithms, as described by ObjectSpace's Graham Glass in his article "STL in Action: Helper Algorithms" (C++ Report, January 1996). For Strings, these take the form of substring and tokenizer classes. The Systems<ToolKit> string tokenizer class splits a given string into a vector of strings, based on a set of separator characters, in a kind of batch operation. This is not quite as flexible as the StringTokenizer in Java's java.util library, which takes a more incremental approach and is better integrated with input streams.

The Error Events and Portable RTTI components from the Systems Components category are primarily historical scaffolding for C++ compilers that did not support exceptions or run-time type information. The RTTI components are the foundation for the library's object-persistence mechanism. It is similar to other portable schemes; see, for example, "Roll Your Own Persistence Implementations to Go Beyond the MFC Frontier," by Allen Holub (Microsoft Systems Journal, June 1996).

The System Access components

(Figure 1) are in the remaining Infrastructure and UNIX Components categories. The Threads, Sockets, and Synchronization components are most interesting, especially since they're available across platforms. The Threads and Synchronization components provide a modern version of multithreading (a much-improvised enhancement to C++, tracing back to the very first C++ class library; see "A Set of C Classes for Co-routine Style Programming," by Bjarne Stroustrup, Bell Laboratories, Computer Science Technical Report CSTR-90, November 1980). The Sockets (and Pipes, in the UNIX version) components provide the key interprocess communication (IPC) services essential to a distributed application.

As with the STL and Strings components, Systems<ToolKit> provides extensions beyond the basic low-level IPC capabilities. For example, the Synchronization components provide a basic mutex_semaphore class to encapsulate the operating system's cross-thread locking mechanism. The library also provides read_write_semaphore and monitor classes, supporting better throughput in designs that have more readers than writers. And it provides semaphore lock classes that seize a semaphore in their constructor and release it in their destructor. These lock components are indispensable for C++ programs that use both multithreading and exceptions (see "Simplifying the Booch Components," by Michael Vilot and Grady Booch, C++ Report, June 1993).

Similarly, the Sockets components offer extensions beyond the low-level ip_address, tcp_socket, and udp_socket classes. The tcp_connection_server and io_multiplexer classes simplify many of the more tedious aspects of server design. While the basic socket classes support the common operations of connecting, reading, and writing, the tcp_connection_server adds the listen() and accept() operations. This clarifies the differences in how clients and servers use their socket connections, reducing the opportunity for errors due to incorrect use of the socket. The io_multiplexer class provides an interface for servers that listen and respond to multiple input ports (see "Connector: A Design Pattern for Actively Initializing Network Services," by D.C. Schmidt, C++ Report, January 1996).

The following examples illustrate two uses of the Systems<ToolKit> components. The first example uses standard strings, containers, iterators, and iostreams to implement a typical filter (a program that transforms an input stream into a differently formatted output stream). The second illustrates the client side of a typical (albeit simple) Internet application: calling upon a server for information and displaying the result.

Examining Web Server-Access Logs

Programs are often written to extract useful information from an otherwise uninteresting mass of data, such as information buried in server log files. For instance, someone designing Web pages may want to know: What kinds of browsers are viewing this site?

A quick look at a typical httpd server-access log reveals that it contains some interesting data, but not in usable form. Listing One illustrates a typical entry in such a log. It is actually a record with nine fields, most of which are not relevant to answering our question.

There are many such entries for each browser/server session, one for each request issued from the browser to the server. The fifth field, a quoted string, contains the command (GET, for example) and its arguments. The final field, also a quoted string, contains the essential browser information. Just for fun, add one other piece of information-which sites (the first field) are using each kind of browser.

Listing Two presents the core of this Application (recall Figure 1): the three functions input(), parse(), and output(). I'll ignore the User Interface for this example, although the code has been incorporated, unchanged, into both UNIX command-line and Windows MFC programs. The two functions input() and output() are complementary halves of this filter program. The input() function reads each line of the server log file, and stores the results in a map of browsers and sites. The output() function walks through the map, formatting and displaying its contents.

The parse() function handles the tedious details of picking apart the fields in the record. In this example, it uses the Standard istream members get() and getline() to tokenize the incoming stream. What makes this task somewhat complex is the fact that the delimiters change: first whitespace, then square brackets, then quotes. The auxiliary function quoted_string() handles skipping the unwanted fields.

Listing Three shows sample output from this filter. The browsers are listed alphabetically. (Most of the entries are for "Mozilla," the Netscape browser, differing in their version and platform information.) For each browser, you see the site(s) using it.

This example illustrates an interesting characteristic shared by the C++ Standard Library map and set components-they store their key values only once, sort them, and ignore any duplicate items added. Thus, the filter program contains only one entry for each browser/site combination, no matter how many times the server log records commands from it.

Client to an Internet Server

The next example illustrates an important class of programs-distributed applications using the Internet as their network communications medium. I'll focus on the client side of an application, using an existing server commonly found on the Internet.

The Network News Transfer Protocol (NNTP) server is the backbone of the ubiquitous Usenet newsgroups. It performs a fundamentally simple task: adding, storing, and presenting text files containing news articles. It also imposes a tree structure to newsgroups, primarily through a naming convention roughly similar to the current Internet domain naming scheme. (For example: clari.biz.industry and clari.biz.industry.transportation are newsgroups in the ClariNet business industry news category, while comp.lang.c++ and comp.std.c++ are in the computer systems category.)

NNTP servers throughout the Internet interact with two different kinds of clients: newsreaders and other news servers. The interaction with an NNTP server is simple, consisting of text-based commands and responses. Listing Four illustrates one such interaction, in which a client logs in to the server to get a list of active newsgroups. The server responds to the initial login, and to each command, with a status code and a description. Status codes in the range of 200-299 denote success. (Web servers operate similarly, using the Hypertext Transfer Protocol, HTTP.) This information is all you need to build a very simple, single-purpose NNTP client. Listing Five presents a small command-line program that connects to an NNTP server, requests the active list, and displays the result (it also counts the number of active groups). This example illustrates an important feature of the Systems<ToolKit> streams components. They are designed as extensions of the Standard iostreams classes, and provide simple adapters for sockets (and pipes, in the UNIX version). Thus, C++ code written to read and write information using iostreams can accommodate IPC communication with ease.

The main() function does the essential housekeeping, setting up an iostream to (and from) the server. The open() function establishes the dialog with the server, and issues the request to have it list its active newsgroups. The input() function gathers up the resulting list of group names (discarding the other information). The program uses a priority_queue to keep the names sorted in alphabetical order, since the NNTP server can return them in any order it finds convenient. Finally, the output() function displays the number and names of the newsgroups received. Listing Six shows a portion of the resulting output-over 7000 active groups!

These two example programs illustrate some of the most common tasks performed in C++ programs-handling string text to and from files in secondary storage. The Systems<ToolKit> library provides components that simplify these tasks and extends them to cover distributed IPC.

Conclusion

Systems<ToolKit> is an important library. It builds on the expressive foundation of the C++ Standard Library, with the data structures and algorithms from STL. It also extends those libraries in an important direction, adding iostream support to distributed C++ applications using Sockets (and Pipes, in the UNIX version) for IPC connections.

Perhaps the most important strength of this library is its design. Recent ads from ObjectSpace feature a quotation from Alexander Stepanov, the driving force for the design and implementation of the STL library: "ObjectSpace has done a remarkable job of significantly extending the STL framework into new areas such as network programming and persistence." The examples presented in this article support this observation. Their use of standard containers, iterators, strings, and iostreams integrates cleanly with the Sockets components. The library's threads, synchronization, and persistence mechanisms (not explored in this article) are similarly effective.

This library is not, however, a panacea. Nor is it the last C++ library you'll ever need. The library's components, while simpler and easier to use than a raw C interface, are still rather low level. This is appropriate, given their systems-programming domain. However, this leaves much work to be done in a distributed C++ application-especially for those developers familiar with remote procedure calls and other, more abstract, conveniences.

The library is also no substitute for a full implementation of the ANSI/ISO C++ Standard Library. Systems<ToolKit> and STL<ToolKit> both overlook some key utility components (such as auto_ptr, see "Coping With Exceptions," by J.W. Reeves, C++ Report, March 1996), and omit the complex number classes, overloaded math functions, valarray, and locale components entirely.

Systems<ToolKit> can be used today to gain experience with the key elements of the C++ Standard Library of tomorrow. And it can be used to develop the foundations for client/server applications using TCP/IP connections on the Internet, certainly a significant number of the C++ programs written today.

Figure 1: Class categories of a typical C++ application.

Figure 2: Typical distributed application.

Figure 3: Systems<ToolKit> class categories.

For More Information

ObjectSpace, Inc.

14881 Quorum Drive, Suite 400

Dallas, TX 75240

214-934-2496

http://www.objectspace.com/

Listing One

rionb04p30.unisys.com.br - - [22/Mar/1996:18:12:46 -0500] "GET
/ooad.html HTTP/1.0" 200 2006 "http://www.objects.destek.com/"
"Mozilla/1.22 (Windows; I; 16bit)"

Listing Two
#include <fstream.h>
#include <iomanip.h>

#include <ospace/string.h>  // should be <string>
#include <map.h>            // for map<>, pair<>, make_pair()
#include <set.h>

typedef pair<string, string> Pairtype;
typedef set<string, less<string> > Settype;
typedef map<string, Settype, less<string> > Maptype;

static Maptype m;   // shared by input() and output()

static Pairtype parse(istream& file, unsigned long& line);
static string empty;    // returned as "browser" @ eof

unsigned long input(istream& file) throw(unsigned long)
{
  unsigned long line = 1;
  while (file && !file.eof()) {
    Pairtype p = parse(file,line);
    string& browser = p.first;
    string& site    = p.second;

    if (browser != empty) {
      Settype& set = m[browser];
      set.insert(site);
    }
  }
  if ( !file.eof() ) throw line;
  return line;
}
static void quoted_string(istream&, unsigned long);
static char buffer[512];  // shared
static Pairtype parse(istream& file, unsigned long& line)
{
  string site;
  file >> site; // 1st field
  if (file.eof()) return make_pair(empty,site);
// ignore through date (4th field)
  file.getline(buffer, sizeof buffer, ']');
// ignore command (5th field)
  quoted_string(file, line);
  // ignore 6th & 7th fields
  int n1;
  file >> n1 >> buffer;
// ignore referrer URL (8th field)
  quoted_string(file, line);
  // get browser info (9th/last field)   file.getline(buffer, sizeof buffer);
  //++line;

  string browser(buffer);
  return make_pair(browser, site);
}
static void quoted_string(istream& file, unsigned long line)
{
  char x = ' ';
  file >> ws; // skip whitespace
  file.get(x);  if (x != '"') throw line;
  file.get(buffer, sizeof buffer, '"');
  file.get(x);  if (x != '"') throw line;
}
void output(ostream& out)
{
  out << "Browsers (and their sites): " << endl;
  out.flags( out.flags() | ios::left );
  Maptype::iterator it;
  for (it = m.begin(); it != m.end(); ++it) {
    out << (*it).first << endl;
    Settype& set = (*it).second;
    Settype::iterator i2;
    for (i2 = set.begin(); i2 != set.end(); ++i2) {
      out << '\t' << *i2 << endl;
    }
  }
}

Listing Three
"Infoseek Robot 1.17"
        homer-bbn.infoseek.com
        homer.infoseek.com
"Lycos Spider (Rex)/TEST v1.0 libwww/3.1"
        fourteen.srv.lycos.com
        qe20.lycos.com
"Mozilla/1.1 (Windows; U; 32bit)"
        mail.jabil.com
"Mozilla/1.1N (Macintosh; I; PPC)"
        box772.labs.cis.pitt.edu
        gate.cs.auckland.ac.nz
 ...
"Mozilla/2.01 (X11; I; OSF1 V3.2 alpha)"
        picasso.idt-isep.ipp.pt
"Mozilla/2.0GoldB1 (Win95; I)"
        206.15.181.148
        ad05-103.compuserve.com
        barredu207.k12.vt.us
        dd35-188.compuserve.com
        fairisaac.com
        hd63-109.compuserve.com
 ...

Listing Four
(Connect to site on "well known" port # 119, using TCP/IP) 200 quartz.mv.com NNTP[auth] server version 1.5.12.1 ready (posting ok).
list active
215 Newsgroups in form "group high low flags".
 ...
 .  (final output line)
 quit

Listing Five
#include <deque.h>
#include <queue.h>          // for priority_queue
#include <ospace/string.h>  // should be simply <string>

#include <ospace/socket.h>
#include <ospace/stream/tstream.h>
#include <ospace/event/event.h> // for os_event

typedef priority_queue< deque< string >, greater< string > > Qtype;

static void open(iostream&) throw(const char*);
static void input(iostream&, Qtype&);
static void output(Qtype&);

int main(int argc, char *argv[])  // list active newsgroups
{
  try {
  // Attach a socket to the NNTP server (well-known port #119)
    const char* name = (argc != 2) ? "news.mv.com" : argv[1];
    os_socket_address nntp_server( os_ip_address(name), 119 );

  // Create an input stream attached to the socket
    os_tcp_socket socket;
    socket.connect_to( nntp_server );
    os_tstream stream( socket );
    open(stream);   // Check login, send list command

  // Create a priority_queue to sort the names
    Qtype queue;
    input(stream, queue);   // Gather names
    stream << "quit" << endl;   // Close connection
    output(queue);  // Display the results

  } catch(os_event& ev) {
    cerr << "ObjectSpace exception: " << ev << endl;
    return 1;
  } catch(const char* msg) {
    cerr << "NNTP Server Error: " << msg << endl;
    return 1;
  }
  return 0;
}
static void open(iostream& stream) throw(const char*)
{
  // Read login information
  static char buffer[256];   stream.getline( buffer, sizeof buffer );
  if ( buffer[0] != '2' ) throw "NNTP not found on port";

  // Send request to list active newsgroups
  stream << "list active" << endl;

  // Check status (200+)
  stream.getline( buffer, sizeof buffer );
  if ( buffer[0] != '2' ) throw "list command failed";
}
static void input(iostream& stream, Qtype& q)
{
  unsigned long count = 0;
  while ( !stream.eof() ) {
    string s;
    stream >> s;  // read 1st string (newsgroup)

    // Check for end of listing
    if (s[0] == '.') break;

    q.push(s);  // save the result

    // Skip past remainder of line
    static char buffer[256];
    stream.getline( buffer, sizeof buffer );

    // Note progress
    ++count;
    if ( (count % 100) == 0 ) cout << "Read " << count << " so far." << endl;
  }
}
static void output(Qtype& q)
{
  cout << "========== " << q.size() << " ACTIVE NEWSGROUPS ==========" << endl;
  while (q.size()) {
    cout << q.top() << endl;
    q.pop();
  }
  cout << "=============================================" << endl;
}

Listing Six
(connect_to news.mv.com)
Read 100 so far.
 ...
Read 7300 so far.
Send:  quit
========== 7394 ACTIVE NEWSGROUPS ==========
alt.0d
alt.1d
alt.2600
alt.2600.hope.announce
alt.2600.hope.d
alt.2600.hope.tech alt.2600hz
alt.3d
 ...
t.unix
vt.uucp
vt.vtuug
wash.general
wash.politics
wash.test
wi.forsale
wpi.clubs.hsa



Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.