Our second example of an add-in that uses object handles is the HandleAPICached sample, and all the source code discussed below can be found in the Samples/HandleAPICached sub-directory.
The sample is a variation on the previous sample, HandleAPI.
MyObj2, The class that will hold the data for our example, based on the previous class MyObj. It has two differences:
class MyObj2 { protected: MyObj2() : m_refCount(0) {} public: MyObj2(double X, double Y) : m_refCount(0) { set(X, Y); } ~MyObj2(); long AddRef() { return ++m_refCount; } void Release() { if (--m_refCount == 0) delete this; } double X() const; double Y() const; const std::vector<double>& Vec() const; protected: long m_refCount; double m_dX, m_dY; std::vector<double> m_vec; ... };
The MyObj2 class can therefore be used with a reference template, so that ownership is shared between owners, and the object is only destroyed when all references to the object are released.
The MyObj2 class can be stored in a reference counting smart pointer, ref_ptr<T>.
template<class T> class ref_ptr { public: ref_ptr<T>() : p_(0) {} ~ref_ptr<T>() { set(0); } ref_ptr<T>(T* t) : p_(0) { set(t); } ref_ptr<T>(const ref_ptr<T>& ptr) : p_(0) { set(ptr.p()); } const ref_ptr<T>& operator=(const ref_ptr<T>& ptr) { set(ptr.p()); return *this; } const ref_ptr<T>& operator=(T* t) { set(t); return *this; } void set(T* t) { if (p_ != t) { if (p_) p_->Release(); if (t) t->AddRef(); p_ = t; } } T* p() const { return p_; } operator T*() const { return p(); } protected: T* p_; };
We add a line of code to the application class, CHandleAPIApp, in HandleAPI.h, which declares a handle store at application level, containing instances of MyObj2. This is slightly different from the store used in the previous example, in that the smart pointer class is explicitly defined.
class CHandleAPICachedApp : public CXllApp { public: ... CXlHandleStore<MyObj2, ref_ptr<MyObj2> > m_hsMyObj; ... };
The application class also contains a cache of MyObj2 references. A MyObj2 instance that is referenced by the cache will not be destroyed by garbage collection, since it's reference count will remain greater than 0.
class CHandleAPICachedApp : public CXllApp { public: ... typedef std::map<std::pair<double, double>, ref_ptr<MyObj2> > cache_type; cache_type m_cache; enum { m_cacheMax = 4 }; MyObj2* FindOrAdd(double X, double Y) { cache_type::const_iterator itF = m_cache.find(std::pair<double, double>(X, Y)); if (itF != m_cache.end()) return itF->second; // Trim the cache while (m_cache.size() >= m_cacheMax) { ... } // Create new object and add to cache MyObj2* obj = new MyObj2(X, Y); m_cache[std::pair<double, double>(X, Y)] = obj; return obj; } ... };
The only other difference between this example and the previous example concerns the object creation function. Instead of creating a new MyObj2 instance directly, it uses the application cache. If an appropriate instance already exists in the cache, then it is used. If not, then a new instance is created and added to the cache.
IMPLEMENT_XLLFN2(MyObj2Create, "JBB", "MyObj2.Create", "X,Y", "User Defi" "ned", "Object creation function", "No description provided\0" "00No description provided\000", "\0\0appscope=1\0", 1) extern "C" __declspec( dllexport ) long MyObj2Create(double X, double Y) { XLL_FIX_STATE; MyObj2* obj = XllGetTypedApp()->FindOrAdd(X, Y); return XllGetTypedApp()->AddObjectHandle(obj); }
This example differed from the previous example by using reference-counted data objects. This technique allows data objects to be maintained intact between garbage collections.
Many other techniques are applicable for managing object life-times, and different techniques will be found to be appropriate for different class libraries.