Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Single-threaded concurrency model design

, 12 Jul 2009
A lightweight library to support single-threaded concurrency with multiple components.
#pragma once

namespace GObj
{

// This is a base template class for guarded objects.
// Encapsulates a wrapper around some type/object that needs cleanup.
// Should not be used as-is. Instead use either GObj_T or GRef_T
template <class T>
class GBase_T
	:protected T
{
protected:
	void InternalCleanup()
	{
		if (IsValid())
			T::InternalDestroy();
	}
	void InternalSetNull()
	{
		T::m_Value = T::InternalNullValue();
	}
public:
	// Destroys the guarded object (if valid).
	~GBase_T()
	{
		InternalCleanup();
	}
	// Explicitly destroy the guarded object (if valid) and set it to invalid.
	void Destroy()
	{
		if (IsValid())
		{
			T::InternalDestroy();
			InternalSetNull();
		}
	}
	// Returns the guarded object and set it to invalid without destroying it. Use with care!
	typename T::GuardType Detach()
	{
		typename T::GuardType val = T::m_Value;
		InternalSetNull();
		return val;
	}
	// Destroys the guarded object (if valid) and assign it to a given one.
	void Attach(typename T::GuardType val)
	{
		InternalCleanup();
		T::m_Value = val;
	}
	// Swaps the guarded object.
	void Swap(GBase_T<T>& other)
	{
		swap_t(m_Value, other.m_Value);
	}
	// Checks if the guarded object is valid.
	bool IsValid() const
	{
		return T::InternalNullValue() != T::m_Value;
	}
	// Checks if the guarded object is NOT valid.
	bool IsNull() const
	{
		return T::InternalNullValue() == T::m_Value;
	}
	// returns the guarded object's value.
	typename T::GuardType GetValue() const
	{
		return T::m_Value;
	}
	// returns the guarded object (same as GetValue).
	operator typename T::GuardType() const
	{
		return T::m_Value;
	}
	// Test for validity (same as IsValid)
	operator bool () const
	{
		return IsValid();
	}
	// A more convenient way to access an object for some types (such as smart pointers). NOTE: No test for validity is performed here
	typename T::GuardType operator ->()
	{
		return T::m_Value;
	}
	// If the guarded type is a kind of a pointer - the following is needed for arithmetic operations, (otherwise confusing with operator bool)
	typename T::GuardType operator + (int nVal)
	{
		return T::m_Value + nVal;
	}
	typename T::GuardType operator - (int nVal)
	{
		return T::m_Value - nVal;
	}

	// Exposes a direct access to the guarded object variable. Use with care!
	typename T::GuardType& GetRef()
	{
		return T::m_Value;
	}
};

// This is a common class for simple guard objects, those that don't support referencing, just destruction.
// For such objects it is illegal to either assign one wrapper to another or use copy constructors,
// since in such a case two wrappers will attempt to destroy the object.
// The Attach is in fact equivalent to the assignment operator.
template <class T>
class GObj_T :public GBase_T<T>
{
	// Copy constructor is disabled
	GObj_T(const GObj_T& val);
	// Assignment from another guarded object is disabled
	GObj_T& operator = (const GObj_T& val);
public:
	// Empty constructor. Sets the guarded object to invalid
	GObj_T()
	{
		InternalSetNull();
	}
	// Constructor that receives an object and saves it. (the object may be invalid)
	GObj_T(typename T::GuardType val)
	{
		T::m_Value = val;
	}
	// Destroy the old guarded object (if valid) and save the new one.
	GObj_T& operator = (typename T::GuardType val)
	{
		InternalCleanup();
		T::m_Value = val;
		return *this;
	}
};

// A class for guard objects that support referencing, such as smart pointers.
// Encapsulates objects that support referencing. Means holding a reference to the object requires
// to call an extra function (such as AddRef for smart pointers).
// For such objects it is legal to have multiple wrappers for the the same object.
// The Attach is NOT equivalent to the assignment operator.
template <class T>
class GRef_T :public GBase_T<T> {
protected:
	void InternalAssign(typename T::GuardType val)
	{
		T::m_Value = val;
		if (IsValid())
			T::Reference();
	}
public:
	// Empty constructor. Sets the guarded object to invalid
	GRef_T()
	{
		InternalSetNull();
	}
	// Constructor that receives an object and saves it. (the object may be invalid)
	GRef_T(typename T::GuardType val)
	{
		InternalAssign(val);
	}
	// Constructor that receives a wrapper to the object and saves the object (the object may be invalid)
	GRef_T(const GRef_T& val)
	{
		InternalAssign(val);
	}
	// Constructor that either saves or attaches to the given object. (the object may be invalid)
	GRef_T(typename T::GuardType val, bool bAddRef)
	{
		if (bAddRef)
			InternalAssign(val);
		else
			T::m_Value = val;
	}
	// Destroys the old object (if valid) and saves the new one.
	GRef_T& operator = (typename T::GuardType val)
	{
		// This is needed if you intend to assign an object to itself.
		if (val != T::m_Value)
		{
			InternalCleanup();
			InternalAssign(val);
		}
		return *this;
	}
	// Destroys the old object (if valid) and saves the new one.
	GRef_T& operator = (const GRef_T& val)
	{
		return operator = (val.T::m_Value);
	}
};

// In order to use those guard template classes - usually it's enough to instantiate the
// typedef for GObj_T/GRef_T, but in some cases we'll also want to override those classes
// for some extra-functionality.

// Alas, the C++ rules are stupid enough to generate copy constructor and assignment
// operator automatically, by just copying the memory. Of course this can't be a correct
// behaviour in our case. Because of this we've implemented those two functions explicitly
// in GObj_T/GRef_T classes.

// But unfortunately this is not enough to be absolutely sure the compiler won't ruin that.
// If you override a class and forget to re-write copy constructor/assignment the compiler
// will generate those two functions automatically again, incorrectly of course.

// To avoid such cases we obey the following rule: NEVER inherit the GObj_T class directly.
// Use the following macros instead:
#define INHERIT_GUARD_OBJ_BASE(gnew, gbase, gtype) \
private: \
	gnew(const gnew&); \
	gnew(const gbase&); \
	gnew& operator = (const gnew&); \
	gnew& operator = (const gbase&); \
public: \
	gnew& operator = (gtype val) { gbase::operator = (val); return *this; }

// Use this macro to inherit the GObj_T class. For more info see INHERIT_GUARD_OBJ_BASE
#define INHERIT_GUARD_OBJ(gnew, gbase, gtype) \
	INHERIT_GUARD_OBJ_BASE(gnew, gbase, gtype) \
	gnew() {} \
	gnew(gtype val) :gbase(val) {}

// Use this macro to inherit the GRef_T class. For more info see INHERIT_GUARD_OBJ_BASE
#define INHERIT_GUARD_REF_BASE(gnew, gbase, gtype) \
public: \
	gnew(const gnew& val) :gbase(val) {} \
	gnew(const gbase& val) :gbase(val) {} \
	gnew& operator = (gtype val) { gbase::operator = (val); return *this; } \
	gnew& operator = (const gnew& val) { gbase::operator = (val); return *this; } \
	gnew& operator = (const gbase& val) { gbase::operator = (val); return *this; }

// Use this macro to inherit the GRef_T class. For more info see INHERIT_GUARD_OBJ_BASE
#define INHERIT_GUARD_REF(gnew, gbase, gtype) \
	INHERIT_GUARD_REF_BASE(gnew, gbase, gtype) \
	gnew() {} \
	gnew(gtype val) :gbase(val) {} \
	gnew(gtype val, bool bAddRef) :gbase(val, bAddRef) {}

// Base template class. Declares a member of the provided type
template <class T>
struct GBaseH_Core {
	// The variable for the guarded object (used by GObj_T/GRef_T)
	T m_Value;
	// The declaration of the guarded type (used by GObj_T/GRef_T)
	typedef T GuardType;
};

// Implements InternalNullValue by returning NULL of specified type, applicable for most of the types.
template <class T>
struct GBaseH_Null {
	// NULL is the invalid value for the given type (This is used by GObj_T/GRef_T to test the validity of the object)
	static T InternalNullValue() { return NULL; }
};

// Implements InternalNullValue by returning -1 of specified type, applicable for some special cases, such as file/socket handles.
template <class T>
struct GBaseH_Minus1 {
	// -1 (or alternatively 0xFFFFFFFF) is the invalid value for the given type (This is used by GObj_T/GRef_T to test the validity of the object)
	static T InternalNullValue() { return (T) -1; }
};

// Combines the GBaseH_Core and GBaseH_Null
template <class T>
struct GBaseH_CoreNull :public GBaseH_Core<T>, public GBaseH_Null<T> {
};

// Combines the GBaseH_Core and GBaseH_Minus1
template <class T>
struct GBaseH_CoreMinus1 :public GBaseH_Core<T>, public GBaseH_Minus1<T> {
};

// Implements destruction of the object by delete operator
template <class T>
struct GBaseH_Ptr :public GBaseH_CoreNull<T*> {
	// Implements destruction of the object by delete operator
	void InternalDestroy() { delete m_Value; }
};

// Guards objects allocated via new operator. The destruction is implemented via
// the delete operator.
template <class T>
class GPtr_T :public GObj_T<GBaseH_Ptr<T> >
{
	INHERIT_GUARD_OBJ(GPtr_T, GObj_T<GBaseH_Ptr<T> >, T*)
};

// Implements destruction of the objects ARRAY by delete[] operator
template <class T>
struct GBaseH_Arr :public GBaseH_CoreNull<T*> {
	// Implements destruction of the object by delete operator
	void InternalDestroy() { delete[] m_Value; }
};

// Guards objects allocated via new operator. The destruction is implemented via
// the delete operator.
template <class T>
class GArr_T :public GObj_T<GBaseH_Arr<T> >
{
	INHERIT_GUARD_OBJ(GArr_T, GObj_T<GBaseH_Arr<T> >, T*)
};

}; // namespace GObj

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)

Share

About the Author

valdok
Software Developer (Senior)
Israel Israel
My name is Vladislav Gelfer, I was born in Kiev (former Soviet Union), since 1993 I live in Israel.
In programming I'm interested mostly in low-level, OOP design, DSP and multimedia.
Besides of the programming I like physics, math, digital photography.

| Advertise | Privacy | Mobile
Web02 | 2.8.140922.1 | Last Updated 12 Jul 2009
Article Copyright 2009 by valdok
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid