Defining the dispatcher

The dispatcher acts as an intermediary between the caller interface and the dispatcher implementation. Dispatchers contain stub code that is responsible for:

  1. Reading requests from a transport
  2. Unmarshalling arguments
  3. Passing arguments to the appropriate dispatcher implementation
  4. Marshalling results and exceptions that are sent to the caller
  5. Streaming parameters back to the caller
The dispatcher inherits the member functions that accomplish these steps from
MRemoteDispatcher. Your dispatcher class must derive from MRemoteDispatcher, directly or indirectly.

While a dispatcher is similar to a caller in that its member functions act as stubs to the remote dispatcher implementations, dispatcher classes do not inherit from the abstract base class that defines the interface of the remote calls. This is because the dispatcher stubs must accept specific arguments that do not match the arguments of the remote call interface.

To define a dispatcher you:

  1. Write the dispatcher class interface as described on page 86.
  2. Define a dispatcher constructor as shown on page 88.
  3. Write the dispatcher stubs as described on page 89.

Writing the dispatcher class interface


In the declaration for your dispatcher class:

  1. Declare a constructor that accepts the dispatcher implementation class pointer as a parameter.
    Using this constructor ensures that you always have an implementation instance for the dispatcher.
  2. Declare a unique numeric identifier for each member function of the dispatcher implementation object that the dispatcher calls.
    The only restriction on the identifier values is that they must be unique within a single caller-dispatcher pair. The values do not have to be contiguous, but dense assignment is better than sparse assignment, as it uses less memory in the dispatcher's task. While the enumerator is public, only the dispatcher class uses the identifiers.
  3. Declare a protected constructor that accepts no parameters.
  4. Declare a dispatcher stub member function for each function implementation you want it to call.
    Each stub accepts two parameters: an argument stream and a result stream.
  5. Declare a private data member that points to the dispatcher implementation class.
TAdditionDispatcher illustrates these steps:

      class TAdditionDispatcher : public MRemoteDispatcher 
      {
        public:
          TaligentTypeExtensionDeclarationsMacro(TAdditionDispatcher)
        public:
                          
    TAdditionDispatcher(TAdditionImplementation* adoptedImplementation);
          virtual         ~TAdditionDispatcher();
                          
          
    enumEAdditionRequest {
                      kAdd,
                      kAddOne,
                      kLastRequest = kAddOne };
                      
        protected:
                              
    TAdditionDispatcher();
          void                
    AddStub(TStream& argStream, TStream& resultStream);
          void                AddOneStub(TStream& argStream, TStream& resultStream);
                          
        private:
                              TAdditionDispatcher(const TAdditionDispatcher& source) {}
          TAdditionDispatcher&operator=(const TAdditionDispatcher& source) {return *this;}
          virtual TStream&    operator>>=(TStream& toStream) const {return toStream;}
          virtual TStream&    operator<<=(TStream& fromStream) {return fromStream;}
      
          enum {kOriginalVersion};
          
    TAdditionImplementation*fImplementation;
      };

Writing the dispatcher class constructor

The public constructor for the dispatcher (which takes a pointer to the implementation instance) initializes and registers the request identifiers that correspond to the dispatcher stubs. The constructor establishes the relationship between each member function identifier and its corresponding dispatcher stub function. This ensures that the dispatcher can properly match incoming requests to dispatcher stubs.

In the dispatcher class constructor:

  1. Call the void constructor for MRemoteDispatcher.
  2. Adopt the implementation instance.
  3. Initialize the list of remote requests by defining a RequestEntry struct (declared in MRemoteDispatcher) with one entry for each remote member function.
    For each entry, the struct specifies:
    1. The enum that serves as the member function identifier (as defined in the class interface)
    2. A function pointer to the corresponding dispatcher stub
  4. Register the requests using MRemoteDispatcher::RegisterRequests, passing the following as arguments:
    1. The name of the dispatcher class, as a TStandardText instance
      This name must match the name you pass to the MRemoteCallerDeclarationsMacro in each caller that references this dispatcher.
    2. The maximum value from the enumerated list that defines the member function identifiers
    3. The RequestEntry struct you just initialized
TAdditionDispatcher defines the RequestEntry struct for its two stub functions:

      TAdditionDispatcher::TAdditionDispatcher(TAdditionImplementation* adoptedImplementation)
          : MRemoteDispatcher(),
          fImplementation(adoptedImplementation)
      {
          ::Assertion(adoptedImplementation != NIL, "\nCannot adopt a NIL implementation!\n");
          
    static RequestEntry requests[] = {
              {kAdd, (RemoteFnPtr)&TAdditionDispatcher::AddStub},
              {kAddOne, (RemoteFnPtr)&TAdditionDispatcher::AddOneStub},
              {MRemoteCaller::kUnknownRequest}
          };
      
          
    RegisterRequests(TStandardText("TAdditionDispatcher"), kLastRequest, requests);
      }

Writing the dispatcher stubs

You must define a dispatcher stub for each member function you want to call. In each dispatcher stub:

  1. Accept only the argument stream and result stream as parameters.
  2. Stream the arguments out of the argument stream.
  3. Call the dispatcher implementation, passing the arguments just read from the argument stream.
  4. Call ReturnSuccess, passing the result stream, to register that the code successfully called the dispatcher implementation.
  5. Stream the result back into the result stream.
This example shows AddStub, one of the two stub functions of TAdditionDispatcher:

      void
      TAdditionDispatcher::AddStub(TStream& argStream, TStream& resultStream)
      {
          ::Assertion(fImplementation != NIL, "\nNIL implementation!\n");
          
          long num1;
          long num2;
          
          
    num1 <<= argStream;
          num2 <<= argStream;
          
          long result = fImplementation->Add(num1, num2);
          
          ReturnSuccess(resultStream);
          
          
    result >>= resultStream;
      }

[Contents] [Previous] [Next]
Click the icon to mail questions or corrections about this material to Taligent personnel.
Copyright©1995 Taligent,Inc. All rights reserved.

Generated with WebMaker