/* * smart_pointer.h version 3 - Storage policy added. * * A smart pointer type implementing deep copy. * * SUPPORT FOR STORAGE POLICY ADDED: * * The raw pointer and copy() member function is removed from smart_pointer, * and instead implemented by the storage policy classes, default_storage * and array_storage. * * Template parameter storage_policy, with default_storage as default. * * Access to the pointer is now done via the storage policy interface. * * SUPPORT FOR CHECKING POLICY ADDED BEFORE: * * Checking policy classes no_check, reject_null_deref, reject_null, * assert_check_deref and assert_check. * * Exception null_pointer_exception, thrown by reject_null_deref and * reject_null. * * Template parameter checking_policy, with no_check as default argument. * * SUPPORT FOR CONVERSION POLICY ADDED BEFORE: * * Template select_type is added and used for selecting one of two types at * compile-time. * * Conversion policy classes allow_conversion and disallow_conversion. * * Template parameter conversion_policy, with disallow_conversion as default * argument. * * Private helper class disallow, to which the raw pointer ptr_ cannot be * converted and therefore disallowing implicit type conversion when used * as return type for the type conversion operator, se below. * * Type conversion operator, converting to either T* or disallow depending * on conversion_policy and selected by using select_type. */ #ifndef SMART_POINTER_H #define SMART_POINTER_H #include #include /******************************************************************************* * TYPE SELECTION ******************************************************************************/ /* * select_type selects one of two types based upon a boolean constant. flag is a * compile-time boolean constant, and if 'true', type T1 is selected, else type * T2 is selected. * * The primary is instantiated for flag equals true, since a specialization for * flag equals false is defined. */ template struct select_type { typedef T1 result; }; template struct select_type { typedef T2 result; }; /******************************************************************************* * CONVERSION POLICY ******************************************************************************/ /* * allow_conversion allow conversion to the pointee type. */ struct allow_conversion { static const bool allow = true; }; /* * disallow_conversion does not allow conversion to the pointee type. */ struct disallow_conversion { static const bool allow = false; }; /******************************************************************************* * CHECKING POLICY ******************************************************************************/ /* * no_check does not perform any checks. */ struct no_check { template static void on_default(const P) {} template static void on_init(const P) {} template static void on_dereference(const P) {} }; /* * null_pointer_exception is used by some implementations of checking_policy. */ class null_pointer_exception : public std::runtime_error { public: explicit null_pointer_exception() noexcept : std::runtime_error("null pointer exception") {} }; /* * reject_null_dereference throws null_pointer_exception if an attempt is made to * dereference or indirect a null smart pointer. */ struct reject_null_dereference { template static void on_default(const P) {} template static void on_init(const P) {} template static void on_dereference(const P p) { if (!p) throw null_pointer_exception(); } }; /* * reject_null throws null_pointer_exception if an attempt is made to initialize or * assign a smart pointer to null, or to dereference or indirect a null smart pointer. */ struct reject_null { template static void on_default(const P p) { on_init(p); } template static void on_init(const P p) { if (!p) throw null_pointer_exception(); } template static void on_dereference(const P p) { on_init(p); } }; /* * assert_check_dereference checks the pointer before dereference and indirect. */ struct assert_check_dereference { template static void on_default(const P) {} template static void on_init(const P) {} template static void on_dereference(const P p) { assert(p); } }; /* * assert_check_dereference checks the pointer before initialization, assignment, * dereference and indirect. */ struct assert_check { template static void on_default(const P p) { assert(p); } template static void on_init(const P p) { assert(p); } template static void on_dereference(const P p) { assert(p); } }; /* * assert_check_deref checks the pointer against zero upon initialization and * assignment, and before dereference and indirect. */ /******************************************************************************* * STORAGE POLICIY ******************************************************************************/ /* * default_storage implements an interface for operating on the implementation. */ template class default_storage { public: using pointer_type = T*; using reference_type = T&; default_storage() : ptr_{ default_value() } {} default_storage(const pointer_type& p) : ptr_{ p } {} default_storage(const default_storage& rhs) : ptr_{ copy(rhs) } {} reference_type operator*() const { return *ptr_; } pointer_type operator->() const { return ptr_; } friend inline pointer_type get_impl(const default_storage& sp) { return sp.ptr_; } friend inline const pointer_type& get_impl_ref(const default_storage& sp) { return sp.ptr_; } friend inline pointer_type& get_impl_ref(default_storage& sp) { return sp.ptr_; } pointer_type copy(const default_storage& s) { return s.ptr_ ? new T{ *s.ptr_ } : nullptr; } protected: void destroy() { delete ptr_; } static pointer_type default_value() { return nullptr; } private: pointer_type ptr_{ nullptr }; }; /******************************************************************************* * SMART POINTER. ******************************************************************************/ template class storage_policy = default_storage > class smart_pointer : public storage_policy { public: using SP = storage_policy; using pointer_type = typename SP::pointer_type; using reference_type = typename SP::reference_type; smart_pointer() { checking_policy::on_default(get_impl(*this)); } smart_pointer(pointer_type p) : SP{ p } { checking_policy::on_init(get_impl(*this)); } smart_pointer(const smart_pointer& rhs) : SP{ rhs } { checking_policy::on_init(get_impl(rhs)); } ~smart_pointer() { this->destroy(); } smart_pointer& operator=(const smart_pointer& rhs) & { if (this != &rhs) { checking_policy::on_init(get_impl(rhs)); this->destroy(); get_impl_ref(*this) = this->copy(rhs); } return *this; } reference_type operator*() const { checking_policy::on_dereference(get_impl_ref(*this)); return SP::operator*(); } reference_type operator*() { checking_policy::on_dereference(get_impl_ref(*this)); return SP::operator*(); } pointer_type operator->() const { checking_policy::on_dereference(get_impl_ref(*this)); return SP::operator->(); } pointer_type operator->() { checking_policy::on_dereference(get_impl_ref(*this)); return SP::operator->(); } private: // Helper class for disallowing implicit type conversion. class disallowed {}; // If implicit type conversion is allowed, 'implicit_conversion_type' // will be defined as 'pointer_type', else as 'disallowed'. using implicit_conversion_type = typename select_type::result; public: // If implicit type conversion is allowed, 'implicit_conversion_type' // will be 'pointer_type', otherwise 'implicit_conversion_type' will // be 'disallowed'. Since there is no conversion from 'pointer_type' // to 'disallowed', it will result in a compile-time error. operator implicit_conversion_type() const { return get_impl(*this); } }; #endif