Click here to Skip to main content
15,881,381 members
Articles / Programming Languages / C++

A policy based reference counting implementation for compound objects

Rate me:
Please Sign up or sign in to vote.
4.77/5 (13 votes)
26 May 2005CPOL16 min read 33.1K   274   30  
Reference counting smart pointers and handles of various flavours.
/////////////////////////////////
// smart.hpp
//	generic assign/copy wrapper
//

#pragma once
#include "dependency.h"
#pragma message ("Compiling " __FILE__)


namespace GE_{ namespace stdx{

	//class smart: inherit from a policy.
	//	requires:
	//		assign, alias_type, value_type, acquire_type
	//	provides
	//		copy, assignment, conversion operators
	
	template<class smart_policy>
	class smart:
		public smart_policy,
		public operators<smart>
	{
	public:
		smart() {}
		~smart() {}
		smart(const smart& s) { assign(static_cast<const smart_policy&>(s)); }
		template<class other>		
			smart(const smart<other>& s) { assign(static_cast<const other&>(s)); }
		template<class A, class B>
			smart(const A& a, const B& b) { assign(a,b); }
		
		//dumb-to-smart is potentially unsafe for policy requiring extra data to be recorded
		//	see pointers::mappedpolicy or pointers::intrpolicy for safe assignments.
		smart(typename smart_policy::acquire_type a) { assign(a); }

		smart& operator=(const smart& s) { assign(static_cast<const smart_policy&>(s)); return *this; }

		typedef typename smart_policy::alias_type alias_type;
		typedef typename smart_policy::acquire_type acquire_type;
		typedef typename smart_policy::value_type value_type;
	};

	namespace refcounts
	{
		
		/////////////////////////
		//	reference counting

		struct counters
		{
		private:
			unsigned ref, hold;
			bool locked;
		public:
			counters() { ref=hold=0; locked=false; }
			template<class P>
			void decrement(P& p, bool bStrong)
			{
				if(bStrong && !locked)
				{
					p.on_release();
					if(hold == 1)
					{
						locked = true;
						p.on_lastrelease();
						p.on_delete();
					}
					hold--;
				}
				ref--;
				if(!ref)
				{
					counters* p(this);
					stdx::Delete(p);
				}
			}

			template<class P>
			void increment(P& p, bool bStrong)
			{
				ref++;
				if(bStrong && !locked)
				{
					hold++;
					if(hold==1)
						p.on_firstref();
					p.on_addref();
				}
			}

			unsigned get_hold() const {return hold;}
			unsigned get_ref() const { return ref; }
		};

		
		//
		//	requires:
		//		assign, equal, less,
		//		on_addref, on_firstref, on_release, on_lastrelease, on_delete
		//		operator!, alias_type, get_counters, New
		//	provides:
		//		assign, equal, less, operator!, alias_type, New
		//
		template<class refcnt_policy, bool bStrong>
		class policy:
			public refcnt_policy
		{
		private:
			friend class policy;
			friend struct counters;
			counters* pCnt;
			
			void clear_()
			{
				if(!!pCnt)
					pCnt->decrement(*this, bStrong);
				pCnt = 0;
			}

			void set_(counters* p)
			{
				ASSERT(!pCnt);
				pCnt = p;
				if(pCnt)
					pCnt->increment(*this, bStrong);
			}

		protected:
			policy() { pCnt=0; }
			~policy() { clear_(); }

			typedef typename refcnt_policy::acquire_type acquire_type;
			typedef typename refcnt_policy::alias_type alias_type;

			template<class other, bool b>
				void assign(const policy<other,b>& a)
			{
				if(a.pCnt == pCnt) return;
				clear_();
				refcnt_policy::assign(static_cast<const other&>(a));
				if(!refcnt_policy::operator!()) set_(a.pCnt);
			}

			void assign(acquire_type a)
			{
				counters* p = get_counters(a);
				if(pCnt == p) return;
				clear_();
				refcnt_policy::assign(a);
				set_(p);
			}


		public:
			bool operator!() const { return !pCnt || !pCnt->get_hold() || refcnt_policy::operator!(); }
			bool operator==(const policy& r) const
			{	return (pCnt == r.pCnt) || refcnt_policy::operator==(static_cast<const refcnt_policy&>(r)); }
			bool operator<(const policy& r) const
			{	return (pCnt != r.pCnt) && refcnt_policy::operator<(static_cast<const refcnt_policy&>(r)); }

			void clear() { clear_(); }
			alias_type New() { clear(); assign(refcnt_policy::New()); return operator()(); }
			template <class A>
				alias_type New(const A& a) { clear(); assign(refcnt_policy::New(a)); return operator()(); }
			template <class A, class B>
				alias_type New(const A& a, const B& b) { clear(); assign(refcnt_policy::New(a,b)); return operator()(); }
			unsigned get_holdcount() { return (!pCnt)? 0: pCnt->get_hold(); }
			unsigned get_refcount() { return (!pCnt)? 0: pCnt->get_ref(); }
			typedef policy refcnt_plc;
		};

	}


	namespace pointers
	{
		struct excp_base {};

		template<class Type>
		struct excp: public excp_base {}; //thrown as excp* in case of null dereferencing

		//
		//	pointer: use static cast in conversions
		//
		//	requires:
		//		Type
		//	provides:
		//		assign, equal, less,
		//		on_addref, on_firstref, on_release, on_lastrelease, 
		//		operator!, alias_type, get_counters, clear, New
		//

		template<class Type>
		class policy
		{
			friend class policy;
		protected:
			Type* ptr;
		protected:
			typedef Type* alias_type;
			typedef Type* acquire_type;
			typedef Type value_type;
			policy() { ptr=0; }
			void on_addref() {}
			void on_firstref() { STRACE(t,1,("first reference to %p, %s\n", ptr, typeid(*ptr).name())); }
			void on_release() {}
			void on_lastrelease() { STRACE(t,1,("last releasing to %p, %s\n", ptr, typeid(*ptr).name())); }
			void on_delete() { if(ptr) stdx::Delete(ptr); ptr=0; }
			refcounts::counters* get_counters(Type* p) { refcounts::counters* pcnt(0); if(p) stdx::New(pcnt); return pcnt;  }
			void clear() { ptr = 0; }

			template<class other>
				void assign(const policy<other>& p) { ptr = static_cast<Type*>(p.ptr); }
			void assign(const policy& p) { ptr = p.ptr; }
			void assign(Type* p) { ptr = p; }
		public:
			bool operator==(const policy& p) const { return ptr == p.ptr; }
			bool operator<(const policy& p) const { return ptr < p.ptr; }
			bool operator!() const { return !ptr; }
			Type* operator()() const { return ptr; }
			template<class I>
			Type& operator[](const I& i) const { ASSERT(i==0); return operator*(); }
			Type* operator->() const { if(!ptr) throw_excpptr<excp<Type> >(); return ptr; }
			Type& operator*() const { if(!ptr) throw_excpptr<excp<Type> >(); return *ptr; }
			operator Type*() const { return ptr; }
			Type* New() { stdx::New(ptr); return ptr; }
			template<class A>
				Type* New(const A& a) { stdx::New(ptr,a); return ptr; }
			template<class A, class B>
				Type* New(const A& a, const B& b) { stdx::New(ptr,a,b); return ptr; }
			typedef policy pointer_plc;
		};

		//array
		template<class Type>
		class vectpolicy:
			public pointers::policy<Type>
		{
		protected:
			void on_delete()
			{	delete[] operator()(); }
		public:
			template<class I>
			Type& operator[](const I& i) 
			{	return operator()()[i]; }
		};

		// dynptr: specialize ptr, using dynamic cast
		template<class Type>
		class dynpolicy:
			public policy<Type>
		{
		protected:
			template<class other>
				void assign(const policy<other>& p) { assign(dynamic_cast<Type*>(p())); }
			void assign(const dynpolicy& p) { assign(p()); }
			void assign(Type* p) { policy<Type>::assign(p); }
		};


		//mapped pointers: use aglobally referred map to associate counters to objects.
		//	the existence of the map is ... refcounted(!) - without a map, of course. -
		template<class Type>
		class mappedpolicy:
			public dynpolicy<Type>
		{
		private:
			typedef typename std::map<void*, refcounts::counters*> map_t;
			typedef typename dynpolicy<Type> base_t;
			typedef typename smart<refcounts::policy<pointers::policy<map_t>, true> > map_p;
			static map_p map() { static map_p p; if(!p) p.New(); return p; }
			map_p pMap;
		protected:
			// a map will exist until the program terminates or someone will refer it
			mappedpolicy() { pMap = map(); }
			refcounts::counters* get_counters(Type* p) 
			{
				refcounts::counters*& pcnt = (*pMap)[dynamic_cast<void*>(p)];
				if(!pcnt) stdx::New(pcnt);
				return pcnt;
			}
			void on_delete()
			{
				pMap->erase(dynamic_cast<void*>(operator()()));
				base_t::on_delete();
			}
		};

	    
		//////////////////////////
		// intrusive refocunters:
		//	Type supposed to be an stdx::i_referrable implementation
		//	stdx::refcountable can be used that way
		//

		template<class Type>
		class intrpolicy:
			public dynpolicy<Type>
		{
		private:
			typedef dynpolicy<Type> base_t;
		protected:
			void on_addref() { if((*this)()) dynamic_cast<refcountable*>((*this)())->on_addref(); }
			void on_firstref() 
			{ 
				STRACE(t,1,("first intrusive reference to %p, %s\n", (*this)(), typeid(*(*this)()).name())); 
				if((*this)()) dynamic_cast<refcountable*>((*this)())->on_firstref(); 
			}
			void on_release() { if((*this)()) dynamic_cast<refcountable*>((*this)())->on_release(); }
			void on_lastrelease() 
			{ 
				STRACE(t,1,("last intrusive releasing to %p, %s\n", (*this)(), typeid(*(*this)()).name()));
				if((*this)()) dynamic_cast<refcountable*>((*this)())->on_lastrelease(); 
			}
			void on_delete()
			{	if((*this)()) dynamic_cast<refcountable*>((*this)())->on_delete(); ptr = 0; }
			refcounts::counters* get_counters(Type* p) { return (!p)? 0: dynamic_cast<refcountable*>(p)->get_counters(); }
		};

	}


	class refcountable
	{
		friend struct refcounts::counters;
		friend class pointers::intrpolicy;
	public:
		refcountable() 
		{ 
			stdx::New(pCnt);
			pCnt->increment(*this, false); 
		}
		virtual ~refcountable() 
		{ 
			if(pCnt)
				pCnt->decrement(*this, false);
			pCnt=0;
		}
	private:
		refcounts::counters* pCnt;
	protected:
		virtual void on_addref() {}
		virtual void on_firstref() {}
		virtual void on_release() {}
		virtual void on_lastrelease() { }
		virtual void on_delete() { refcountable* p(this); stdx::Delete(p); }
		virtual refcounts::counters* get_counters() { return pCnt; }
	};

	////////////////////
	// shortcuts for frequent types
	//

	template<class Type>
	struct ptr //intrusive ptrs, dumb-to smart safe assignment
	{
		typedef smart<refcounts::policy<pointers::intrpolicy<Type>, true> > strong;
		typedef smart<refcounts::policy<pointers::intrpolicy<Type>, false> > weak;
	};

	template<class Type>
	struct statptr //static_cast non-intrusive ptrs, usafe dumb-to smart assignments
	{
		typedef smart<refcounts::policy<pointers::policy<Type>, true> > strong;
		typedef smart<refcounts::policy<pointers::policy<Type>, false> > weak;
	};

	template<class Type>
	struct refcntvect //non casting, non intrusive ptr to an array. unsafe dumb-to smart assign
	{
		typedef smart<refcounts::policy<pointers::vectpolicy<Type>, true> > strong;
		typedef smart<refcounts::policy<pointers::vectpolicy<Type>, false> > weak;
	};

	template<class Type>
	struct dynptr //dynamic_cast non-intrusive ptrs, usafe dumb-to smart assignments
	{
		typedef smart<refcounts::policy<pointers::dynpolicy<Type>, true> > strong;
		typedef smart<refcounts::policy<pointers::dynpolicy<Type>, false> > weak;
	};

	template<class Type>
	struct mappedptr //dynamic_cast non-intrusive, safe dumb to smart assignment
	{
		typedef smart<refcounts::policy<pointers::mappedpolicy<Type>, true> > strong;
		typedef smart<refcounts::policy<pointers::mappedpolicy<Type>, false> > weak;
	};


	/////////////////////////////////////////
	//// other policies
	//
	
	namespace values {

        // like "pointers", but giving value semantics
	
		template<
			class Type, 
			Type nullval, //a singular value for Type
			class CleanupFn //a "void operator()(Type&)" functor, doing cleanup action
		>
		class policy
		{
			friend class policy;
		protected:
			Type val;
			CleanupFn fnCleanup;
		protected:
			typedef const Type& alias_type;
			typedef const Type& acquire_type;
			typedef Type value_type;
			policy() { val=nullval; }
			void on_addref() {}
			void on_firstref() { STRACE(t,1,("first value reference to %p, %s\n", (*this)(), typeid((*this)()).name())); }
			void on_release() {}
			void on_lastrelease() 
			{ 
				 STRACE(t,1,("last value release to %p, %s\n", (*this)(), typeid((*this)()).name())); 
			}
			void on_delete() { fnCleanup(val); val=nullval; }
			refcounts::counters* get_counters(Type* p) { refcounts::counters* p(0); stdx::New(p); return p; }
			void clear() { val = nullval; }

			void assign(const policy& p) { val = p.val; }
			void assign(const Type& p) { val = p; }
		public:
			bool operator==(const policy& p) const { return val == p.val; }
			bool operator<(const policy& p) const { return val < p.val; }
			bool operator!() const { return val == nullval; }
			const Type& operator()() const { return val; }
			operator const Type&() const { return val; }
		};


		//mapped values: use a globally referred map to associate counters to values.
		//	the existence of the map is ... refcounted(!) - without a map, of course. -
		template<class Type, Type nullval, class CleanupFn>
		class mappedpolicy:
			public policy<Type, nullval, CleanupFn>
		{
		private:
			typedef typename std::map<Type, refcounts::counters*> map_t;
			typedef typename policy<Type, nullval, CleanupFn> base_t;
			typedef typename smart<refcounts::policy<pointers::policy<map_t>, true> > map_p;
			static map_p map() { static map_p p; if(!p) p.New(); return p; }
			map_p pMap;
		protected:
			// a map will exist until the program terminates or someone will refer it
			mappedpolicy() { pMap = map(); }
			~mappedpolicy() {}
			refcounts::counters* get_counters(const Type& p) 
			{
				refcounts::counters*& pcnt = (*pMap)[p];
				if(!pcnt) stdx::New(pcnt);
				return pcnt;
			}
			void on_delete()
			{
				pMap->erase(operator()());
				base_t::on_delete();
			}
		};

	}


	template<class Type, Type nullval, class CleanupFn>
	struct hnd
	{
		typedef smart<refcounts::policy<values::policy<Type,nullval,CleanupFn>, true> > strong;
		typedef smart<refcounts::policy<values::policy<Type,nullval,CleanupFn>, false> > weak;
	};

	template<class Type, Type nullval, class CleanupFn>
	struct mappedhnd
	{
		typedef smart<refcounts::policy<values::mappedpolicy<Type,nullval,CleanupFn>, true> > strong;
		typedef smart<refcounts::policy<values::mappedpolicy<Type,nullval,CleanupFn>, false> > weak;
	};



}}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
Italy Italy
Born and living in Milan (Italy), I'm an engineer in electronics actually working in the ICT department of an important oil/gas & energy company as responsible for planning and engineering of ICT infrastructures.
Interested in programming since the '70s, today I still define architectures for the ICT, deploying dedicated specific client application for engineering purposes, working with C++, MFC, STL, and recently also C# and D.

Comments and Discussions