Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Using OODCE


MAR96: Using OODCE

Jonathan is a senior software engineer with Compuware in Alameda, California, and can be reached at Jonathan_Roberts @compuware.com. Dan is a software engineer with Siren Software in Palo Alto, California, and can be contacted at djz@siren. com.


Distributed computing has been possible as long as computers have been on networks, but only recently have sophisticated tools become available to facilitate writing distributed apps in heterogeneous environments. The Open Software Foundation's Distributed Computing Environment (OSF/DCE) is one such environment, providing multithreading, remote procedure call (RPC), time synchronization, security, and uniform naming (for more information, see "Distributed Computing and the OSF/DCE," by John Bloomer, DDJ, February 1995).

DCE applications are typically complex. The DCE libraries contain over 500 functions and data types. Even seemingly trivial applications require nontrivial amounts of code just to cover the basics. In fact, the difficulty of writing real-world DCE applications by hand has long been seen as an obstacle to DCE's widespread acceptance. OODCE, a C++ library originally developed by Hewlett-Packard, greatly simplifies the development of distributed applications.

By encapsulating DCE functionality in C++ objects, OODCE accomplishes several things at once. First, it allows programmers to write DCE applications in C++ style. Secondly, it makes obvious the underlying DCE object model. In the DCE model, a server is responsible for exporting objects to the RPC run time, making them accessible to clients. Each exported object can be seen as an instance of a class defined by an IDL interface specification. For example, a server may export several Coke-machine objects, each of which acts independently. Although these two machines are exported by the same server, one machine may report being empty while another has plenty of Cokes to sell. This aspect of the interface is obscured in the standard DCE interface by the myriad of C APIs required to get anything done. OODCE clarifies this aspect of the DCE object model by expressing it in a set of C++ class hierarchies.

Finally, OODCE simplifies developing DCE applications by hiding details of key components with default behavior, allowing you to specialize the behavior when necessary. For instance, registering a DCE server with the RPC run time occupies two full pages in the OSF/DCE Application Development Guide. Using OODCE, we can get away with only two member-function calls to theServer because the DCEServer class provides reasonable default behavior regarding network protocols, authentication services, and the like. If we don't like that default behavior, we can override it in a subclass of DCEServer. Thus, we can put together a server in record time with the option of fine tuning it later. This aspect captures the spirit of OODCE.

Rather than explain in full detail every part of OODCE, we'll walk through the development of a simple application. This should demonstrate how little effort is required to get a prototype distributed system off the ground. We'll also discuss what else you can (and can't) do with OODCE 1.0.

Defining the Interface

One key feature of sophisticated distributed computing is interoperability. Sharing data on a homogenous network has been relatively simple for many architectures and operating systems for about 15 years on microcomputers and about 25 years on larger systems. Trying these machines together in a way that transcends both computer architecture and operating system is one of DCE's strengths.

The same principle holds true for DCE development systems. An OODCE server that could talk only to OODCE clients would be of limited utility. Similarly, an OODCE client that could not work with existing C-based servers would be shut out of many applications where legacy servers are in place (and working too well to be rewritten), but where new clients are needed.

For this reason, an OODCE application starts out with exactly the same sort of interface specification as a C-based DCE application, written in the DCE Interface Description Language (IDL). An IDL file describes a specific interface to a server class, including all its potential RPCs and all its data types.

In the C world, an IDL compiler ("idl") translates this file into three output files: two files of C code implementing a set of "stubs" for marshaling and unmarshaling parameters and return codes on both the client and server files, and a header file specifying the function signatures for which the server programmer must provide function implementations.

OODCE takes this a step further with its own IDL compiler, "idl++," which produces the three files of the standard idl compiler, plus four more files to implement the C++ interface. We will describe these files by way of example.

Our example application is a Coke-machine server that allows some clients to purchase Cokes from the machine, and allows others to add Cokes to the machine to keep it from running out of Coke. Listing One, an IDL file for this app, includes three basic RPCs: CokeCount, returning the number of Cokes available; BuyBeverages, allowing a customer to purchase some Cokes; and AddBeverages, allowing a maintainer to restock the machine. Listing Two, CokeMachine.h, is produced by both idl and idl++. Both programs also produce the basic "stub" files CokeMachine_cstub.c and CokeMachine_sstub.c, which actually implement the underlying RPCs. These files are too long (and tedious) to reproduce here. Together they comprise about 1200 lines of code that you'll never need to see.

The following files are produced only by idl++. Listing Three, CokeMachineC.H (and CokeMachineC.C, which we haven't shown), defines the client class CokeMachine_1_0. As you'll see when we write some client applications, this is all you need to use CokeMachine_1_0. For specialized semantics, such as always finding the Coke machine closest to your office, you can subclass from CokeMachine_1_0 and write your own constructor.

CokeMachineS.H (Listing Four) describes two server-side classes, CokeMachine_ 1_0_ABS and CokeMachine_1_0_MGR. These two classes are identical except that CokeMachine_1_0_ABS is an abstract class that declares our RPC functions pure virtual, while CokeMachine_1_0_MGR is a concrete class that declares them virtual and requires you to provide the actual code to define the server behavior. Using the MGR class is a good way to get your application off the ground quickly, but if your object needs state or non-RPC member functions, you will want to subclass from the abstract class and ignore the MGR class altogether. Listing Five (CokeMachineS.C) shows how we might have implemented the member functions of the CokeMachine_1_0_MGR class to get our server up and running in a hurry.

CokeMachineE.C implements a wrapper in C for each RPC we specify in the idl file. Each wrapper is responsible for dispatching the incoming RPC to the corresponding member function of the correct server-side object.

Writing the Server

Because our server is driven entirely by incoming RPCs, the actual server code is trivial; see Listing Six. It simply instantiates a CokeMachine_1_0_Mgr object, registers it with theServer, exports it, and listens for incoming requests. theServer, whose member functions are used in each of these steps, is a global object available to any program featuring the line #include <oodce/Server.H>. Only server-side code should use this class.

theServer is responsible for exporting OODCE objects to the RPC run time. You can export more than one object, and these objects can even be of different classes. For example, a single server can export three Coke machines, two bubble-gum machines, and a beer machine. Because many states have a law prohibiting the sale of beer to individuals under 21 years of age, we must assign proper access control to our beer machine, but we can ignore such restrictions for our other objects.

theServer can also export itself to CDS as an RPC entry, as a group element, or as a profile element. Additionally, it is responsible for selecting which network protocols to support, initiating and halting the listening to RPCs, and maintaining the program's login context. Keeping these functions within the DCEServer allows us to forget about them elsewhere in our code.

Writing the Client

Implementing the client side is even easier. There are no classes to define; we simply make member function calls on a CokeMachine_1_0 object. The default implementation of the client-side classes (here, CokeMachine_1_0) handles binding to existing server-side objects upon construction. It also handles all the RPCs implicitly, so that calls to the member function CokeMachine_1_0 on the client side transparently result in calls to the object CokeMachine_1_0_Mgr on the server side.

Listings Seven and Eight are two simple clients, customer and stockboy. The only difference between the two is that customer calls BuyBeverage while stockboy calls AddBeverage.

OODCE Benefits and Limitations

OODCE has many virtues beyond rapid prototyping. Not only is naming a server easy, but with a single constructor, cleaning up the CDS namespace can be made automatic upon server shutdown. The default method for locating objects exported by named servers is very simple: Just pass the name to your client object's constructor. A DCETracer class has a trace level that is very useful for tracking the activities of a server. The trace level, of course, can be changed as the server runs. We recommend that no server be passed off to testing or production without DCETracer.

Lastly, security has been made simple. This is one of OODCE's most-significant contributions, but the security classes deserve an article unto themselves.

While OODCE is clearly a step toward creating a reasonable distributed programming environment, there is still room for improvement. For instance, there

is no default behavior for pipes (a convenient way to move large amounts of data between clients and servers in DCE), which would help speed prototyping with them. The distribution of objects is asymmetric, unlike OSF's Request For Comments (RFC) 48.2. In this RFC, references to client-side objects can be passed to servers via RPC, making the client a client/server without requiring extra code. Do not allow RPC exceptions (which are C exceptions) to propagate beyond the scope of any automatic C++ variables, because the C exception mechanism will not destroy these objects, producing memory leaks. Using exceptions with multiple RPC threads is also problematic. This is significant because a server with only one RPC thread isn't much of a server. The OODCE notes indicate that this problem will be remedied in a future release.

Other limitations stem from the fact that OODCE is built using C++ rather than a more-sophisticated object-oriented language. Consider our technique for locking mutexes in C++. Example 1(a) is a naive way to do this. The problem with this sort of code is that if DoSomething() throws an exception, TheMutex will never be unlocked. OODCE provides a way around this using DCEPthreadLock; see Example 1(b). Here, both the locking and the unlocking are implicit and are handled by the constructors and destructors of DCEPthreadLock. When TheLock is created, TheMutex is locked; when TheLock is destroyed, TheMutex is unlocked. Because C++ exceptions are defined to call all destructors when they leave a given scope, our code is now exception safe.

But what if we want to lock TheMutex for only part of our function? Then we need to explicitly create a new scope, as in Example 2. This works, but it starts to look ugly. As our programming needs become more complicated, it's hard not to feel that we're coding around the language, inventing new variables and levels of scope in order to twist a few C++ rules in our favor.

C++ has tended to encourage this sort of coding along with the use of macros to clean things up. Example 3, for instance, is not generally considered good C++ style, although it does encapsulate the messy details of locking a mutex by introducing a new scope. Furthermore, because of C++'s relatively unsophisticated macro system, LOCK will break if we use it to lock multiple mutexes, and the name __TheLock may not be used elsewhere in the scope of our LOCK macro (variable capture). LOCK will also behave unexpectedly if your compiler doesn't support the new scoping rule for For loops. Our macro assumes that the scope of variables declared in the For does not extend beyond the body of the loop. Many compilers still make these variables visible in the surrounding scope after the For; in such compilers, we can use LOCK only once within a given scope without causing compile-time errors.

We could change the LOCK macro to allow us to supply the name of the dummy variable to solve both problems, but this defeats the purpose of trying to hide such details from the application programmer. We could also write macros to lock other numbers of mutexes (LOCK2, LOCK3, and so on) to avoid the nesting problem, but this only works if we always lock all the mutexes for exactly the same scope. For example, we still can't use the macros to lock one mutex for a while, then lock another, then unlock both, because such macros still won't nest correctly.

Other object-oriented languages, such as Common Lisp, have embraced rather than shunned macros, with the result that we can easily customize the language to better suit our needs. In Common Lisp, we can easily define macros that completely hide the details of mutex locking without introducing any of the problems caused by the limitations of the C preprocessor. In Example 4, the macro with-locks takes a list of mutexes and locks them in order, then executes the statements that follow. In this example, the function protected-task-one is only called after both mutex-one and mutex-two have been locked; protected-task-two is called after mutex-three has been locked as well. Whether all the statements are executed or the normal flow of control is interrupted through an error condition or a nonlocal exit, the mutexes are unlocked. In Common Lisp, it is impossible to tell whether with-locks is a macro added to the language, or one that is built in (like with-open-file). Listing Nine provides the complete code for the with-locks macro.

Is C++ Worth the Trouble?

So why use C++ at all for DCE applications? C++ has always been a sort of compromise between the high-level expressiveness of languages like Common Lisp and Smalltalk, and the efficiency of languages close to the iron, like C and assembly. It's clearly easier to use a language with automatic garbage collection (like Common Lisp and Smalltalk) than to code the memory management by hand (as in C and C++). But programmers are willing to manage their own memory to facilitate run-time efficiency.

In distributed computing, local run-time efficiency gains are often dwarfed by network communications, which typically consume far more time than memory management. Furthermore, in a DCE-based system, the RPC run time itself has considerable overhead, including a garbage-collection system. Given all this, why stick with C++?

In his book The C++ Programming Language, Bjarne Stroustrup gives four reasons why C++ remained close to the original C model, even when rejecting this heritage might have improved the language. The reasons are essentially cultural: the millions of lines of C code and C-based libraries (he counts this as two reasons), the hundreds of thousands of C programmers, and the need for C and C++ to be used side-by-side.

All four reasons apply to DCE applications at times, but other times it may make sense to break free of the C/C++ model entirely and use a more abstract and more expressive language. OODCE provides an excellent set of tools for making distributed computing about as easy as it could be in C++.

OODCE and DCE 1.2

The next major DCE release is billed as having built-in C++ support. It should be available in 1996 (although it took some vendors quite a while to move from DCE 1.0.2 to 1.1, so don't hold your breath). Although 1.2's C++ support shares the goal of facilitating DCE application development in C++, it is not compatible with OODCE.

The C++ component of DCE 1.2 is described in the Open Software Foundation's DCE RFC 48.2, prepared by DEC. Instead of providing a complete C++ class library for DCE programming, RFC 48.2 describes a set of extensions to idl itself for providing distributable objects. (Remember that OODCE goes out of its way not to change the input language of idl.) In doing so, it establishes the "hooks" that a class library (like OODCE) uses to provide complete C++ support for DCE. One very interesting feature of RFC 48.2, which is missing from OODCE, is object-location transparency, which allows use of objects without knowing whether they are local or only proxies representing nonlocal objects. While OODCE may be rewritten at some point to take advantage of this and other features, we know of no plans to do so.

Availability

As of this writing, OODCE is only available on HP-UX series machines (HP 9000) running HP's DCE/9000. There appears to be some interest in supporting OODCE on other platforms, but no specific ports have been announced. The Open Software Foundation is currently discussing with HP the possibility of making OODCE part of the OSF DCE source offering, which would help spread it throughout the DCE community.

Example 1: (a) The naive way to lock a mutex; (b) the preferred way in OODCE.

(a)
void Function()


{


  TheMutex.Lock();


  DoSomething();


  TheMutex.UnLock();


}




(b)
void Function


{


  DCEPthreadLock TheLock


     ( TheMutex );


  DoSomething();


}

Example 2: Locking a mutex for only part of a code block in OODCE.

void Function
{
  DoSomeInitialWork();
  {
    DCEMutexLock TheLock
           ( TheMutex );
    DoSomething();
  }
  DoSomethingElse();
}

Example 3: Hiding the details of mutex locking using macros.

#define LOCK( m ) for(DCEMutexLock
                  __TheLock( m );;)
void Function()
{  
  DoSomeInitialWork();
  LOCK( TheMutex ) {
    DoSomething();
  }
  DoSomethingElse();
}

Example 4: Hiding the details of mutex locking in Common Lisp.

(defun function()
  (initial-task)
  (with-locks (mutex-one mutex-two)
    (protected-task-one)
    (with-locks (mutex-three)
      (protected-task-two))
  (unprotected-task))

Listing One

/* Copyright (c) 1995 Avatar Software, Inc. */
[uuid( 75e76bbc-bc9d-11ce-902c-0800096d6656 ),
 version(1.0)]
interface CokeMachine
{
typedef long int CokeMachineStatus;
typedef long int BeverageType;
  unsigned long GetStatus( [in] handle_t   h );
  unsigned long BuyBeverages(
                             [in] handle_t   h,
                             [in] long       NumBeverages
                             );
  unsigned long AddBeverages(
                             [in] handle_t   h,
                             [in] long       NumBeverages
                             );
}

Listing Two

/* Generated by HP OODCE IDL++ compiler version 1.0 */
#ifndef CokeMachine_v1_0_included
#define CokeMachine_v1_0_included
#ifndef IDLBASE_H
#include <dce/idlbase.h>
#endif
#include <dce/rpc.h>
#ifdef __cplusplus
    extern "C" {
#endif
#ifndef nbase_v0_0_included
#include <dce/nbase.h>
#endif
extern idl_ulong_int CokeCount(
#ifdef IDL_PROTOTYPES
    /* [in] */ handle_t h
#endif
);
extern idl_ulong_int BuyBeverages(
#ifdef IDL_PROTOTYPES
    /* [in] */ handle_t h,
    /* [in] */ idl_ulong_int NumBeverages
#endif
);
extern idl_ulong_int AddBeverages(
#ifdef IDL_PROTOTYPES
    /* [in] */ handle_t h,
    /* [in] */ idl_ulong_int NumBeverages
#endif
);
typedef struct CokeMachine_v1_0_epv_t {
idl_ulong_int (*CokeCount)(
#ifdef IDL_PROTOTYPES
    /* [in] */ handle_t h
#endif
);
idl_ulong_int (*BuyBeverages)(
#ifdef IDL_PROTOTYPES
    /* [in] */ handle_t h,
    /* [in] */ idl_ulong_int NumBeverages
#endif
);
idl_ulong_int (*AddBeverages)(
#ifdef IDL_PROTOTYPES
    /* [in] */ handle_t h,
    /* [in] */ idl_ulong_int NumBeverages
#endif
);
} CokeMachine_v1_0_epv_t;
typedef struct CokeMachine_v1_0_m_epv_t {
idl_ulong_int (*CokeCount)(
#ifdef IDL_PROTOTYPES
    /* [in] */ handle_t h,
    /* [out] */ error_status_t *_dcecxxsts
#endif
);
idl_ulong_int (*BuyBeverages)(
#ifdef IDL_PROTOTYPES
    /* [in] */ handle_t h,
    /* [in] */ idl_ulong_int NumBeverages,
    /* [out] */ error_status_t *_dcecxxsts
#endif
);
idl_ulong_int (*AddBeverages)(
#ifdef IDL_PROTOTYPES
    /* [in] */ handle_t h,
    /* [in] */ idl_ulong_int NumBeverages,
    /* [out] */ error_status_t *_dcecxxsts
#endif
);
} CokeMachine_v1_0_m_epv_t;
extern CokeMachine_v1_0_epv_t CokeMachine_v1_0_c_epv;
extern rpc_if_handle_t CokeMachine_v1_0_c_ifspec;
extern rpc_if_handle_t CokeMachine_v1_0_s_ifspec;
#ifdef __cplusplus
    }
#endif
#endif

Listing Three

/* Generated by HP OODCE IDL++ compiler version 1.0 */
#ifndef __CokeMachine_1_0_Class_Included______LINEEND____
#define __CokeMachine_1_0_Class_Included______LINEEND____
#include <oodce/Interface.H>
#include <CokeMachine.h>
extern   rpc_if_handle_t CokeMachine_v1_0_c_ifspec;
class CokeMachine_1_0: public DCEInterface {
public:
    // Define Class Constructors
    CokeMachine_1_0(DCEUuid& to = NullUuid):
    DCEInterface(CokeMachine_v1_0_c_ifspec, to) {}
    CokeMachine_1_0(rpc_binding_handle_t bh, DCEUuid& to = NullUuid):
    DCEInterface(CokeMachine_v1_0_c_ifspec, bh, to) {}
    CokeMachine_1_0(rpc_binding_vector_t* bvec, DCEUuid& to = NullUuid):
    DCEInterface(CokeMachine_v1_0_c_ifspec, bvec, to) {}
    CokeMachine_1_0(DCENsiObject* nsi_obj, DCEUuid& to = NullUuid):
    DCEInterface(CokeMachine_v1_0_c_ifspec, nsi_obj, to) {}
    CokeMachine_1_0(unsigned char* name,
        unsigned32 syntax = rpc_c_ns_syntax_default,
        DCEUuid& to = NullUuid):
    DCEInterface(CokeMachine_v1_0_c_ifspec, name, syntax, to) {}
    CokeMachine_1_0(unsigned char* netaddr,
        unsigned char* protseq, DCEUuid& to = NullUuid):
    DCEInterface(CokeMachine_v1_0_c_ifspec, netaddr, protseq, to) {}
    CokeMachine_1_0(DCEObjRefT* ref):
    DCEInterface(CokeMachine_v1_0_c_ifspec, ref) {}
    // Member functions for client
    idl_ulong_int CokeCount(
    );
    idl_ulong_int BuyBeverages(
        /* [in] */ idl_ulong_int NumBeverages
    );
    idl_ulong_int AddBeverages(
        /* [in] */ idl_ulong_int NumBeverages
    );
};
#endif

Listing Four

 /* Generated by HP OODCE IDL++ compiler version 1.0 */
#ifndef __CokeMachine_1_0_Mgr_Class_Included______LINEEND____
#define __CokeMachine_1_0_Mgr_Class_Included______LINEEND____
#include <oodce/InterfaceMgr.H>
#include <oodce/DCEObj.H>
#include <CokeMachine.h>
extern  CokeMachine_v1_0_m_epv_t        CokeMachine_v1_0_mgr;
extern  rpc_if_handle_t         CokeMachine_v1_0_s_ifspec;
class CokeMachine_1_0_ABS : public virtual DCEObj, public DCEInterfaceMgr {
private:
        CokeMachine_1_0_ABS(DCEObj& obj, uuid_t* type):
        DCEObj(obj.GetId()),
        DCEInterfaceMgr(CokeMachine_v1_0_s_ifspec, obj, type,
        (rpc_mgr_epv_t)(&CokeMachine_v1_0_mgr)) {}
public:
        // Declare Class Constructors
        CokeMachine_1_0_ABS(uuid_t* obj, uuid_t* type):
        DCEObj(obj),
        DCEInterfaceMgr(CokeMachine_v1_0_s_ifspec, (DCEObj&)*this, type,
        (rpc_mgr_epv_t)(&CokeMachine_v1_0_mgr)) {}
        CokeMachine_1_0_ABS(uuid_t* type):
        DCEObj((uuid_t*)(0)),
        DCEInterfaceMgr(CokeMachine_v1_0_s_ifspec, (DCEObj&)*this, type,
        (rpc_mgr_epv_t)(&CokeMachine_v1_0_mgr)) {}
        // Declare Class pure virtual member functions
        // These correspond to the remote procedures
        // declared in CokeMachine.idl
        // These need to be implemented by the developer
        virtual         idl_ulong_int CokeCount(
        ) = 0;
        virtual         idl_ulong_int BuyBeverages(
                /* [in] */ idl_ulong_int NumBeverages
        ) = 0;
        virtual         idl_ulong_int AddBeverages(
                /* [in] */ idl_ulong_int NumBeverages
        ) = 0;
};
class CokeMachine_1_0_Mgr : public CokeMachine_1_0_ABS {
public:
        // Declare Class Constructors
        CokeMachine_1_0_Mgr(uuid_t* obj):
        DCEObj(obj),
        CokeMachine_1_0_ABS(obj, (uuid_t*)(0)) {}
        CokeMachine_1_0_Mgr():
        DCEObj((uuid_t*)(0)),
        CokeMachine_1_0_ABS((uuid_t*)(0)) {}
        // Declare Class member functions. These correspond to the remote 
        // procedures declared in CokeMachine.idl. These need to be 
        // implemented by the developer
        virtual         idl_ulong_int CokeCount(
        );
        virtual         idl_ulong_int BuyBeverages(
                /* [in] */ idl_ulong_int NumBeverages
        );
        virtual         idl_ulong_int AddBeverages(
                /* [in] */ idl_ulong_int NumBeverages
        );
};
#endif

Listing Five

// Copyright (c) 1995 Avatar Software, Inc.
#include <iostream.h>
#include "CokeMachineS.H"
unsigned long CokeMachine_1_0_Mgr::CokeCount( )
{
  // lock access to this coke machine
  DCEPthreadLock ContentLock( &ContentMutex );
  return NumItems;
}
unsigned long CokeMachine_1_0_Mgr::BuyBeverages( idl_ulong_int NumBeverages )
{
  // lock access to this coke machine
  DCEPthreadLock ContentLock( &ContentMutex );
  // Buy as many beverages as possible up to NumBeverages
  // and set NumBeverages to 
  unsigned long ItemsBought = NumBeverages;
  if( ItemsBought > NumItems )
    ItemsBought = NumItems;
  NumItems -= ItemsBought;
  return ItemsBought;
}
unsigned long CokeMachine_1_0_Mgr::AddBeverages( idl_ulong_int NumBeverages )
{
  // lock access to RepContents
  DCEPthreadLock ContentLock( &ContentMutex );
  NumItems += NumBeverages;
}

Listing Six

// Copyright (c) 1995 Avatar Software, Inc.
#include <iostream.h>
#include "CokeMachineS.H"
#include <oodce/Server.H>
void main( int argc, char *argv[] )
{
  try {
    DCEPthread* TheCleanupThread = new DCEPthread(DCEServer::ServerCleanup,
                                                  (void *)(0));
    CokeMachine_1_0_Mgr TheCokeMachine;
    // Register CokeMachine object with the server object
    theServer->RegisterObject(TheCokeMachine, true);
   
    cerr << "Listening..." << endl;
    theServer->Listen();                
  }
  catch (DCEErr& exc)  {
    cerr << "Caught DCE Exception\n" << (const char*)exc;
  }
  exit(0);
}

Listing Seven

// Copyright (c) 1995 Avatar Software, Inc.
#include <stdlib.h>
#include <iostream.h>
#include "CokeMachineC.H"
#include <oodce/Exceptions.H>
int main( int argc, char * argv[] )
{
  try {
    CokeMachine_1_0 TheCokeMachine((unsigned char*) argv[1],
                                           (unsigned char *) "ip");
    TheCokeMachine.BuyBeveragres(atoi(argv[1]));
  }
  catch (DCEErr& exc) {
    cerr << "Caught DCE Exception: " << (const char *) exc << endl;
    exit(1);
  }
  exit(0);
}

Listing Eight

// Copyright (c) 1995 Avatar Software, Inc.
#include <stdlib.h>
#include <iostream.h>
#include "CokeMachineC.H"
#include <oodce/Exceptions.H>
int main( int argc, char * argv[] )
{
  try {
    CokeMachine_1_0 TheCokeMachine((unsigned char*) argv[1],
                                      (unsigned char *) "ip");
    TheCokeMachine.AddBeveragres(atoi(argv[1]));
  }
  catch (DCEErr& exc) {
    cerr << "Caught DCE Exception: " << (const char *) exc << endl;
    exit(1);
  }
  exit(0);
}

Listing Nine


;;; with-locks.lisp
;;; Copyright (c) 1995 Avatar Software, Inc.
;; The WITH-LOCKS macros takes pains to do two things.  First, it
;; ensures that the macro is completely expanded at compile time, to
;; speed up code.  Second, it ensures that only mutexes which have
;; been locked (through a successful call to LOCK) get unlocked (by
;; calling UNLOCK).  In other words, if (LOCK THE-MUTEX) itself causes
;; a non-local exit (due to an error, for example), then we do not
;; want (UNLOCK THE-MUTEX) to be called.  This is accomplished through
;; a fairly thick wrapping of UNWIND-PROTECTs and PROGNs.
(defmacro with-locks (mutex-list &body body)
  "Evaluate the forms in BODY with the mutexes in MUTEX-LIST locked."
  (labels ((expand-with-locks (mutex-list body)
         (if (null mutex-list)
         `(progn ,@body)
         `(progn (lock ,(car mutex-list))
                 (unwind-protect
                     ,(expand-with-locks (cdr mutex-list) body))
                   (unlock ,(car mutex-list))))))
          (expand-with-locks mutex-list body)))
DDJ


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.