pachi_py/ptr.hpp (259 lines of code) (raw):

/** * @file ptr.hpp * * A stand-along general-purpose non-intrusive reference-counted pointer. * * Based on the boost::shared_ptr and yasper smart pointer libraries. * * @version 1.0 * @author Kevin McGuinness (kevin.mcguinness {at} gmail {dot} com) * @date 2010-04-02 (Last Modified) * * * @verbatim * ---------------------------------------------------------------------------- * Copyright 2007 Kevin McGuinness * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ---------------------------------------------------------------------------- * @endverbatim */ #ifndef PTR_HPP_INCLUDED #define PTR_HPP_INCLUDED #include <algorithm> #include <cassert> #include <new> #ifndef SMART_PTR_NO_OSTREAM_OPERATOR #include <iostream> #endif // Supress Intel C++ warning #if defined(__ICC) #pragma warning(disable:981) #endif /// smart pointer namespace namespace smart { // Implementation details for static assert namespace detail { // Templates for the static assert macro template <bool x> struct compile_time_check; template <> struct compile_time_check<true> { }; template <int x> struct check { }; } /* namespace detail */ /// Static assertation macro for compile time checks. #define ptr_hpp_static_assert(T) \ typedef detail::check< sizeof( detail::compile_time_check<(bool) (T)> ) > \ ptr_hpp_static_assertion_##__LINE__; // Implementation details for ptr<T> namespace detail { // Template metaprogramming stuff to check if a type is complete template <class T> struct is_complete { static const bool value = (sizeof(T) ? true : false); }; // Template metaprogramming stuff for compile time array notation detection template <class T> struct is_array { static const bool value = false; }; template <class T> struct is_array<T[]> { static const bool value = true; }; // Template metaprogramming stuff for extracting underlying type from an array template <class T> struct get_type { typedef T value; }; template <class T> struct get_type<T[]> { typedef T value; }; // Checked delete of normal types template <class T> struct deleter { static void free(T* v) { // Prevent deleting incomplete types ptr_hpp_static_assert(is_complete<T>::value); // Delete delete v; } }; // Checked delete of array types template <class T> struct deleter<T[]> { template <class U> static void free(U* v) { // Prevent deleting incomplete types ptr_hpp_static_assert(is_complete<U>::value); // Array delete delete[] v; } }; } /* namespace detail */ /** * @brief Stand-alone general-purpose non-intrusive reference-counted * pointer template. * * * Based on the boost::shared_ptr and yasper libraries. * * * @b Requirements: * - Contained type destructor or delete operator must @b NEVER throw! * - Copy constructor must be provided if you want to use clone() * * * @b Features: * - Single header file "ptr.hpp": no dependancies on other libraries * - Well documented, with several examples * - Well tested with unit tests * - Safer to use than yasper (no raw pointer assignment allowed) * - Fast: accesses are unchecked in release mode * - Small: stack size of 2 pointers, heap overhead one int * - Handles array types correctly using the ptr<T[]> x(new T[100]) notation * - Handles array indexing using operator [] * - Includes compile time checks to prevent deleting of incomplete types * - Compile time guards for operator -> on arrays * - Compile time guards for operator [] on non-arrays * - Debug mode run time checks for null pointer dereferencing * - Easily clone pointers to non array types using clone() * - Easy to type :-D * * * @b Limitations: * - Not thread safe * - Using incorrect form for array (ex. <tt>ptr< T > p(new T[100])</tt>) is * not detected (language constraint). * * * @b Usage: * * Creating pointers: * @code * // Create a pointer to an int with value 5 * ptr<int> p(new int(5)); * * // Create a pointer to an object of type X * ptr<X> q(new X); * * // Create a null pointer to an object of type Y * ptr<Y> r; * * // Create a pointer to an array * ptr<char[]> s(new char[100]); * @endcode * * * Dereferencing the pointer: * @code * // Assign value of pointer to 3 * *p = 3; * * // Increment value of pointer * (*p)++; * * // Print pointer value * cout << *p << endl; * @endcode * * * Calling member functions of objects: * @code * // Assuming X has a function func() * q->func(); * @endcode * * * Indexing array elements: * @code * s[0] = '?'; * @endcode * * * Check if pointer is null: * @code * // Explicit syntax * if (p.null()) { * //... * } * * // Implicit syntax * if (!p) { * //... * } * @endcode * * * Check if pointer is valid: * @code * // Explicit syntax * if (p.valid()) { * //... * } * * // Implicit syntax * if (p) { * //... * } * @endcode * * * Passing the raw pointer to a legacy function: * @code * // Legacy function * void func(X* v) { * v->func(); * } * * // Pass raw pointer * func(q.get()); * * // Or the shorthand notation * func(+q); * @endcode * * * Using @b STL algorithms: * @code * // Create an array of n elements * int n = 100; * ptr<int[]> x(new int[n]); * * // Fill it with zeros * std::fill(+x, +x+n, 0); * @endcode * * * Aliasing the pointer value: * @code * // Create pointer alias x * ptr<int> x(p); * * // Create null pointer y * ptr<int> y; * * // Set x to y * y = x; * * // Reset y (set to null) * y.reset() * * // Ok, y is null but x is still in scope * cout << *x << endl; * @endcode * * * Return the pointer from a function: * @code * ptr< vector<int> > create_vector(int elements) { * // Factory method simplifies creation * return mkptr(new vector<int>(elements)); * } * @endcode * * * Determine if we have a unique reference: * @code * if (p.unique()) { * // ... * } * @endcode * * * Determine the reference count: * @code * int refs = p.count(); * @endcode * * * Clone the value pointed to by the pointer: * @code * // Requires copy constructor in X * ptr<X> c(q.clone()); * @endcode * * * Reset the pointer to own a different value: * @code * // Releases any old resource before aquiring new one * p.reset(new int(10)); * @endcode * * * Swap pointer values: * @code * ptr<int> a(new int(1)); * ptr<int> b(new int(2)); * * // Swap pointers, now *a = 2 , *b = 1 * swap(a, b); * * // Alternative notation * a.swap(b); * @endcode * * * @b Polymorphism is supported: * @code * struct A { * virtual ~A() { } * virtual void func() const { cout << "A" << endl; } * }; * * struct B : public A { * virtual ~B() { } * virtual void func() const { cout << "B" << endl; } * void bfunc() const { } * }; * * // Create polymorphic pointer * ptr<A> a(new B); * * // prints "B" * a->func(); * * // Downcast pointer * ptr<B> b(ptr_dynamic_cast<B>(a)); * * // Call function unique to B * p->bfunc(); * * // Can copy construct and assign too * ptr<A> c(b); * @endcode * * <b> Handling Constness </b> * * Handling constness is a little different than you might expect, but it * actually makes sense if you think about it. If you want a function to * accept a pointer to a const int, for example, declare it as the following: * * @code * // Use this (note that we're not passing by reference) * void func(ptr<const int> p) { * // read only *p * } * * // rather than this... * void func(const ptr<int>& p) { * // can write to *p!, the ptr<> is const! * } * @endcode * * Pass by reference may not be used in the first function declaration: the * compiler is unable to generate the necessary temporary. Using the first * function incurs a small overhead (the copy construction); for functions * where speed is critical functions, a raw pointer may be more appropriate: * * @code * // Speed critical function * void func(const int* p) { * // read only *p * } * * ptr<int> x(new int(5)); * * // Now use the unary + operator to get the raw pointer * func(+x); * * // Same technique applies for arrays * @endcode * * * @b Compilers (tested on): * - GNU C++ Compiler (GCC 4.1.2) * - Intel C++ Compiler (ICC 10.0.023) * - Microsoft Visual C++ Compiler 8.0 (MSVC8) * * @author * Kevin McGuinness * * @version * 1.0.2 * * @date * 2010-04-02 (Last Modified) * */ template <class T> struct ptr { /// All types of this template are friends template <class U> friend class ptr; /// Type of contained element typedef typename detail::get_type< T >::value element_t; /// Static constant indicating whether or not the contained element is an array static const bool is_array = detail::is_array< T >::value; /** * Null pointer constructor. * * @throws * Never throws. * * @note @e Implementation: Having an extra constructor is more efficient * than defaulting the parameter of the next constructor to zero when * creating null pointers. */ ptr() : px(0), pn(0) { } /** * Standard constructor. * * This constructor is explicit in order to prevent unintentional conversion * from a raw pointer to a @c ptr<T> pointer, which could result in the raw * pointer being prematurely deleted. For example, if this constructor was * not explicit, and you passed a raw pointer to a function expecting a ptr * object, then the raw pointer would be converted to a ptr implicitly and * deleted when the function returns. This means that you must construct * ptr objects using the following syntax: * * @code * // Correct syntax * ptr<T> x(new T); * * // Incorrect syntax - wont compile! * ptr<T> x = new T; * @endcode * * * @throws * std::bad_alloc In the unlikely event that the counter is not * successfully allocated. * */ explicit ptr(element_t* p) : px(p), pn(0) { aquire(); } /** * Copy constructor. * * Copy the pointer and increment the shared reference count if the passed * pointer is not null. * * @throws * std::bad_alloc In the unlikely event that the counter is not * successfully allocated. * */ ptr(const ptr& x) : px(x.px), pn(x.pn) { aquire(); } /// Conversion copy constructor template <class U> ptr(const ptr<U>& x) : px(x.px), pn(x.pn) { aquire(); } /** * Destructor. * * If the pointer is not null, decrement the reference count and if the * reference count then equals zero, free the pointer and shared count. * * @throws * Never throws. * */ ~ptr() { release(); } /** * Assignment. * * Assign the pointer to this if it is not already the same object. The * assignment decrements the reference counter for the already contained * object in the reciever if it is not null. The pointer and reference * count are then assigned and the reference count is incremented. * * @throws * Never throws. */ ptr& operator = (const ptr& x) { assign(x); return *this; } /// Conversion assignment template <class U> ptr& operator = (const ptr<U>& x) { assign(x); return *this; } /** * Indirection operator (non arrays only). * Asserts the pointer is valid in debug mode. * * @throws * Never throws. */ inline element_t* operator -> () const { ptr_hpp_static_assert (!is_array); assert (px != 0); return px; } /** * Dereference operator. * Asserts the pointer is valid in debug mode. * * @throws * Never throws. */ inline element_t& operator * () const { assert (px != 0); return *px; } /** * Array element access operator (arrays only). * Asserts valid in debug mode. * * @throws * Never throws. */ inline element_t& operator[] (int idx) const { ptr_hpp_static_assert(is_array); assert (px != 0); return px[idx]; } /** * Unary operator+: returns the raw pointer. * * This allows a shorthand notation for passing the raw pointer to a function * that expects a raw pointer. * * @note Why the '+' operator? It looks nice and has high precedence. * * @throws * Never throws. */ inline element_t* operator + () const { return px; } /** * Conversion to bool: converts to true if not null. * * @throws * Never throws. */ inline operator bool () const { return px != 0; } /** * Not operator: returns false if null. * * @throws * Never throws. */ inline bool operator ! () const { return px == 0; } /** * Equality operator: compares values of the raw pointers. * * @throws * Never throws. */ template <class U> bool operator == (const ptr<U>& x) const { return px == x.px; } /** * Inequality operator: compares values of raw pointers. * * @throws * Never throws. */ template <class U> bool operator != (const ptr<U>& x) const { return px != x.px; } /** * Less than operator (for containers). * * @throws * Never throws. */ template <class U> bool operator < (const ptr<U>& x) const { return px < x.px; } /** * Returns the raw pointer. * * @throws * Never throws. */ element_t* get() const { return px; } /** * Returns true if valid (non null) * * @throws * Never throws. */ bool valid() const { return px != 0; } /** * Returns true if the pointer is null (invalid). * * @throws * Never throws. */ bool null() const { return px == 0; } /** * Returns the reference count, which is always zero for a null pointer. * * @throws * Never throws. */ int count() const { return pn ? *pn : 0; } /** * Returns true if reference count is one or pointer is null. * * @throws * Never throws. */ bool unique() const { return px ? count() == 1 : true; } /** * Reset the pointer: set null. * * @throws * Never throws. */ void reset() { ptr<T>().swap(*this); } /** * Reset the pointer to the given value. * * In debug mode this function asserts that if the passed pointer is not 0, * it is not equal to the contained pointer in debug mode. This helps to * catch self reset errors. * * @throws * std::bad_alloc In the unlikely event that the counter is not * successfully allocated. */ void reset(element_t* x) { assert(x == 0 || x != px); ptr<T>(x).swap(*this); } /** * Clone the element held by the pointer, returning a pointer to the new * element. Requires the contained element class to be copy-constructable. * Does not apply to arrays. * * @throws * ... If the contained element type's copy constructor throws then * the exception is propagated. */ ptr<T> clone() const { ptr_hpp_static_assert(!is_array); assert(px != 0); return ptr<T>(new T(*px)); } /** * Swap the pointer with another. * * @throws * Never throws. */ void swap(ptr& x) { std::swap(px, x.px); std::swap(pn, x.pn); } /** * Assign the pointer. * * @throws * Never throws. */ void assign(const ptr& x) { if (this != &x) { release(); px = x.px; pn = x.pn; // faster than if(pn) (*pn)++ pn && (*pn)++; } } /** * Assign the pointer (convert version). * * @throws * Never throws. */ template <class U> void assign(const ptr<U>& x) { if (this != (ptr<T>*) &x) { release(); px = x.px; pn = x.pn; // faster than if(pn) (*pn)++ pn && (*pn)++; } } /// Cast pointer (c style cast) template <class U, class V> friend ptr<U> ptr_cast(const ptr<V>& x); /// Static cast pointer template <class U, class V> friend ptr<U> ptr_static_cast(const ptr<V>& x); /// Dynamic cast pointer template <class U, class V> friend ptr<U> ptr_dynamic_cast(const ptr<V>& x); /// Const cast pointer template <class U, class V> friend ptr<U> ptr_const_cast(const ptr<V>& x); /// Reinterpret cast pointer template <class U, class V> friend ptr<U> ptr_reinterpret_cast(const ptr<V>& x); private: /// Type of the counter typedef unsigned int counter_t; /// Internal constructor inline ptr(element_t* x, counter_t* n) : px(x), pn(n) { aquire(); } /// Dereference pointer inline void release() { if (pn && --(*pn) == 0) { delete pn; // Calls correct delete function detail::deleter<T>::free(px); } px = 0, pn = 0; } /// Refrerence pointer inline void aquire() { if (px) { if (pn) { // Counter exists ++(*pn); } else { // Create counter create_pn(); } } } // Create counter inline void create_pn() { // Allocate counter and surpress throw pn = new (std::nothrow) counter_t(1); if (!pn) { // Free element detail::deleter<T>::free(px); px = 0; pn = 0; // Throw throw std::bad_alloc(); } } private: // Raw pointer element_t* px; // Reference count counter_t* pn; }; #ifndef SMART_PTR_NO_OSTREAM_OPERATOR /// Stream insertion operator template <typename T> std::ostream& operator << (std::ostream& out, const ptr<T>& x) { out << "smart::ptr<T>("; if (x.null()) { out << "NULL)"; } else { out << x.get() << ",refs=" << x.count() << ")"; } return out; } #endif /// Swap pointers template<class T> inline void swap(ptr<T>& a, ptr<T>& b) { a.swap(b); } /// General pointer cast template <class U, class T> inline ptr<U> ptr_cast(const ptr<T>& x) { typedef typename ptr<U>::element_t* cast_t; return ptr<U>((cast_t) (x.get()), x.pn); } /// Static pointer cast template <class U, class T> inline ptr<U> ptr_static_cast(const ptr<T>& x) { typedef typename ptr<U>::element_t* cast_t; return ptr<U>(static_cast<cast_t>(x.get()), x.pn); } /// Dynamic pointer cast template <class U, class T> inline ptr<U> ptr_dynamic_cast(const ptr<T>& x) { typedef typename ptr<U>::element_t* cast_t; return ptr<U>(dynamic_cast<cast_t>(x.get()), x.pn); } /// Const pointer cast template <class U, class T> inline ptr<U> ptr_const_cast(const ptr<T>& x) { typedef typename ptr<U>::element_t* cast_t; return ptr<U>(const_cast<cast_t>(x.get()), x.pn); } /// Reinterpret pointer cast template <class U, class T> inline ptr<U> ptr_reinterpret_cast(const ptr<T>& x) { typedef typename ptr<U>::element_t* cast_t; return ptr<U>(reinterpret_cast<cast_t>(x.get()), x.pn); } /// Factory function template <class T> inline ptr<T> mkptr(T* v) { return ptr<T>(v); } /// Factory function, calls default constructor template <class T> inline ptr<T> mkptr() { return ptr<T>(new T); } } /* end namespace smart */ #endif /* PTR_HPP_INCLUDED */