How to use multiple active objects

This document illustrates how to use one active object to control another.

The following example code fragments show how to construct a program with two active objects, where one controls the initiation and cancellation of the other.

In these examples:

CActiveConsole is an active object and contains a pointer to a CConsoleBase object which is a service provider. Through this service provider, the active object provides the facility to request a character from the keyboard.

RunL() delegates the handling of completed requests to the pure virtual function ProcessKeyPress(), which must be provided by a derived class.

CMessageKeyProcessor is further derived from CActiveConsole and contains a pointer to another active object CExampleActiveObject, which requires input from the keyboard.

Depending on the input character, the CMessageKeyProcessor active object does one of the following:

  • issues a request to the CExampleActiveObject active object

  • cancels any outstanding request to the CExampleActiveObject active object

  • does nothing

The implementation of the CExampleActiveObject active object is not relevant to the example and is not shown.

The following diagram shows the relationship between the classes.

Encapsulating the service provider

The class CActiveConsole encapsulates the provision of basic keyboard services. Its iConsole data member is a pointer to the service provider, the CConsoleBase object.

The active object class is defined as:

class CActiveConsole : public CActive
    {
public:
    CActiveConsole(CConsoleBase* aConsole);
    void ConstructL();
    ~CActiveConsole();
    void RequestCharacter();
    void RunL();
    void DoCancel();
    virtual void ProcessKeyPress(TChar aChar)=0;
protected:
    CConsoleBase* iConsole; 
    };

The class constructor takes a pointer to a CConsoleBase object as its single argument and initializes its iConsole data member to this value:

CActiveConsole::CActiveConsole(CConsoleBase* aConsole)
    : iConsole(aConsole)
    {}

The ConstructL() function adds the active object to the active scheduler:

void CActiveConsole::ConstructL()
    {
    CActiveScheduler::Add(this);
    }

The destructor cancels any outstanding request before destroying the active object:

CActiveConsole::~CActiveConsole()
    {
    Cancel();
    }

DoCancel() is implemented to cancel the request to iConsole.

The RequestCharacter() function makes a request for a key press to the service provider by calling iConsole->Read(iStatus) and setting the active request flag:

void CActiveConsole::RequestCharacter()
    {
    iConsole->Read(iStatus);
    SetActive();
    }

The RunL() function makes a call to the ProcessKeyPress() function. This is a pure virtual function that derived classes must implement to handle the key press and to reissue the request:

void CActiveConsole::RunL()
    {
    ProcessKeyPress(TChar(iConsole->KeyCode()));
    }

Further deriving from the active object

The class CMessageKeyProcessor is a concrete class, derived from CActiveConsole. It provides an implementation for theProcessKeyPress() function and can issue or cancel requests to aCExampleActiveObject active object.

This active object class is defined as:

class CMessageKeyProcessor : public CActiveConsole
    {
public:
    ...
    CMessageKeyProcessor(CConsoleBase* aConsole, CExampleActiveObject* iExampleObject);
    void ProcessKeyPress(TChar aChar);
private:
    CExampleActiveObject* iExampleObject;
    };

Notes

  • The first constructor parameter specifies a CConsoleBase which will be used to provide asynchronous keyboard input.

  • the second constructor parameter specifies a CExampleActiveObject which will be controlled by this CMessageKeyProcessor.

The behaviour of the ProcessKeyPress() function depends on the key code value:

void CMessageKeyProcessor::ProcessKeyPress(TChar aChar)
    {
    if (aChar == 'm' || aChar == 'M')
        {
        iExampleObject->Cancel();
        iExampleObject->IssueRequest();
        }
    if (aChar == 'c' || aChar == 'C')
        {
        iExampleObject->Cancel();
        }
    if (aChar != EKeyEscape)
        {
        RequestCharacter();
        }
    else
        {
        iExampleObject->Cancel();
        CActiveScheduler::Stop();
        }
    }

Enhanced framework

In the code fragment below, an active scheduler is created to which both a CMessageKeyProcessor active object and aCExampleActiveObject active object are added:

LOCAL_C void doExampleL()
    {
    CActiveScheduler* exampleScheduler=new (ELeave) CActiveScheduler;
    CleanupStack::PushL(exampleScheduler);
    CActiveScheduler::Install(exampleScheduler);

    CExampleActiveObject* iExampleObject =
            CExampleActiveObject::NewLC();
    CMessageKeyProcessor* keyProcessor=
             CMessageKeyProcessor::NewLC(console, iExampleObject);

    keyProcessor->RequestCharacter();
    CActiveScheduler::Start();
    CleanupStack::PopAndDestroy(3);
    }

Notes

  • An instance of the active scheduler, exampleScheduler is pushed onto the cleanup stack and installed as the current active scheduler.

  • An instance of the CExampleActiveObject active object is created.

  • An instance of the CMessageKeyProcessor active object is created and this is in control.

  • keyProcessor->RequestCharacter() issues a request for keyboard input.

  • CActiveScheduler::Start() starts the active scheduler. At least one outstanding request is necessary before the wait loop is started, otherwise the thread hangs. All further request issuing and servicing occurs within this function. The wait loop continues until one of the active objects’ RunL() callsCActiveScheduler::Stop().

  • The active objects and the active scheduler are popped from the cleanup stack and destroyed.