EAMessageExtra

Introduction

EAMessageExtra provides additional optional functionality on top of EAMessage.

Interface

QueuedHandler:

/// QueuedHandler
///
/// Implements a handler which maintains a private queue. 
/// This is a utility class which implements the IQueuedHandler interface.
/// QueuedHandler can be registered like a regular IHandler but messages can be only posted to its
/// queue and not sent to it, because message queues can hold only ref-counted (IMessageRC) messages.
/// Messages sent to a QueuedHandler as opposed to posted are not queued but delivered as usual
/// via the HandleMessage function.
///
///    QueuedHandler myQueuedHandler(pCoreAllocator);
///    myQueuedHandler.SetHandler(pSomeHandler); // This is optional
///
///    gpServer->AddQueuedHandler(&myQueuedHandler, 0x12345678, false); }
///
///    MessageBasicRC<1>* pMessage = new MessageBasicRC<1>;
///    pServer->MessagePost(0x12345678, pMessage);
///
///    // Later in handler's thread:
///    myQueuedHandler.ProcessQueue();          // If pSomeHandler was associated with myQueuedHandler like in the example above then the messages will be sent to it here.
///        or
///    myQueuedHandler.GetMessageQueue()->...;  // Directly read the queue.
///
class QueuedHandler : public EA::Messaging::IQueuedHandler
{
public:     
    QueuedHandler(EA::Allocator::ICoreAllocator* pCoreAllocator = NULL, bool threadSafe = true);

    /// Sets the memory allocator to use with this class (in particular mQueue).
    /// This function must be called before any messages are handled by the instance.
    void SetAllocator(EA::Allocator::ICoreAllocator* pCoreAllocator);

    /// Allows you to set a handler to pass messages onto. This is optional, as you can
    /// alternatively subclass QueuedHandler and intercept the messages directly.
    void SetHandler(IHandler* pHandler);

    /// Same as SetHandler but lets you set a C function.
    void SetHandlerFunction(HandlerFunction pHandlerFunction, void* pHandlerFunctionContext);

    /// Inherited from IHandler.
    /// Calls mpHandlerRC if it's non-NULL, else call mpHandlerFunction if it's non-NULL.
    bool HandleMessage(MessageId messageId, void* pMessage);

    /// Passes on the message to our mpHandlerFunction if there is one.
    bool HandleQueuedMessage(MessageId messageId, EA::Messaging::IMessageRC* pMessageRC, int priority);

    /// Does one cycle of queue processing. 
    /// Messages are delivered to the HandleMessage function.
    /// Returns the number of messages processed.
    /// This function would typically be called by the message recipient, possibly in its own thread.
    size_t ProcessQueue();
 
    /// If you don't want to call ProcessQueue and have it push the messages onto a handler, 
    /// you can call manually pull the messages from the queue via the MessageQueue.
    MessageQueue* GetMessageQueue();
};

Handler:

/// Handler
///
/// This templated class allows you to have a class which recieves messages to 
/// a user-defined member function as opposed to a function named
/// HandleMessage. This may be useful for the case that you have a class
/// which you want to handle multiple messages, each via a different
/// function. It's also useful for that case that you don't want your
/// class to inherit from an IHandler.
///
/// Example usage:
///    struct MultiHandler{
///       MultiHandler() : mHandleMessage3(this, &MultiHandler::HandleMessage3), 
///                        mHandleMessage4(this, &MultiHandler::HandleMessage4)
///       {
///           pMessageManager->AddHandler(&mHandleMessage3, 0x33333333);
///           pMessageManager->AddHandler(&mHandleMessage4, 0x44444444);
///       }
///
///       ~MultiHandler()
///       {
///           pMessageManager->RemoveHandler(&mHandleMessage3, 0x33333333);
///           pMessageManager->RemoveHandler(&mHandleMessage4, 0x44444444);
///       }
///
///       bool HandleMessage3(MessageId id, void* pMsg);
///       bool HandleMessage4(MessageId id, void* pMsg);
///
///    protected:
///       Handler<MultiHandler> mHandleMessage3;
///       Handler<MultiHandler> mHandleMessage4;
///    };
///
template <typename T>
class Handler : public EA::Messaging::IHandler
{
public:
    typedef bool (T::*MemberFunction)(EA::Messaging::MessageId messageId, void* pMessage);
    Handler(T* pT = NULL, MemberFunction pMemberFunction = NULL);

    void Assign(T* pT, MemberFunction pMemberFunction);
    bool HandleMessage(EA::Messaging::MessageId messageId, void* pMessage);
};

HandlerMemberFunction:

/// HandlerMemberFunction
///
/// This is an adapter class that allows a class member function to be a message
/// handler without being associated with an Handler interface and without 
/// necessarily being a virtual function. This class works via the Server 
/// AddHandler function:
///     void AddHandler(HandlerFunction, void* pHandlerFunctionContext, MessageId messageId, 
///                       bool bRefCounted, int nPriority);
/// The first argument to AddHandler must be this class and the second argument 
/// must be the address of this class. See the example below.
///
/// Example usage:
///    class Widget{
///       bool HandleMessage(MessageId id, void* pMsg);
///       HandlerMemberFunction mHandlerMemberFunction;
///       Widget() : mHandlerMemberFunction(this, HandleMessage){}
///    };
///
///    Widget* pWidget = new Widget;
///    pMessageManager->AddHandlerFunction(pWidget->mHandlerMemberFunction, &pWidget1->mHandlerMemberFunction, 0x12345678);
///    pMessageManager->RemoveHandlerFunction(pWidget->mHandlerMemberFunction, 0x12345678);
///
template <typename t>
class HandlerMemberFunction
{
public:
    typedef bool (T::*MemberFunction)(MessageId messageId, void* pMessage);
        
    HandlerMemberFunction(T* pT = NULL, MemberFunction pMemberFunction = NULL);

    void Assign(T* pT, MemberFunction pMemberFunction);

    static bool HandleMessage(MessageId messageId, void* pMessage, void* pContext);

    operator HandlerFunction();
};

AutoHandler:

/// AutoHandler
///
/// This class is useful for helping provide error free notification additions and removals in 
/// the case where an entity needs to manage large numbers of notifications. History has shown 
/// that code that registers for large numbers of notifications starts to get bogged down in 
/// code to add and remove notifications. This class reduces both the amount of code that needs
/// to be written and reduces the chance of error due to mismatched handler addition/removal. 
///
/// Note that AutoHandler uses a reference to the id array and not a copy of the id array.
/// This is done like so in order to prevent memory allocations from occurring. However, it 
/// results in the user having to make the id array available at the time of the AutoHandler
/// destruction. 
///
/// Example Usage:
///     const uint32_t ids[] = { 0x11111111, 0x22222222, 0x33333333 };
///
///     class Widget : public Handler{
///         AutoHandler mAutoHandler;
///
///         Widget(){
///             mAutoHandler.AddHandler(pMessageServer, this, ids, 3);
///         }
///
///         int HandleMessage(MessageId messageId, void* pMessage);
///     };
///     
class AutoHandler
{
public:
    AutoHandler();
   ~AutoHandler();

    void AddHandler(Server* pServer, IHandler* pHandler, const MessageId* pIdArray, 
                     size_t nIdArrayCount, bool bRefCounted, int nPriority = kPriorityNormal);

    bool RemoveHandler();
};

MessageRC:

/// class MessageRC
///
/// Implements a thread-safe, reference-counted message class suitable
/// for use as a base class for custom message class implementations.
///
class MessageRC : public IMessageRC
{
public:
    MessageRC();
    virtual ~MessageRC();

    virtual int AddRef();
    virtual int Release();
};

MessageBasic:

/// class MessageBasic
///
/// Provides a simple message class which stores arbitrary basic data.
/// You can use this class for simple messages that pass basic data
/// to recipients. In the past we've found that a surprisingly large
/// percentage of messages can be done this way.
///
/// Example usage:
///    MessageBasic<3> message;
///    message.SetUint32(1, 0x12345678);
///    uint32_t val = message.GetUint32(1);
///
/// Example usage:
///   In this case we have a subclass that does something smart in 
///   its destructor with owned data:
///
///   struct Subclass : public MessageBasic<1>{
///      Subclass(Blah* pBlah) { SetVoid(0, pBlah); }
///     ~Subclass()            { Blah* pBlah = GetVoid(0); delete pBlah; } 
///   };
///
template <size_t n>
class MessageBasic
{
public:
    union Data // This union is like a very small 'Variant' implementation.
    {
        uint32_t mUint32;
        uint64_t mUint64;
        float    mFloat;
        double   mDouble;
        void*    mpVoid;
    };

    Data      mData[N];
    MessageId mId;

    MessageBasic(MessageId id = 0) : mId(id) { }

    uint32_t GetUint32(size_t i) const       { return mData[i].mUint32; }
    void     SetUint32(size_t i, uint32_t n) { mData[i].mUint32 = n;    }

    uint64_t GetUint64(size_t i) const       { return mData[i].mUint64; }
    void     SetUint64(size_t i, uint64_t n) { mData[i].mUint64 = n;    }

    float    GetFloat(size_t i) const        { return mData[i].mFloat;  }
    void     SetFloat(size_t i, float f)     { mData[i].mFloat = f;     }

    double   GetDouble(size_t i) const       { return mData[i].mDouble; }
    void     SetDouble(size_t i, double d)   { mData[i].mDouble = d;    }

    void*    GetVoidPtr(size_t i) const      { return mData[i].mpVoid; }
    void     SetVoidPtr(size_t i, void* p)   { mData[i].mpVoid = p;    }
};

MessageBasic:

/// class MessageBasicRC
///
/// This is a version of MessageBasic which implements reference counting.
/// This class can be used for Server::MessagePost, as that function 
/// uses IMessageRC (IMessage, Reference Counted).
///
/// Instances of this class need to be created via some kind of heap and 
/// cannot be created on the stack. This is because their lifetime is 
/// necessarily indeterminate. However, such objects can be located in a 
/// special nonfragmenting fixed size heap in order to optimize their allocation.
///
/// Example usage:
///    MessageBasicRC<1>* pMBRC = new MessageBasicRC;
///    pServer->MessagePost(0x12345678, pMBRC);
///
template <size_t n>
class MessageBasicRC : public MessageBasic, public MessageRC
{
public:
    MessageBasicRC(MessageId id = 0);
};