/*------------------------------------------------------------------------------ Author: Andy Rushton and Daniel Milton Copyright: (c) Andy Rushton, 2004; Daniel Milton 2005 License: BSD License, see ../docs/license.html ------------------------------------------------------------------------------*/ #include "string_utilities.hpp" #include "debug.hpp" //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Simple Pointer Classes // (c) Dan Milton // based on smart_ptr classes //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // simple_ptr class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // constructors, assignments and destructors // create a null pointer template simple_ptr::simple_ptr(void) : m_pointer(0), m_count(new unsigned(1)) { } // create a pointer containing a *copy* of the object pointer template simple_ptr::simple_ptr(const T& data) : m_pointer(0), m_count(new unsigned(1)) { set_value(data); } // delete any old value in the pointer and assign to it a new *copy* of the argument template simple_ptr& simple_ptr::operator=(const T& data) { set_value(data); return *this; } // copy constructor implements aliasing template simple_ptr::simple_ptr(const simple_ptr& r) : m_pointer(r.m_pointer), m_count(r.m_count) { ++(*m_count); } // assignment of smart pointers implements aliasing template simple_ptr& simple_ptr::operator=(const simple_ptr& r) { // make it alias-copy safe - this means that I don't try to do the // assignment if r is either the same object or an alias of it if (m_count == r.m_count) return *this; // dealias this object if((--(*m_count)) == 0) { if (m_pointer) delete m_pointer; delete m_count; } // make this object an alias of r m_pointer = r.m_pointer; m_count = r.m_count; ++(*m_count); return *this; } // create a pointer containing a dynamically created object // Note: the object must be allocated *by the user* with new // constructor form - must be called in the form simple_ptr x(new type(args)) template simple_ptr::simple_ptr(T* data) : m_pointer(0), m_count(new unsigned(1)) { set(data); } // assignment form template simple_ptr& simple_ptr::operator=(T* data) { set(data); return *this; } // destructor decrements the reference count and delete only when the last reference is destroyed template simple_ptr::~simple_ptr(void) { // dealias this object if((--(*m_count)) == 0) { if (m_pointer) delete m_pointer; delete m_count; } } ////////////////////////////////////////////////////////////////////////////// // logical tests to see if there is anything contained in the pointer since it can be null template bool simple_ptr::null(void) const { return m_pointer == 0; } template bool simple_ptr::present(void) const { return m_pointer != 0; } template bool simple_ptr::operator!(void) const { return m_pointer == 0; } template simple_ptr::operator bool(void) const { return m_pointer != 0; } ////////////////////////////////////////////////////////////////////////////// // dereference operators and functions template T& simple_ptr::operator*(void) throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator*"); return *m_pointer; } template const T& simple_ptr::operator*(void) const throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator*"); return *m_pointer; } template T* simple_ptr::operator->(void) throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator->"); return m_pointer; } template const T* simple_ptr::operator->(void) const throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator->"); return m_pointer; } ////////////////////////////////////////////////////////////////////////////// // explicit function forms of the above assignment dereference operators template void simple_ptr::set_value(const T& data) { set(new T(data)); } template T& simple_ptr::value(void) throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::value"); return *m_pointer; } template const T& simple_ptr::value(void) const throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::value"); return *m_pointer; } template void simple_ptr::set(T* data) { // if this is the only object, delete the old value, else dealias if (*m_count == 1) { if (m_pointer) delete m_pointer; } else { --(*m_count); m_count = new unsigned(1); } m_pointer = data; } template T* simple_ptr::pointer(void) { return m_pointer; } template const T* simple_ptr::pointer(void) const { return m_pointer; } //////////////////////////////////////////////////////////////////////////////// // functions to manage counted referencing template bool simple_ptr::aliases(const simple_ptr& r) const { // aliases have the same counter return m_count == r.m_count; } template unsigned simple_ptr::alias_count(void) const { return *m_count; } template void simple_ptr::make_unique(void) { // if already unique, do nothing if (*m_count == 1) return; // dealias this and copy the old contents --(*m_count); if (m_pointer) m_pointer = new T(*m_pointer); m_count = new unsigned(1); } // clear sets this alias to null, dealiasing if necessary template void simple_ptr::clear(void) { set(0); } template void simple_ptr::copy(const simple_ptr& data) { // first make this an alias of the data, then make it a copy by making it unique *this = data; make_unique(); } template simple_ptr simple_ptr::copy(void) const { simple_ptr result; result.copy(*this); return result; } //////////////////////////////////////////////////////////////////////////////// // persistence methods // see also - external persistence functions // Like ordinary pointers they are dumped as a magic key and then, on the first // alias only, the contents. Subsequent aliases to the same object are dumped // as just a magic key. I use the address of the counter to generate the key // since that is always there and is unique. Using the address of the object // would make all null pointers aliases of each other which may not be // correct. // I've split these into the exported non-member functions and internal member // functions because I need access to the data structure but couldn't get // friend templates to work on VC // a simple_ptr is used to point to non-clonable objects, so use the standard dump/restore for pointers template void simple_ptr::dump(dump_context& context) const throw(persistent_dump_failed) { // get a magic key for the count - this also returns a flag saying whether its been seen before std::pair mapping = context.pointer_map(m_count); // dump the magic key ::dump(context,mapping.second); // dump the contents but only if this is the first time this object has been seen // use the existing routines for ordinary pointers to dump the contents if (!mapping.first) dump_pointer(context,m_pointer); } template void simple_ptr::restore(restore_context& context) throw(persistent_restore_failed) { // restore the magic key unsigned magic = 0; ::restore(context,magic); // lookup this magic number to see if we have seen this already std::pair mapping = context.pointer_map(magic); if (mapping.first) { // this object has already been restored // dealias the existing object and make it an alias of the existing object if (--(*m_count) == 0) { if (m_pointer) delete m_pointer; delete m_count; } m_pointer = ((simple_ptr*)mapping.second)->m_pointer; m_count = ((simple_ptr*)mapping.second)->m_count; ++(*m_count); } else { // this is the first contact with this object // make sure this pointer is unique to prevent side-effects if (--(*m_count) == 0) { if (m_pointer) delete m_pointer; delete m_count; } m_count = new unsigned(1); // restore the contents restore_pointer(context,m_pointer); // map the magic key onto this object context.pointer_add(magic,this); } } //////////////////////////////////////////////////////////////////////////////// // comparisons required for using this class in an STL container template bool operator==(const simple_ptr& left, const simple_ptr& right) { // a null is not equal to a non-null but equal to another null if(!left || !right) return left.pointer() == right.pointer(); // shortcut - if the two pointers are equal then the objects must be equal if (left.pointer() == right.pointer()) return true; // otherwise compare the objects themselves return *left == *right; } template bool operator<(const simple_ptr& left, const simple_ptr& right) { // a null pointer is less than a non-null but equal to another null if(!left || !right) return left.pointer() < right.pointer(); // shortcut - if the two pointers are equal then the comparison must be false if (left.pointer() == right.pointer()) return false; // otherwise, compare the objects return *left < *right; } //////////////////////////////////////////////////////////////////////////////// // string/print utilities template std::string simple_ptr_to_string(const simple_ptr& ptr, std::string null_string) { if (!ptr) return null_string; return "*(" + to_string(*ptr) + ")"; } template otext& print_simple_ptr(otext& str, const simple_ptr& ptr, std::string null_string) { if (!ptr) return str << null_string; str << "*("; print(str, *ptr); str << ")"; return str; } template otext& print_simple_ptr(otext& str, const simple_ptr& ptr, unsigned indent, std::string null_string) { print_indent(str, indent); print_simple_ptr(str, ptr, null_string); return str << endl; } //////////////////////////////////////////////////////////////////////////////// // Persistence - non-member functions actually called by the user template void dump_simple_ptr(dump_context& context, const simple_ptr& data) throw(persistent_dump_failed) { data.dump(context); } template void restore_simple_ptr(restore_context& context, simple_ptr& data) throw(persistent_restore_failed) { data.restore(context); } //////////////////////////////////////////////////////////////////////////////// // simple_ptr_clone class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // constructors, assignments and destructors // create a null pointer template simple_ptr_clone::simple_ptr_clone(void) : m_pointer(0), m_count(new unsigned(1)) { } // create a pointer containing a *copy* of the object pointer template simple_ptr_clone::simple_ptr_clone(const T& data) : m_pointer(0), m_count(new unsigned(1)) { set_value(data); } // delete any old value in the pointer and assign to it a new *copy* of the argument template simple_ptr_clone& simple_ptr_clone::operator=(const T& data) { set_value(data); return *this; } // copy constructor implements counted referencing - no copy is made template simple_ptr_clone::simple_ptr_clone(const simple_ptr_clone& r) : m_pointer(r.m_pointer), m_count(r.m_count) { ++(*m_count); } // assignment of smart pointers implement counted referencing - no copy is made template simple_ptr_clone& simple_ptr_clone::operator=(const simple_ptr_clone& r) { // make it alias-copy safe - this means that I don't try to do the // assignment if r is either the same object or an alias of it if (m_count == r.m_count) return *this; // dealias this object if((--(*m_count)) == 0) { if (m_pointer) delete m_pointer; delete m_count; } // make this object an alias of r m_pointer = r.m_pointer; m_count = r.m_count; ++(*m_count); return *this; } // create a pointer containing a dynamically created object // Note: the object must be allocated *by the user* with new // constructor form - must be called in the form simple_ptr_clone x(new type(args)) template simple_ptr_clone::simple_ptr_clone(T* data) : m_pointer(0), m_count(new unsigned(1)) { set(data); } // assignment form template simple_ptr_clone& simple_ptr_clone::operator=(T* data) { set(data); return *this; } // destructor decrements the reference count and delete only when the last reference is destroyed template simple_ptr_clone::~simple_ptr_clone(void) { // dealias this object if((--(*m_count)) == 0) { if (m_pointer) delete m_pointer; delete m_count; } } ////////////////////////////////////////////////////////////////////////////// // logical tests to see if there is anything contained in the pointer since it can be null template bool simple_ptr_clone::null(void) const { return m_pointer == 0; } template bool simple_ptr_clone::present(void) const { return m_pointer != 0; } template bool simple_ptr_clone::operator!(void) const { return m_pointer == 0; } template simple_ptr_clone::operator bool(void) const { return m_pointer != 0; } ////////////////////////////////////////////////////////////////////////////// // dereference operators and functions template T& simple_ptr_clone::operator*(void) throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr_clone::operator*"); return *m_pointer; } template const T& simple_ptr_clone::operator*(void) const throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr_clone::operator*"); return *m_pointer; } template T* simple_ptr_clone::operator->(void) throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr_clone::operator->"); return m_pointer; } template const T* simple_ptr_clone::operator->(void) const throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr_clone::operator->"); return m_pointer; } ////////////////////////////////////////////////////////////////////////////// // explicit function forms of the above assignment dereference operators template void simple_ptr_clone::set_value(const T& data) { set((T*)data.clone()); } template T& simple_ptr_clone::value(void) throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr_clone::value"); return *m_pointer; } template const T& simple_ptr_clone::value(void) const throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr_clone::value"); return *m_pointer; } template void simple_ptr_clone::set(T* data) { // if this is the only object, delete the old value, else dealias if (*m_count == 1) { if (m_pointer) delete m_pointer; } else { --(*m_count); m_count = new unsigned(1); } m_pointer = data; } template T* simple_ptr_clone::pointer(void) { return m_pointer; } template const T* simple_ptr_clone::pointer(void) const { return m_pointer; } //////////////////////////////////////////////////////////////////////////////// // functions to manage counted referencing template bool simple_ptr_clone::aliases(const simple_ptr_clone& r) const { // aliases have the same counter return m_count == r.m_count; } template unsigned simple_ptr_clone::alias_count(void) const { return *m_count; } template void simple_ptr_clone::make_unique(void) { // if already unique, do nothing if (*m_count == 1) return; // dealias this and copy the old contents --(*m_count); if (m_pointer) m_pointer = (T*)m_pointer->clone(); m_count = new unsigned(1); } // this version of the smart pointer only clears unique template void simple_ptr_clone::clear(void) { set(0); } template void simple_ptr_clone::copy(const simple_ptr_clone& data) { // first make this an alias of the data, then make it a copy by making it unique *this = data; make_unique(); } template simple_ptr_clone simple_ptr_clone::copy(void) const { simple_ptr_clone result; result.copy(*this); return result; } //////////////////////////////////////////////////////////////////////////////// // persistence methods // see also - external persistence functions // Like ordinary pointers they are dumped as a magic key and then, on the first // alias only, the contents. Subsequent aliases to the same object are dumped // as just a magic key. I use the address of the counter to generate the key // since that is always there and is unique. Using the address of the object // would make all null pointers aliases of each other which may not be // correct. // I've split these into the exported non-member functions and internal member // functions because I need access to the data structure but couldn't get // friend templates to work on VC // a simple_ptr_clone is used to point to non-clonable objects, so use the standard dump/restore for pointers template void simple_ptr_clone::dump(dump_context& context) const throw(persistent_dump_failed) { // get a magic key for the count - this also returns a flag saying whether its been seen before std::pair mapping = context.pointer_map(m_count); // dump the magic key ::dump(context,mapping.second); // dump the contents but only if this is the first time this object has been seen // use the existing routines for ordinary pointers to dump the contents if (!mapping.first) dump_interface(context,m_pointer); } template void simple_ptr_clone::restore(restore_context& context) throw(persistent_restore_failed) { // restore the magic key unsigned magic = 0; ::restore(context,magic); // lookup this magic number to see if we have seen this already std::pair mapping = context.pointer_map(magic); if (mapping.first) { // this object has already been restored // dealias the existing object and make it an alias of the existing object if (--(*m_count) == 0) { if (m_pointer) delete m_pointer; delete m_count; } m_pointer = ((simple_ptr_clone*)mapping.second)->m_pointer; m_count = ((simple_ptr_clone*)mapping.second)->m_count; ++(*m_count); } else { // this is the first contact with this object // make sure this pointer is unique to prevent side-effects if (--(*m_count) == 0) { if (m_pointer) delete m_pointer; delete m_count; } m_count = new unsigned(1); // restore the contents restore_interface(context,m_pointer); // map the magic key onto this object context.pointer_add(magic,this); } } //////////////////////////////////////////////////////////////////////////////// // comparisons required for using this class in an STL container template bool operator==(const simple_ptr_clone& left, const simple_ptr_clone& right) { // a null is not equal to a non-null but equal to another null if(!left || !right) return left.pointer() == right.pointer(); // shortcut - if the two pointers are equal then the objects must be equal if (left.pointer() == right.pointer()) return true; // otherwise compare the objects themselves return *left == *right; } template bool operator<(const simple_ptr_clone& left, const simple_ptr_clone& right) { // a null pointer is less than a non-null but equal to another null if(!left || !right) return left.pointer() < right.pointer(); // shortcut - if the two pointers are equal then the comparison must be false if (left.pointer() == right.pointer()) return false; // otherwise, compare the objects return *left < *right; } //////////////////////////////////////////////////////////////////////////////// // string/print utilities template std::string simple_ptr_clone_to_string(const simple_ptr_clone& ptr, std::string null_string) { if (!ptr) return null_string; return "*(" + to_string(*ptr) + ")"; } template otext& print_simple_ptr_clone(otext& str, const simple_ptr_clone& ptr, std::string null_string) { if (!ptr) return str << null_string; str << "*("; print(str, *ptr); str << ")"; return str; } template otext& print_simple_ptr_clone(otext& str, const simple_ptr_clone& ptr, unsigned indent, std::string null_string) { print_indent(str, indent); print_simple_ptr_clone(str, ptr, null_string); return str << endl; } //////////////////////////////////////////////////////////////////////////////// // Persistence - non-member functions actually called by the user template void dump_simple_ptr_clone(dump_context& context, const simple_ptr_clone& data) throw(persistent_dump_failed) { data.dump(context); } template void restore_simple_ptr_clone(restore_context& context, simple_ptr_clone& data) throw(persistent_restore_failed) { data.restore(context); } //////////////////////////////////////////////////////////////////////////////// // simple_ptr_nocopy class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // constructors, assignments and destructors // create a null pointer template simple_ptr_nocopy::simple_ptr_nocopy(void) : m_pointer(0), m_count(new unsigned(1)) { } // copy constructor implements counted referencing - no copy is made template simple_ptr_nocopy::simple_ptr_nocopy(const simple_ptr_nocopy& r) : m_pointer(r.m_pointer), m_count(r.m_count) { ++(*m_count); } // assignment of smart pointers implement counted referencing - no copy is made template simple_ptr_nocopy& simple_ptr_nocopy::operator=(const simple_ptr_nocopy& r) { // make it alias-copy safe - this means that I don't try to do the // assignment if r is either the same object or an alias of it if (m_count == r.m_count) return *this; // dealias this object if((--(*m_count)) == 0) { if (m_pointer) delete m_pointer; delete m_count; } // make this object an alias of r m_pointer = r.m_pointer; m_count = r.m_count; ++(*m_count); return *this; } // create a pointer containing a dynamically created object // Note: the object must be allocated *by the user* with new // constructor form - must be called in the form simple_ptr_nocopy x(new type(args)) template simple_ptr_nocopy::simple_ptr_nocopy(T* data) : m_pointer(0), m_count(new unsigned(1)) { set(data); } // assignment form template simple_ptr_nocopy& simple_ptr_nocopy::operator=(T* data) { set(data); return *this; } // destructor decrements the reference count and delete only when the last reference is destroyed template simple_ptr_nocopy::~simple_ptr_nocopy(void) { // dealias this object if((--(*m_count)) == 0) { if (m_pointer) delete m_pointer; delete m_count; } } ////////////////////////////////////////////////////////////////////////////// // logical tests to see if there is anything contained in the pointer since it can be null template bool simple_ptr_nocopy::null(void) const { return m_pointer == 0; } template bool simple_ptr_nocopy::present(void) const { return m_pointer != 0; } template bool simple_ptr_nocopy::operator!(void) const { return m_pointer == 0; } template simple_ptr_nocopy::operator bool(void) const { return m_pointer != 0; } ////////////////////////////////////////////////////////////////////////////// // dereference operators and functions template T& simple_ptr_nocopy::operator*(void) throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr_nocopy::operator*"); return *m_pointer; } template const T& simple_ptr_nocopy::operator*(void) const throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr_nocopy::operator*"); return *m_pointer; } template T* simple_ptr_nocopy::operator->(void) throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr_nocopy::operator->"); return m_pointer; } template const T* simple_ptr_nocopy::operator->(void) const throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr_nocopy::operator->"); return m_pointer; } ////////////////////////////////////////////////////////////////////////////// // explicit function forms of the above assignment dereference operators template T& simple_ptr_nocopy::value(void) throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr_nocopy::value"); return *m_pointer; } template const T& simple_ptr_nocopy::value(void) const throw(null_dereference) { if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr_nocopy::value"); return *m_pointer; } template void simple_ptr_nocopy::set(T* data) { // if this is the only object, delete the old value, else dealias if (*m_count == 1) { if (m_pointer) delete m_pointer; } else { --(*m_count); m_count = new unsigned(1); } m_pointer = data; } template T* simple_ptr_nocopy::pointer(void) { return m_pointer; } template const T* simple_ptr_nocopy::pointer(void) const { return m_pointer; } //////////////////////////////////////////////////////////////////////////////// // functions to manage counted referencing template bool simple_ptr_nocopy::aliases(const simple_ptr_nocopy& r) const { // aliases have the same counter return m_count == r.m_count; } template unsigned simple_ptr_nocopy::alias_count(void) const { return *m_count; } // this version of the smart pointer only clears unique template void simple_ptr_nocopy::clear(void) { set(0); } //////////////////////////////////////////////////////////////////////////////// // persistence methods // a smart_ptr_nocopy is used to point to non-copyable objects, so it doesn't make sense to provide persistence! //////////////////////////////////////////////////////////////////////////////// // comparisons required for using this class in an STL container template bool operator==(const simple_ptr_nocopy& left, const simple_ptr_nocopy& right) { // a null is not equal to a non-null but equal to another null if(!left || !right) return left.pointer() == right.pointer(); // shortcut - if the two pointers are equal then the objects must be equal if (left.pointer() == right.pointer()) return true; // otherwise compare the objects themselves return *left == *right; } template bool operator<(const simple_ptr_nocopy& left, const simple_ptr_nocopy& right) { // a null pointer is less than a non-null but equal to another null if(!left || !right) return left.pointer() < right.pointer(); // shortcut - if the two pointers are equal then the comparison must be false if (left.pointer() == right.pointer()) return false; // otherwise, compare the objects return *left < *right; } //////////////////////////////////////////////////////////////////////////////// // string/print utilities template std::string simple_ptr_nocopy_to_string(const simple_ptr_nocopy& ptr, std::string null_string) { if (!ptr) return null_string; return "*(" + to_string(*ptr) + ")"; } template otext& print_simple_ptr_nocopy(otext& str, const simple_ptr_nocopy& ptr, std::string null_string) { if (!ptr) return str << null_string; str << "*("; print(str, *ptr); str << ")"; return str; } template otext& print_simple_ptr_nocopy(otext& str, const simple_ptr_nocopy& ptr, unsigned indent, std::string null_string) { print_indent(str, indent); print_simple_ptr_nocopy(str, ptr, null_string); return str << endl; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Smart Pointer Classes - Andy Rushton //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // internal holder data structure //////////////////////////////////////////////////////////////////////////////// template class smart_ptr_holder { private: unsigned m_count; T* m_data; // disallow copying because the holder doesn't know how to copy smart_ptr_holder(const smart_ptr_holder&) : m_count(0), m_data(0) { DEBUG_ASSERT(false); } smart_ptr_holder& operator=(const smart_ptr_holder&) { DEBUG_ASSERT(false); return *this; } public: smart_ptr_holder(T* p = 0) : m_count(1), m_data(p) { } ~smart_ptr_holder(void) { clear(); } unsigned count(void) const { return m_count; } void increment(void) { ++m_count; } bool decrement(void) { --m_count; return m_count == 0; } bool null(void) { return m_data == 0; } void clear(void) { if(m_data) delete m_data; m_data = 0; } void set(T* p = 0) { clear(); m_data = p; } T*& pointer(void) { return m_data; } const T* pointer(void) const { return m_data; } T& value(void) { return *m_data; } const T& value(void) const { return *m_data; } }; //////////////////////////////////////////////////////////////////////////////// // smart_ptr class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // constructors, assignments and destructors // create a null pointer template smart_ptr::smart_ptr(void) : m_holder(new smart_ptr_holder) { } // create a pointer containing a *copy* of the object pointer template smart_ptr::smart_ptr(const T& data) : m_holder(new smart_ptr_holder) { m_holder->set(new T(data)); } // delete any old value in the pointer and assign to it a new *copy* of the argument template smart_ptr& smart_ptr::operator=(const T& data) { m_holder->set(new T(data)); return *this; } // copy constructor implements counted referencing - no copy is made template smart_ptr::smart_ptr(const smart_ptr& r) : m_holder(0) { m_holder = r.m_holder; m_holder->increment(); } // assignment of smart pointers implement counted referencing - no copy is made template smart_ptr& smart_ptr::operator=(const smart_ptr& r) { // // make it self-copy safe // if (this == &r) return *this; // make it alias-copy safe - this means that I don't try to do the // assignment if r is either the same object or an alias of it if (m_holder == r.m_holder) return *this; if(m_holder->decrement()) delete m_holder; m_holder = r.m_holder; m_holder->increment(); return *this; } // create a pointer containing a dynamically created object // Note: the object must be allocated *by the user* with new // constructor form - must be called in the form smart_ptr x(new type(args)) template smart_ptr::smart_ptr(T* data) : m_holder(new smart_ptr_holder) { m_holder->set(data); } // assignment form template smart_ptr& smart_ptr::operator=(T* data) { m_holder->set(data); return *this; } // destructor decrements the reference count and delete only when the last reference is destroyed template smart_ptr::~smart_ptr(void) { if(m_holder->decrement()) delete m_holder; } ////////////////////////////////////////////////////////////////////////////// // logical tests to see if there is anything contained in the pointer since it can be null template bool smart_ptr::null(void) const { return m_holder->null(); } template bool smart_ptr::present(void) const { return !m_holder->null(); } template bool smart_ptr::operator!(void) const { return m_holder->null(); } template smart_ptr::operator bool(void) const { return !m_holder->null(); } ////////////////////////////////////////////////////////////////////////////// // dereference operators and functions template T& smart_ptr::operator*(void) throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator*"); return m_holder->value(); } template const T& smart_ptr::operator*(void) const throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator*"); return m_holder->value(); } template T* smart_ptr::operator->(void) throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator->"); return m_holder->pointer(); } template const T* smart_ptr::operator->(void) const throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator->"); return m_holder->pointer(); } ////////////////////////////////////////////////////////////////////////////// // explicit function forms of the above assignment dereference operators template void smart_ptr::set_value(const T& data) { m_holder->set(new T(data)); } template T& smart_ptr::value(void) throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::value"); return m_holder->value(); } template const T& smart_ptr::value(void) const throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::value"); return m_holder->value(); } template void smart_ptr::set(T* data) { m_holder->set(data); } template T* smart_ptr::pointer(void) { return m_holder->pointer(); } template const T* smart_ptr::pointer(void) const { return m_holder->pointer(); } //////////////////////////////////////////////////////////////////////////////// // functions to manage counted referencing template bool smart_ptr::aliases(const smart_ptr& r) const { return !m_holder->null() && m_holder->pointer() == r.m_holder->pointer(); } template unsigned smart_ptr::alias_count(void) const { return m_holder->count(); } template void smart_ptr::make_unique(void) { if(m_holder->count() > 1) { smart_ptr_holder* old_holder = m_holder; m_holder = new smart_ptr_holder; old_holder->decrement(); if (old_holder->pointer()) m_holder->set(new T(old_holder->value())); } } template void smart_ptr::clear(void) { m_holder->clear(); } template void smart_ptr::clear_unique(void) { if (m_holder->count() == 1) m_holder->clear(); else { m_holder->decrement(); m_holder = new smart_ptr_holder; } } template void smart_ptr::copy(const smart_ptr& data) { *this = data; make_unique(); } template smart_ptr smart_ptr::copy(void) const { smart_ptr result; result.copy(*this); return result; } //////////////////////////////////////////////////////////////////////////////// // persistence methods // see also - external persistence functions // Like ordinary pointers they are dumped as an address and then, on the first // alias only, the contents. Subsequent aliases to the same object are dumped // as just an address. I use the address of the holder within smart pointer // since that is always there and is unique. Using the address of the object // would make all null pointers aliases of each other which may not be // correct. This requires burglarisation of the smart pointer class. // I've split these into the exported non-member functions and internal member // functions because I need access to the data structure but couldn't get // friend templates to work on VC // a smart_ptr is used to point to non-clonable objects, so use the standard dump/restore for pointers template void smart_ptr::dump(dump_context& context) const throw(persistent_dump_failed) { // Many smart pointers can point to the same object. // I could have used the address of the object to differentiate, // but that would not have differentiated between different null smart pointers // so I use the address of the substructure to differentiate between different objects. // get a magic key for the substructure - this also returns a flag saying whether its been seen before std::pair mapping = context.pointer_map(m_holder); // dump the magic key ::dump(context,mapping.second); // dump the contents but only if this is the first time this object has been seen // use the existing routines for ordinary pointers to dump the contents if (!mapping.first) dump_pointer(context,m_holder->pointer()); } template void smart_ptr::restore(restore_context& context) throw(persistent_restore_failed) { // get the old substructure magic key unsigned magic = 0; ::restore(context,magic); // lookup this magic number to see if we have seen this already std::pair mapping = context.pointer_map(magic); if (mapping.first) { // this holder has already been restored // dealias the existing holder and replace it with the seen-before holder to make this object an alias of it if (m_holder->decrement()) delete m_holder; m_holder = (smart_ptr_holder*)mapping.second; m_holder->increment(); } else { // this is the first contact with this holder // make sure this smart pointer is unique to prevent side-effects clear_unique(); // map the magic key onto this structure's holder and then restore the contents context.pointer_add(magic,m_holder); restore_pointer(context,m_holder->pointer()); } } //////////////////////////////////////////////////////////////////////////////// // comparisons required for using this class in an STL container template bool operator==(const smart_ptr& left, const smart_ptr& right) { // a null is not equal to a non-null but equal to another null if(!left || !right) return left.pointer() == right.pointer(); // shortcut - if the two pointers are equal then the objects must be equal if (left.pointer() == right.pointer()) return true; // otherwise compare the objects themselves return *left == *right; } template bool operator<(const smart_ptr& left, const smart_ptr& right) { // a null pointer is less than a non-null but equal to another null if(!left || !right) return left.pointer() < right.pointer(); // shortcut - if the two pointers are equal then the comparison must be false if (left.pointer() == right.pointer()) return false; // otherwise, compare the objects return *left < *right; } //////////////////////////////////////////////////////////////////////////////// // string/print utilities template std::string smart_ptr_to_string(const smart_ptr& ptr, std::string null_string) { if (!ptr) return null_string; return "*(" + to_string(*ptr) + ")"; } template otext& print_smart_ptr(otext& str, const smart_ptr& ptr, std::string null_string) { if (!ptr) return str << null_string; str << "*("; print(str, *ptr); str << ")"; return str; } template otext& print_smart_ptr(otext& str, const smart_ptr& ptr, unsigned indent, std::string null_string) { print_indent(str, indent); print_smart_ptr(str, ptr, null_string); return str << endl; } //////////////////////////////////////////////////////////////////////////////// // Persistence - non-member functions actually called by the user template void dump_smart_ptr(dump_context& context, const smart_ptr& data) throw(persistent_dump_failed) { data.dump(context); } template void restore_smart_ptr(restore_context& context, smart_ptr& data) throw(persistent_restore_failed) { data.restore(context); } //////////////////////////////////////////////////////////////////////////////// // smart_ptr_clone class //////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // constructors, assignments and destructors // create a null pointer template smart_ptr_clone::smart_ptr_clone(void) : m_holder(new smart_ptr_holder) { } // create a pointer containing a *copy* of the object pointer template smart_ptr_clone::smart_ptr_clone(const T& data) : m_holder(new smart_ptr_holder) { m_holder->set((T*)data.clone()); } // delete any old value in the pointer and assign to it a new *copy* of the argument template smart_ptr_clone& smart_ptr_clone::operator=(const T& data) { m_holder->set((T*)data.clone()); return *this; } // copy constructor implements counted referencing - no copy is made template smart_ptr_clone::smart_ptr_clone(const smart_ptr_clone& r) : m_holder(0) { m_holder = r.m_holder; m_holder->increment(); } // assignment of smart pointers implement counted referencing - no copy is made template smart_ptr_clone& smart_ptr_clone::operator=(const smart_ptr_clone& r) { // // make it self-copy safe // if (this == &r) return *this; // make it alias-copy safe - this means that I don't try to do the // assignment if r is either the same object or an alias of it if (m_holder == r.m_holder) return *this; if(m_holder->decrement()) delete m_holder; m_holder = r.m_holder; m_holder->increment(); return *this; } // create a pointer containing a *copy* of the object // this copy is taken because the pointer class maintains a dynamically allocated object // and the T& may not be (usually is not) dynamically allocated // constructor form template smart_ptr_clone::smart_ptr_clone(T* data) : m_holder(new smart_ptr_holder) { m_holder->set(data); } // assignment form for an already-constructed smart-pointer template smart_ptr_clone& smart_ptr_clone::operator=(T* data) { m_holder->set(data); return *this; } // destructor decrements the reference count and delete only when the last reference is destroyed template smart_ptr_clone::~smart_ptr_clone(void) { if(m_holder->decrement()) delete m_holder; } ////////////////////////////////////////////////////////////////////////////// // logical tests to see if there is anything contained in the pointer since it can be null template bool smart_ptr_clone::null(void) const { return m_holder->null(); } template bool smart_ptr_clone::present(void) const { return !m_holder->null(); } template bool smart_ptr_clone::operator!(void) const { return m_holder->null(); } template smart_ptr_clone::operator bool(void) const { return !m_holder->null(); } ////////////////////////////////////////////////////////////////////////////// // dereference operators and functions template T& smart_ptr_clone::operator*(void) throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr_clone::operator*"); return m_holder->value(); } template const T& smart_ptr_clone::operator*(void) const throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr_clone::operator*"); return m_holder->value(); } template T* smart_ptr_clone::operator->(void) throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr_clone::operator->"); return m_holder->pointer(); } template const T* smart_ptr_clone::operator->(void) const throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr_clone::operator->"); return m_holder->pointer(); } ////////////////////////////////////////////////////////////////////////////// // explicit function forms of the above assignment dereference operators template void smart_ptr_clone::set_value(const T& data) { m_holder->set((T*)data.clone()); } template T& smart_ptr_clone::value(void) throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr_clone::value"); return m_holder->value(); } template const T& smart_ptr_clone::value(void) const throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr_clone::value"); return m_holder->value(); } template void smart_ptr_clone::set(T* data) { m_holder->set(data); } template T* smart_ptr_clone::pointer(void) { return m_holder->pointer(); } template const T* smart_ptr_clone::pointer(void) const { return m_holder->pointer(); } //////////////////////////////////////////////////////////////////////////////// // functions to manage counted referencing template bool smart_ptr_clone::aliases(const smart_ptr_clone& r) const { return !m_holder->null() && m_holder->pointer() == r.m_holder->pointer(); } template unsigned smart_ptr_clone::alias_count(void) const { return m_holder->count(); } template void smart_ptr_clone::make_unique(void) { if(m_holder->count() > 1) { smart_ptr_holder* old_holder = m_holder; m_holder = new smart_ptr_holder; old_holder->decrement(); if (old_holder->pointer()) m_holder->set((T*)(old_holder->pointer()->clone())); } } template void smart_ptr_clone::clear(void) { m_holder->clear(); } template void smart_ptr_clone::clear_unique(void) { if (m_holder->count() == 1) m_holder->clear(); else { m_holder->decrement(); m_holder = new smart_ptr_holder; } } template void smart_ptr_clone::copy(const smart_ptr_clone& data) { *this = data; make_unique(); } template smart_ptr_clone smart_ptr_clone::copy(void) const { smart_ptr_clone result; result.copy(*this); return result; } //////////////////////////////////////////////////////////////////////////////// // persistence methods // see also - external persistence functions // Like ordinary pointers they are dumped as an address and then, on the first // alias only, the contents. Subsequent aliases to the same object are dumped // as just an address. I use the address of the holder within smart pointer // since that is always there and is unique. Using the address of the object // would make all null pointers aliases of each other which may not be // correct. This requires burglarisation of the smart pointer class. // I've split these into the exported non-member functions and internal member // functions because I need access to the data structure but couldn't get // friend templates to work on VC // a smart_ptr_clone is used to point to clonable objects, so use the dump/restore for the clonable interface template void smart_ptr_clone::dump(dump_context& context) const throw(persistent_dump_failed) { // Many smart pointers can point to the same object. // I could have used the address of the object to differentiate, // but that would not have differentiated between different null smart pointers // so I use the address of the substructure to differentiate between different objects. // get a magic key for the substructure - this also returns a flag saying whether its been seen before std::pair mapping = context.pointer_map(m_holder); // dump the magic key ::dump(context,mapping.second); // dump the contents but only if this is the first time this object has been seen // use the existing routines for ordinary pointers to dump the contents if (!mapping.first) dump_interface(context,m_holder->pointer()); } template void smart_ptr_clone::restore(restore_context& context) throw(persistent_restore_failed) { // get the old substructure magic key unsigned magic = 0; ::restore(context,magic); // lookup this magic number to see if we have seen this already std::pair mapping = context.pointer_map(magic); if (mapping.first) { // this holder has already been restored // dealias the existing holder and replace it with the seen-before holder to make this object an alias of it if (m_holder->decrement()) delete m_holder; m_holder = (smart_ptr_holder*)mapping.second; m_holder->increment(); } else { // this is the first contact with this holder // make sure this smart pointer is unique to prevent side-effects clear_unique(); // map the magic key onto this structure's holder and then restore the contents context.pointer_add(magic,m_holder); restore_interface(context,m_holder->pointer()); } } //////////////////////////////////////////////////////////////////////////////// // comparisons required for using this class in an STL container template bool operator==(const smart_ptr_clone& left, const smart_ptr_clone& right) { // a null is not equal to a non-null but equal to another null if(!left || !right) return left.pointer() == right.pointer(); // shortcut - if the two pointers are equal then the objects must be equal if (left.pointer() == right.pointer()) return true; // otherwise compare the objects themselves return *left == *right; } template bool operator<(const smart_ptr_clone& left, const smart_ptr_clone& right) { // a null pointer is less than a non-null but equal to another null if(!left || !right) return left.pointer() < right.pointer(); // shortcut - if the two pointers are equal then the comparison must be false if (left.pointer() == right.pointer()) return false; // otherwise, compare the objects return *left < *right; } //////////////////////////////////////////////////////////////////////////////// // string/print utilities template std::string smart_ptr_clone_to_string(const smart_ptr_clone& ptr, std::string null_string) { if (!ptr) return null_string; return "*(" + to_string(*ptr) + ")"; } template otext& print_smart_ptr_clone(otext& str, const smart_ptr_clone& ptr, std::string null_string) { if (!ptr) return str << null_string; str << "*("; print(str, *ptr); str << ")"; return str; } template otext& print_smart_ptr_clone(otext& str, const smart_ptr_clone& ptr, unsigned indent, std::string null_string) { print_indent(str, indent); print_smart_ptr_clone(str, ptr, null_string); return str << endl; } //////////////////////////////////////////////////////////////////////////////// // Persistence - non-member functions actually called by the user template void dump_smart_ptr_clone(dump_context& context, const smart_ptr_clone& data) throw(persistent_dump_failed) { data.dump(context); } template void restore_smart_ptr_clone(restore_context& context, smart_ptr_clone& data) throw(persistent_restore_failed) { data.restore(context); } //////////////////////////////////////////////////////////////////////////////// // smart_ptr_nocopy class //////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // constructors, assignments and destructors // create a null pointer template smart_ptr_nocopy::smart_ptr_nocopy(void) : m_holder(new smart_ptr_holder) { } // copy constructor implements counted referencing - no copy is made template smart_ptr_nocopy::smart_ptr_nocopy(const smart_ptr_nocopy& r) : m_holder(0) { m_holder = r.m_holder; m_holder->increment(); } // assignment of smart pointers implement counted referencing - no copy is made template smart_ptr_nocopy& smart_ptr_nocopy::operator=(const smart_ptr_nocopy& r) { // // make it self-copy safe // if (this == &r) return *this; // make it alias-copy safe - this means that I don't try to do the // assignment if r is either the same object or an alias of it if (m_holder == r.m_holder) return *this; if(m_holder->decrement()) delete m_holder; m_holder = r.m_holder; m_holder->increment(); return *this; } // constructor form template smart_ptr_nocopy::smart_ptr_nocopy(T* data) : m_holder(new smart_ptr_holder) { m_holder->set(data); } // assignment form template smart_ptr_nocopy& smart_ptr_nocopy::operator=(T* data) { m_holder->set(data); return *this; } // destructor decrements the reference count and delete only when the last reference is destroyed template smart_ptr_nocopy::~smart_ptr_nocopy(void) { if(m_holder->decrement()) delete m_holder; } ////////////////////////////////////////////////////////////////////////////// // logical tests to see if there is anything contained in the pointer since it can be null template bool smart_ptr_nocopy::null(void) const { return m_holder->null(); } template bool smart_ptr_nocopy::present(void) const { return !m_holder->null(); } template bool smart_ptr_nocopy::operator!(void) const { return m_holder->null(); } template smart_ptr_nocopy::operator bool(void) const { return !m_holder->null(); } ////////////////////////////////////////////////////////////////////////////// // dereference operators and functions template T& smart_ptr_nocopy::operator*(void) throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr_nocopy::operator*"); return m_holder->value(); } template const T& smart_ptr_nocopy::operator*(void) const throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr_nocopy::operator*"); return m_holder->value(); } template T* smart_ptr_nocopy::operator->(void) throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr_nocopy::operator->"); return m_holder->pointer(); } template const T* smart_ptr_nocopy::operator->(void) const throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr_nocopy::operator->"); return m_holder->pointer(); } ////////////////////////////////////////////////////////////////////////////// // explicit function forms of the above assignment dereference operators template T& smart_ptr_nocopy::value(void) throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr_nocopy::value"); return m_holder->value(); } template const T& smart_ptr_nocopy::value(void) const throw(null_dereference) { if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr_nocopy::value"); return m_holder->value(); } template void smart_ptr_nocopy::set(T* data) { m_holder->set(data); } template T* smart_ptr_nocopy::pointer(void) { return m_holder->pointer(); } template const T* smart_ptr_nocopy::pointer(void) const { return m_holder->pointer(); } //////////////////////////////////////////////////////////////////////////////// // functions to manage counted referencing template bool smart_ptr_nocopy::aliases(const smart_ptr_nocopy& r) const { // aliases have the same holder return m_holder == r.m_holder(); } template unsigned smart_ptr_nocopy::alias_count(void) const { return m_holder->count(); } template void smart_ptr_nocopy::clear(void) { m_holder->clear(); } template void smart_ptr_nocopy::clear_unique(void) { if (m_holder->count() == 1) m_holder->clear(); else { m_holder->decrement(); m_holder = new smart_ptr_holder; } } //////////////////////////////////////////////////////////////////////////////// // persistence methods // a smart_ptr_nocopy is used to point to non-copyable objects, so it doesn't make sense to provide persistence! //////////////////////////////////////////////////////////////////////////////// // comparisons required for using this class in an STL container template bool operator==(const smart_ptr_nocopy& left, const smart_ptr_nocopy& right) { // a null is not equal to a non-null but equal to another null if(!left || !right) return left.pointer() == right.pointer(); // shortcut - if the two pointers are equal then the objects must be equal if (left.pointer() == right.pointer()) return true; // otherwise compare the objects themselves return *left == *right; } template bool operator<(const smart_ptr_nocopy& left, const smart_ptr_nocopy& right) { // a null pointer is less than a non-null but equal to another null if(!left || !right) return left.pointer() < right.pointer(); // shortcut - if the two pointers are equal then the comparison must be false if (left.pointer() == right.pointer()) return false; // otherwise, compare the objects return *left < *right; } //////////////////////////////////////////////////////////////////////////////// // string/print utilities template std::string smart_ptr_nocopy_to_string(const smart_ptr_nocopy& ptr, std::string null_string) { if (!ptr) return null_string; return "*(" + to_string(*ptr) + ")"; } template otext& print_smart_ptr_nocopy(otext& str, const smart_ptr_nocopy& ptr, std::string null_string) { if (!ptr) return str << null_string; str << "*("; print(str, *ptr); str << ")"; return str; } template otext& print_smart_ptr_nocopy(otext& str, const smart_ptr_nocopy& ptr, unsigned indent, std::string null_string) { print_indent(str, indent); print_smart_ptr_nocopy(str, ptr, null_string); return str << endl; } ////////////////////////////////////////////////////////////////////////////////