/* * smart_pointer.h version 2 - Checking policy added. * * A simple smart pointer type implementing deep copy. * * SUPPORT FOR CHECKING POLICY ADDED: * * 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 EARLIER: * * 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. */ /******************************************************************************* * SMART POINTER ******************************************************************************/ template class smart_pointer { public: smart_pointer() = default; smart_pointer(T* p) : ptr_{ p } { checking_policy::on_init(ptr_); } smart_pointer(const smart_pointer& rhs) { checking_policy::on_init(rhs.ptr_); ptr_ = copy(rhs); } ~smart_pointer() { delete ptr_; } smart_pointer& operator=(const smart_pointer& rhs) & { if (this != &rhs) { checking_policy::on_init(rhs.ptr_); T* ptr{ copy(rhs) }; delete ptr_; ptr_ = ptr; } return *this; } T& operator*() const { checking_policy::on_dereference(ptr_); return *ptr_; } T& operator*() { checking_policy::on_dereference(ptr_); return *ptr_; } T* operator->() const { checking_policy::on_dereference(ptr_); return ptr_; } T* operator->() { checking_policy::on_dereference(ptr_); return ptr_; } 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 ptr_; } private: T* ptr_{ nullptr }; T* copy(const smart_pointer& sp) { return sp.ptr_ ? new T{ *sp.ptr_ } : nullptr; } }; #endif