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(); };
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.
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. |
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.