blob: 6cec3e855c882123ea6d453789a97b3c4ded16c7 [file] [log] [blame]
#ifndef BUTIL_INTRUSIVE_PTR_HPP
#define BUTIL_INTRUSIVE_PTR_HPP
// Copyright (c) 2001, 2002 Peter Dimov
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// See http://www.boost.org/libs/smart_ptr/intrusive_ptr.html for documentation.
//
// intrusive_ptr
//
// A smart pointer that uses intrusive reference counting.
//
// Relies on unqualified calls to
//
// void intrusive_ptr_add_ref(T * p);
// void intrusive_ptr_release(T * p);
//
// (p != 0)
//
// The object is responsible for destroying itself.
#include <functional>
#include <cstddef>
#include <ostream>
#include "butil/build_config.h"
#include "butil/containers/hash_tables.h"
namespace butil {
namespace detail {
// NOTE: sp_convertible is different from butil::is_convertible
// (in butil/type_traits.h) that it converts pointers only. Using
// butil::is_convertible results in ctor/dtor issues.
template< class Y, class T > struct sp_convertible {
typedef char (&yes) [1];
typedef char (&no) [2];
static yes f( T* );
static no f( ... );
enum _vt { value = sizeof((f)(static_cast<Y*>(0))) == sizeof(yes) };
};
template< class Y, class T > struct sp_convertible<Y, T[]> {
enum _vt { value = false };
};
template< class Y, class T > struct sp_convertible<Y[], T[]> {
enum _vt { value = sp_convertible<Y[1], T[1]>::value };
};
template<class Y, std::size_t N, class T> struct sp_convertible<Y[N], T[]> {
enum _vt { value = sp_convertible<Y[1], T[1]>::value };
};
struct sp_empty {};
template< bool > struct sp_enable_if_convertible_impl;
template<> struct sp_enable_if_convertible_impl<true> { typedef sp_empty type; };
template<> struct sp_enable_if_convertible_impl<false> {};
template< class Y, class T > struct sp_enable_if_convertible
: public sp_enable_if_convertible_impl<sp_convertible<Y, T>::value> {};
} // namespace detail
template<class T> class intrusive_ptr {
private:
typedef intrusive_ptr this_type;
public:
typedef T element_type;
intrusive_ptr() BAIDU_NOEXCEPT : px(0) {}
intrusive_ptr(T * p, bool add_ref = true): px(p) {
if(px != 0 && add_ref) intrusive_ptr_add_ref(px);
}
template<class U>
intrusive_ptr(const intrusive_ptr<U>& rhs,
typename detail::sp_enable_if_convertible<U,T>::type = detail::sp_empty())
: px(rhs.get()) {
if(px != 0) intrusive_ptr_add_ref(px);
}
intrusive_ptr(const intrusive_ptr& rhs): px(rhs.px) {
if(px != 0) intrusive_ptr_add_ref(px);
}
~intrusive_ptr() {
if(px != 0) intrusive_ptr_release(px);
}
template<class U> intrusive_ptr & operator=(const intrusive_ptr<U>& rhs) {
this_type(rhs).swap(*this);
return *this;
}
// Move support
#if defined(BUTIL_CXX11_ENABLED)
intrusive_ptr(intrusive_ptr && rhs) BAIDU_NOEXCEPT : px(rhs.px) {
rhs.px = 0;
}
intrusive_ptr & operator=(intrusive_ptr && rhs) BAIDU_NOEXCEPT {
this_type(static_cast< intrusive_ptr && >(rhs)).swap(*this);
return *this;
}
#endif
intrusive_ptr & operator=(const intrusive_ptr& rhs) {
this_type(rhs).swap(*this);
return *this;
}
intrusive_ptr & operator=(T * rhs) {
this_type(rhs).swap(*this);
return *this;
}
void reset() BAIDU_NOEXCEPT {
this_type().swap(*this);
}
void reset(T * rhs) {
this_type(rhs).swap(*this);
}
void reset(T * rhs, bool add_ref) {
this_type(rhs, add_ref).swap(*this);
}
T * get() const BAIDU_NOEXCEPT {
return px;
}
T * detach() BAIDU_NOEXCEPT {
T * ret = px;
px = 0;
return ret;
}
T & operator*() const {
return *px;
}
T * operator->() const {
return px;
}
// implicit conversion to "bool"
#if defined(BUTIL_CXX11_ENABLED)
explicit operator bool () const BAIDU_NOEXCEPT {
return px != 0;
}
#elif defined(__CINT__)
operator bool () const BAIDU_NOEXCEPT {
return px != 0;
}
#elif (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ < 304))
typedef element_type * (this_type::*unspecified_bool_type)() const;
operator unspecified_bool_type() const BAIDU_NOEXCEPT {
return px == 0? 0: &this_type::get;
}
#else
typedef element_type * this_type::*unspecified_bool_type;
operator unspecified_bool_type() const BAIDU_NOEXCEPT {
return px == 0? 0: &this_type::px;
}
#endif
// operator! is redundant, but some compilers need it
bool operator! () const BAIDU_NOEXCEPT {
return px == 0;
}
void swap(intrusive_ptr & rhs) BAIDU_NOEXCEPT {
T * tmp = px;
px = rhs.px;
rhs.px = tmp;
}
private:
T * px;
};
template<class T, class U>
inline bool operator==(const intrusive_ptr<T>& a, const intrusive_ptr<U>& b) {
return a.get() == b.get();
}
template<class T, class U>
inline bool operator!=(const intrusive_ptr<T>& a, const intrusive_ptr<U>& b) {
return a.get() != b.get();
}
template<class T, class U>
inline bool operator==(const intrusive_ptr<T>& a, U * b) {
return a.get() == b;
}
template<class T, class U>
inline bool operator!=(const intrusive_ptr<T>& a, U * b) {
return a.get() != b;
}
template<class T, class U>
inline bool operator==(T * a, const intrusive_ptr<U>& b) {
return a == b.get();
}
template<class T, class U>
inline bool operator!=(T * a, const intrusive_ptr<U>& b) {
return a != b.get();
}
#if __GNUC__ == 2 && __GNUC_MINOR__ <= 96
// Resolve the ambiguity between our op!= and the one in rel_ops
template<class T>
inline bool operator!=(const intrusive_ptr<T>& a, const intrusive_ptr<T>& b) {
return a.get() != b.get();
}
#endif
#if defined(BUTIL_CXX11_ENABLED)
template<class T>
inline bool operator==(const intrusive_ptr<T>& p, std::nullptr_t) BAIDU_NOEXCEPT {
return p.get() == 0;
}
template<class T>
inline bool operator==(std::nullptr_t, const intrusive_ptr<T>& p) BAIDU_NOEXCEPT {
return p.get() == 0;
}
template<class T>
inline bool operator!=(const intrusive_ptr<T>& p, std::nullptr_t) BAIDU_NOEXCEPT {
return p.get() != 0;
}
template<class T>
inline bool operator!=(std::nullptr_t, const intrusive_ptr<T>& p) BAIDU_NOEXCEPT {
return p.get() != 0;
}
#endif // BUTIL_CXX11_ENABLED
template<class T>
inline bool operator<(const intrusive_ptr<T>& a, const intrusive_ptr<T>& b) {
return std::less<T *>()(a.get(), b.get());
}
template<class T> void swap(intrusive_ptr<T> & lhs, intrusive_ptr<T> & rhs) {
lhs.swap(rhs);
}
// mem_fn support
template<class T> T * get_pointer(const intrusive_ptr<T>& p) {
return p.get();
}
template<class T, class U> intrusive_ptr<T> static_pointer_cast(const intrusive_ptr<U>& p) {
return static_cast<T *>(p.get());
}
template<class T, class U> intrusive_ptr<T> const_pointer_cast(const intrusive_ptr<U>& p) {
return const_cast<T *>(p.get());
}
template<class T, class U> intrusive_ptr<T> dynamic_pointer_cast(const intrusive_ptr<U>& p) {
return dynamic_cast<T *>(p.get());
}
template<class Y> std::ostream & operator<< (std::ostream & os, const intrusive_ptr<Y>& p) {
os << p.get();
return os;
}
} // namespace butil
// hash_value
namespace BUTIL_HASH_NAMESPACE {
#if defined(COMPILER_GCC)
template<typename T>
struct hash<butil::intrusive_ptr<T> > {
std::size_t operator()(const butil::intrusive_ptr<T>& p) const {
return hash<T*>()(p.get());
}
};
#elif defined(COMPILER_MSVC)
template<typename T>
inline size_t hash_value(const butil::intrusive_ptr<T>& sp) {
return hash_value(p.get());
}
#endif // COMPILER
} // namespace BUTIL_HASH_NAMESPACE
#endif // BUTIL_INTRUSIVE_PTR_HPP