Defining a caller

The caller provides the interface to the remote dispatcher. Instead of the dispatcher implementations, the caller class contains stubs. These caller stubs are responsible for:

  1. Marshalling parameters before sending them to the dispatcher
  2. Streaming parameters over to the dispatcher
  3. Unmarshalling any results returned by the dispatcher
  4. Returning results back to the caller
The caller class accomplishes these functions by mixing in
MRemoteCaller. Your caller class must derive from MRemoteCaller, directly or indirectly.

NOTE MRemoteCaller is not multithread safe. Only one thread can operate on a single caller instance at a time.

To define a caller, you:

  1. Write the caller class interface as described on page 81.
  2. Define a caller constructor as described on page 83.
  3. Write the caller stubs as described on page 83.
  4. Call MRemoteCallerDefinitionsMacro using the name of the dispatcher class that corresponds to this caller, as shown on page 84.

Writing the caller class interface


In the declaration for your caller class:

  1. Declare a constructor that accepts a service reference to the dispatcher as a parameter.
  2. Declare the caller stubs by overriding the member functions from the abstract base class.
  3. Call the macro MRemoteCallerDeclarationsMacro, passing it the name of your dispatcher class (without quotes or whitespace).
The interface for TAdditionCaller overrides Add and AddOne (defining them as caller stubs) and specifies the dispatcher class TAdditionDispatcher using the MRemoteCallerDeclarationsMacro:

      class TAdditionCaller : public MRemoteCaller, public MAdditionProtocol {
        public:
          TaligentTypeExtensionDeclarationsMacro(TAdditionCaller)
        public:
                      
    TAdditionCaller(const TServiceReference& serviceReference);
                      TAdditionCaller(const TAdditionCaller& source);
          virtual     ~TAdditionCaller();
          
          virtual TStream&    operator>>=(TStream& toStream) const;
          virtual TStream&    operator<<=(TStream& fromStream);
                                      
          
    virtual long        Add(long num1, long num2);
          virtual void        AddOne(long& num);
      
          
    MRemoteCallerDeclarationsMacro(TAdditionDispatcher);
      
      private:
                      TAdditionCaller();
          TAdditionCaller&operator=(const TAdditionCaller& source);
      
        private:
          enum {kOriginalVersion};
          TServiceReference* fServiceReference;
          TMessageStreamsTransport*fTransport;
      };

Defining the caller class constructor

Design your caller class constructor to:

  1. Call the empty constructor of the abstract base class from which the caller is derived (for example, TAdditionProtocol).
  2. Accept the service reference as a parameter.
  3. Call the MRemoteCaller constructor, passing the transport as a parameter.
  4. Call the macro MRemoteCallerEnable to build the dispatcher ID. You can use this macro anywhere in the constructor.
  5. Construct a TMessageStreamsTransport object using a TRequestSenderStream.
    Use the service reference passed into the constructor to create the TRequestSenderStream object. This opens a communications channel to the dispatcher.
The TAdditionCaller illustrates each of these steps:

      TAdditionCaller::TAdditionCaller(const TServiceReference& serviceReference)
          : MRemoteCaller(),
          MAdditionProtocol(),
          fServiceReference(NIL),
          fTransport(NIL)
      {
          
    MRemoteCallerEnable();
          
          
    fServiceReference = ::Copy(serviceReference);
          fTransport = 
              new TMessageStreamsTransport (new TRequestSenderStream(*fServiceReference));
          SetTransport(fTransport);
      }

Writing the caller stubs

For each member function you want to call remotely, you need to construct a caller stub. To do this, you override the pure virtual member functions inherited from the abstract base class.

In the caller stub:

  1. Call MRemoteCaller::BeginRequest, passing the member function identifier as the only argument.
    BeginRequest returns the stream as a parameter. Because this storage is owned by the base class, do not delete the pointer that this function returns.
  2. Stream each argument into the argument stream.
  3. Call MRemoteCaller::SendRequest to transmit the request to the dispatcher and receive the reply back.
    SendRequest catches any exceptions thrown in this process. If you catch an exception from SendRequest, you must re-throw it. You cannot stream out the results at this point.
  4. Stream the results out of the result stream.
  5. Call MRemoteCaller::EndRequest.
  6. Return the result.
This code shows how the calculator program defines caller stub code for Add, one of the remote member functions of the calculator program:

      long
      TAdditionCaller::Add(long num1, long num2)
      {
          long result = 0;
          
          try {
              
    TStream& argumentStream = *BeginRequest(TAdditionDispatcher::kAdd);
              
              num1 >>= argumentStream;
              num2 >>= argumentStream;
              
              TStream& resultStream = *SendRequest();
              
              result <<= resultStream;
              
              EndRequest();
          }
          
    catch (TRemoteCallException& exception) {
              cout << endl << "*** Remote call failed. ***" << endl;
          }
          catch (TMathException& exception) {
              cout << endl << "*** Received Math Exception ***" << endl;
              throw;
          }
          catch (TStandardException& exception) {
              cout << endl 
                  << "*** Encountered an error while making remote call. ***" << endl;
          }
          catch (...) {
              cout << endl 
                  << "*** Encountered a completely unknown error while making remote call.***" 
                  << endl;
          }
          
          
    return result;
      }

Using the caller definitions macro

In the implementation for each caller class that you define, you must call MRemoteCallerDefinitionsMacro, passing it the name of the caller class. Do not use quotes or whitespace in or around the caller name and do not nest the macro call within a function.

    MRemoteCallerDefinitionsMacro(TAdditionCaller)

[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