CGII/framework/include/cgv/data/ref_ptr.h
2018-05-17 16:01:02 +02:00

246 lines
7.1 KiB
C++

#pragma once
#include "ref_counted.h"
#include <cgv/defines/assert.h>
#include <cgv/type/cond/is_base_of.h>
#include <cgv/type/cond/has_virtual_destructor.h>
#include <assert.h>
#include "lib_begin.h"
namespace cgv {
/// namespace for data management components
namespace data {
// extern CGV_API bool validate_delete(const void* ptr);
template <class T, bool is_ref_counted>
class ref_ptr;
/// struct used to make ref pointers to ref_counted friends of ref_counted
class ref_ptr_tag
{
protected:
/// increment the count of a ref counted object
void inc_ref_count(const ref_counted* ptr) const
{
ptr->set_ref_count(ptr->get_ref_count()+1);
}
/// decrement the count of a ref counted object and return whether to delete the object
bool dec_ref_count(const ref_counted* ptr) const
{
int count = ptr->get_ref_count();
if (count > 0) {
ptr->set_ref_count(count-1);
return count == 1;
}
// ERROR: zero ref count decremented
assert(0);
return false;
}
};
template <typename T, bool is_ref_counted = false>
class ref_ptr_impl
{
protected:
friend class ref_ptr_impl<const T, is_ref_counted>;
/// struct to store the pointer with a count
struct counter_type
{
counter_type(T* p, int c) : ptr(p), count(c) {}
T* ptr;
int count;
};
/// store pointer to counter struct
mutable counter_type* counter;
/// decrement the count, delete if it is 0
void release()
{
if (counter) {
if (--counter->count == 0) {
delete counter->ptr;
counter->ptr = 0;
delete counter;
}
counter = 0;
}
}
/// return the pointer itself
T* ref () const { return counter ? counter->ptr : 0; }
/// construct reference counted pointer
explicit ref_ptr_impl(T* p = 0) : counter(0) {
if (p)
counter = new counter_type(p, 1);
}
/// copy construct from same pointer type and increment count
ref_ptr_impl(const ref_ptr_impl<T,false>& r) : counter(r.counter)
{
if (counter)
++counter->count;
}
/// copy construct from pointer of derived type with virtual destructor
template <typename S>
ref_ptr_impl(const ref_ptr_impl<S,false>& s)
{
// ref_ptr conversion only valid if T is base of S
CGV_DEFINES_ASSERT(type::cond::is_base_of<T,S>::value);
// and T has a virtual destructor
CGV_DEFINES_ASSERT(type::cond::has_virtual_destructor<T>::value);
// after validity checks, set pointer
counter = reinterpret_cast<counter_type*>(s.counter);
// and increment reference count
if (counter)
++counter->count;
}
public:
/// return current count
int get_count() const { return counter ? counter->count : 0; }
};
template <typename T>
class ref_ptr_impl<T,true> : public ref_ptr_tag
{
T* ptr;
protected:
/// if the pointer had been initialized before, decrement reference count and release pointer, if necessary delete instance
void release() {
if (ptr) {
if (dec_ref_count(ptr)) {
// if (validate_delete(ptr))
delete ptr;
}
ptr = 0;
}
}
/// return the pointer itself
T* ref () const {
return ptr;
}
/// construct from pointer and increment reference count
explicit ref_ptr_impl(T* p) : ptr(p) {
if (ptr)
inc_ref_count(ptr);
}
/// copy construct from same pointer type and increment count
ref_ptr_impl(const ref_ptr_impl<T,true>& r) : ptr(r.ref())
{
if (ptr)
inc_ref_count(ptr);
}
/// copy construct from pointer of derived type with virtual destructor
template <typename S>
ref_ptr_impl(const ref_ptr_impl<S,true>& s)
{
// ref_ptr conversion only valid if T is base of S
CGV_DEFINES_ASSERT(type::cond::is_base_of<T,S>::value);
// and T has a virtual destructor
CGV_DEFINES_ASSERT(type::cond::has_virtual_destructor<T>::value);
// after validity checks, set pointer with a very bad hack!!
ptr = static_cast<const ref_ptr<S,true>&>(s).operator->();
// and increment reference count
if (ptr)
inc_ref_count(ptr);
}
public:
// void kill() { ptr = 0; }
/// return the reference count
int get_count() const {
return ptr ? ptr->get_ref_count() : 0;
}
};
/** reference counted pointer, which can work together with types that are derived
from ref_counted, in which case the reference count of ref_counted is
used. Otherwise a reference count is allocated and access to the stored instance
needs to follow two pointers. */
template <class T, bool is_ref_counted = type::cond::is_base_of<ref_counted,T>::value>
class ref_ptr : public ref_ptr_impl<T,is_ref_counted>
{
public:
/// type of the reference counted pointer
typedef ref_ptr<T,is_ref_counted> this_type;
/// type of base class that implements the reference count specific methods
typedef ref_ptr_impl<T,is_ref_counted> base_type;
public:
/// construction from pointer or empty if pointer is not given
ref_ptr(T* p = 0) : base_type(p) {}
/// copy constructor for reference pointers of the same type
ref_ptr(const this_type& r) : base_type(r) {}
/// destruct reference counted pointer
~ref_ptr() {
this->release();
}
/// allow to copy ref_ptr<S> to a ref_ptr<T> if T is a base class of S and if T has a virtual destructor
template <typename S>
ref_ptr(const ref_ptr<S,is_ref_counted>& s) : base_type(s) {}
/// use static cast to convert from T to S if T is a base class of S and has a virtual destructor
template <typename S>
ref_ptr<S,is_ref_counted> up_cast() const {
// ref_ptr conversion only valid if T is base of S
CGV_DEFINES_ASSERT(type::cond::is_base_of<T,S>::value);
// and S has a virtual destructor
CGV_DEFINES_ASSERT(type::cond::has_virtual_destructor<T>::value);
// after validity checks, return converted pointer
return ref_ptr<S,is_ref_counted>(
*reinterpret_cast<const ref_ptr<S,is_ref_counted>*>(this)
);
}
/// assignment to pointer of same type
this_type& operator = (const this_type& r) {
if (this == &r)
return *this;
this->release();
new (this) this_type(r);
return *this;
}
/// assignment to pointer of derived type with virtual destructor
template <typename S>
this_type& operator = (const ref_ptr<S,is_ref_counted>& r) {
if (this == (const this_type*)&r)
return *this;
return *this = this_type(r);
}
/// access to element
T& operator*() const {
return *this->ref();
}
/// access to element pointer
T* operator->() const {
return this->ref();
}
/// compare by pointer
bool operator == (const this_type& r) const {
return this->ref() == r.ref();
}
/// compare by pointer
bool operator < (const this_type& r) const {
return this->ref() < r.ref();
}
/// compare by pointer
bool operator != (const this_type& r) const {
return this->ref() != r.ref();
}
/// check if this is the only reference
bool unique() const {
return this->get_count() <= 1;
}
/// check if pointer is not yet set
bool empty() const {
return this->get_count() == 0;
}
/// conversion to bool results in false if the stored pointer is a null pointer
operator bool () const {
return !empty();
}
/// set to null pointer
void clear() {
this->release();
}
};
}
}
#include <cgv/config/lib_end.h>