XLL+ Class Library

MtCalc.h

CMtCalcData

The basic data packet is the class CMtCalcData.

This contains the following:

The CMtCalcData class also has a method Calculate() which is responsible for calling the external calculation function, eval_avg_opt() and storing the results.

class CMtCalcData {
...
public:
    // Constructor - make copies of all inputs
    CMtCalcData
    (
        int         put,
        double      spot,
        double      sigma,
        ...
    ) 
    {
        // Copy all inputs
        ...
        
        // Calculate a string key which concatenates 
        // all input values
        m_strKey = ToString();
    }

    // Inputs
    int                 m_put;
    double              m_spot;
    double              m_sigma;
    ...

    // Encoded inputs
    std::string         m_strKey;

    // Outputs
    int                 m_ok;
    std::string         m_strError;
    double              m_result;

public:
    ...
    std::string ToString() const {
        std::string stKey;
        stKey = p(m_put) + "~" + p(m_spot) + "~" + ...;
        return stKey;
    }

    // The actual calculation
    void Calculate();
};

CMtCalcMsg

This data is embedded in a message class before it is posted to the main thread. The message class is therefore as follows:

class CMtCalcMsg : public CXllMtMsg {
public:
    CMtCalcMsg(CMtCalcData* data) : m_data(data) {}
    virtual ~CMtCalcMsg() {
        if (m_data)
            delete m_data;
    }
    CMtCalcData *RemoveData() {
        CMtCalcData *data = m_data;
        m_data = 0;
        return data;
    }
    CMtCalcData *m_data;
};

Note the method RemoveData(), which lets us remove the data from the message to use elsewhere, ensuring that it will not be destroyed when the message is deleted.

CMtCalcDataCache

The data cache maps topic strings to CMtCalcData pointers.

The topic string is constructed by printing out all the input variables, in such a way that every different calculation will produce a different topic string.

class CMtCalcDataCache {
public:
    ~CMtCalcDataCache() {
        Clear();
    }
    void Set(const char* pszArgs, CMtCalcData* data) {
        map_type::iterator itF = m_map.find(pszArgs);
        if (itF != m_map.end() && itF->second)
            delete itF->second;
        m_map[pszArgs] = data;
    }
    void Clear() {
        map_type::iterator it, itE;
        for (it = m_map.begin(), itE = m_map.end(); it != itE; it++) {
            if (it->second)
                delete it->second;
        }
        m_map.clear();
    }
    BOOL Lookup(LPCSTR pszArgs, CMtCalcData*& data) const {
        map_type::const_iterator itF = m_map.find(pszArgs);
        if (itF == m_map.end()) 
            return FALSE;
        data = itF->second;
        return TRUE;
    }
protected:
    typedef std::map<std::string, CMtCalcData*> map_type;
    map_type m_map;
};

The table below describes the states of the map entries:

Value State Ownership
0 A calculation has been requested, but is either still waiting to be dispatched to a thread, or has not yet completed. While a data object for this request exists, it is not owned by the cache, but currently belongs either to the dispatch queue or to a background thread.
Non-zero The calculation has been completed. If m_ok is non-zero, then the result is in m_result. If m_ok is zero, then an error string is in m_strError.
The data pointed to is owned by the cache.

Application class

Most of the application class, CMtCalcApp, is shown below. The areas that have been changed from the AppWizard-produced original are shown in grey.

class CMtCalcApp : public CXllPushApp
{
public:
    CMtCalcApp();

// Names
public:
    static LPCSTR m_pszDefName;

// Implementation

    // Data cache, containing results of previous             
    // and current calculations                               
    CMtCalcDataCache    m_cache;                              

    // Queue of calculations, waiting to be processed         
    queue_type          m_queue;                              

    // State flag                                             
    BOOL                m_bStopped;                           

    // Maximum number of worker threads                       
    int                 m_nThreadMax;                         

    // Current worker threads                                 
    threads_type        m_threads;                            

    // Start as many new calculations as permissible          
    void StartCalculations();                                 

// Overrides
    virtual int OnXllOpenEx();                                
    virtual void OnXllClose();                                
    virtual void ProcessAsyncMessage(CXllMtMsg* msg);         
    ...
};

Let's examine each section in turn.

    // Data cache, containing results of previous             
    // and current calculations                               
    CMtCalcDataCache    m_cache;                              

The data cache is a data member of the application class, and shares its lifetime.

    // Queue of calculations, waiting to be processed         
    queue_type          m_queue;                              

The queue contains calculations which have been requested, but not yet dispatched to a background thread.

    // State flag                                             
    BOOL                m_bStopped;                           

The flag m_bStopped is set during close down, to make sure that no further messages are processed.

    // Maximum number of worker threads                       
    int                 m_nThreadMax;                         

    // Current worker threads                                 
    threads_type        m_threads;                            

When a new thread is created, it is added to the m_threads collection. When the main thread receives notification of its death, it is removed from the collection. If the size of m_threads is equal to m_nThreadMax, no new thread is created.

    // Start as many new calculations as permissible          
    void StartCalculations();                                 

StartCalculations() dispatches as many new threads as permissible, i.e. until the queue is empty or the number of live threads is equal to m_nThreadMax.

    virtual int OnXllOpenEx();                                
    virtual void OnXllClose();                                
    virtual void ProcessAsyncMessage(CXllMtMsg* msg);         

These three overrides handle the three major events of the add-in's life: creation, destruction and the arrival of asynchronous data. We will examine them in detail later.

Next: MtCalc.cpp >>