In my previous entry, C++ RAII adapter for Xerces, I presented a simple memory management wrapper for Xerces types. Because of the way Xerces manages memory, I said, the quite handy boost::shared_ptr
couldn’t be used, so I wrote the memory management code myself to produce a safe wrapper in the style of std::auto_ptr
.
However, as Alf P. Steinbach pointed out, I was wrong, in that boost::shared_ptr
could be used by taking advantage of the custom deleter facility offered by that class. One benefit is that I can get rid of my hand-rolled memory management, but on the other hand, I’ll have to adjust the public interface to reflect different semantics.
The custom deleter version of the boost::shared_ptr
takes a deleter function as a constructor argument, and is perfectly happy with taking a static member function.
template class shared_xerces_ptr { // The actual data we're holding boost::shared_ptr item_; // Function to release Xerces data type with a release member function template static void do_release(T* item) { // Only release this if it has no owner if (0 == item->getOwnerDocument()) item->release(); } // Specializations for character types, released by XMLString::release template static void do_release(char* item) { XMLString::release(&item); } template static void do_release(XMLCh* item) { XMLString::release(&item); } public: ... // Assignment constructor shared_xerces_ptr(T* item) : item_(item, do_release ) {} // Release currently held data, if any, to hold another void assign(T* item) { item_.reset(item, xerces_deleter ); } ...
Unlike my previous effort, the auto_xerces_ptr
, there is no need here to write a custom destructor, as the boost::shared_ptr
takes care of that. Neither do I need to hide the copy constructor or assignment operator, as the boost::shared_ptr
makes those safe, too.
But by the same token, I can’t provide a xerces_release
function to release the data, like I did in auto_xerces_ptr
. Since the boost::shared_ptr
is safe with multiple copies, I cannot guarantee when held data will be released; if there are multiple references to it, it will only be released when the last reference is gone. For the same reason, the auto_xerces_ptr::yield
would make no sense in the shared_xerces_ptr
class, simply because there might be other instences referencing the data. Instead, I’ll provide a reset
function, and leave it at that.
The final difference is to make the assignment operator work like we’re used to, and make it chainable.
template class shared_xerces_ptr { // The actual data we're holding boost::shared_ptr item_; // Function to release Xerces data type with a release member function template static void do_release(T* item) { // Only release this if it has no owner if (0 == item->getOwnerDocument()) item->release(); } // Specializations for character types, released by XMLString::release template static void do_release(char* item) { XMLString::release(&item); } template static void do_release(XMLCh* item) { XMLString::release(&item); } public: // Default constructor shared_xerces_ptr() {} // Assignment constructor shared_xerces_ptr(T* item) : item_(item, do_release ) {} // Assignment of data to guard shared_xerces_ptr& operator=(T* item) { assign(item); return *this; } // Give up hold on data void reset() { item_.reset(); } // Release currently held data, if any, to hold another void assign(T* item) { item_.reset(item, do_release ); } // Get pointer to the currently held data, if any T* get() { return item_.get(); } const T* get() const { return item_.get(); } // Return true if no data is held bool is_released() const { return (0 == item_.get()); } };
There you have it. Not a replacement for auto_xerces_ptr
, but a complement. Again, if you find it useful, or have suggestions for improvements, please let me know.