//
// (C) 2002 by hersto, Herbert Stocker
//
// You can redistribute, modify or use this file in your own
// projects as long as you keep this comment block intact. This
// requirement does not apply if you make very major changes
// to the file.
// The above statements apply to this file only, not to the whole
// program, should you have found this file as a part of such.
//
// This file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// The original can be found at http://www.hersto.de/
//
#ifndef _REF_Template_H
#define _REF_Template_H
/*
*
* ref.h - Template based Ref class |
*
* Purpose:
* Template based reference class that replaces plain
* C pointers if their purpose is to point to objects.
*
* Automatically calls reference counting functions
* Addref() and Release(). Thus it can point to COM
* objects.
*
* Also wraps the COM functions CoCreateInstance(.)
* and QueryInterface(.) .
*
* Another design goal is to be a smart pointer for
* COM objects without the requirement that the compiler
* supports the #import statement.
*
*
*
* Note:
* the methods and operators dealing with COM objects
* are only defined if the macro _COM is #defined.
*
*
* Changes:
* ...from time to time...
*
* /
/**********************************************************************
*
* Description:
*
* ref<T> can be used instead of plain C pointers to perform
* reference counting on the objects they point to and automatically
* delete them if the last reference to them falls out of scope.
* ref<T> can be used on any objects which have AddRef() and Release()
* member functions and do reference counting in these.
* Additionally the constructors of these objects must set the
* initial reference count to 0.
* ref<T> can also take a reference to a COM object interface.
*
* If _COM is defined, there is also a constructor which can
* instantiate a COM object - when given CLSID and IID - and the
* function ref<T>::Query(.) which queries for another interface.
*
*
* How to use:
* -----------
*
*
* Instead of CSpaceShip* pShip= new CSpaceShip(args);
*
* write ref<CSpaceShip> refShip= new CSpaceShip(args);
* and forget about deleting pShip.
*
*
*
*
* Instead of CSpaceShip* pMyShip= pShip;
* if(pMyShip) pMyShip->AddRef();
*
* write ref<CSpaceShip> refMyShip= refShip;
* and forget about calling AddRef() and Release().
*
*
*
*
* Hints:
* If you want to get the address of the object the
* ref points to, write for example:
*
* printf("Object is at 0x%08x\n", &*refShip);
*
* and read the &* as "adress of where it points to".
*
*
*
*
*
* If _COM is defined, instantiate a COM object with
*
* ref<IFly> refFlyMyShip(CLSID_SpaceShip, IID_IFly);
*
* and test wether refShip is NULL afterwards, to see if the
* instantiation was successfull. An optional third parameter
* allows you to specify the adress of an HRESULT variable that
* receives the COM error code.
*
*
* To switch to another interface, write:
*
* ref<IFight> refFightWithMyShip= refShip.Query(IID_IFight);
*
* and again test afterwards whether refFightWithMyShip is NULL.
* Like all COM related methods, Query has also an optional
* parameter that can return an HRESULT error code.
*
* Note: .Query(.) is not type safe. The IID you pass to .Query(.)
* _must_ correspond to the interface type the receiving
* ref object points to. It was intended to not use MS
* extensions to C++ .
*
* Again, an optional third parameter can point to an HRESULT
* variable that receives an error code.
*
*
**********************************************************************/
// An intermediate class for ref<T>::Query(.) .
// To achieve some type savity.
// Not intended to be used outside this .h file.
#ifdef _COM
class __QueryIfcResult
{
public:
__QueryIfcResult(void* pPointer) { mpPointer= pPointer; }
void* GetPointer() { return mpPointer; }
protected:
void* mpPointer;
};
#endif
//////////////////////////////////
// The ref class (a smart pointer)
template<class Type>
class ref
{
public:
ref();
ref(Type* pTarget);
ref(const ref<Type>& that);
~ref();
ref<Type>& operator= (Type* pTarget);
ref<Type>& operator= (const ref<Type>& that);
operator Type* () const;
Type* operator-> () const;
Type& operator* () const;
operator bool () const;
bool operator! () const;
bool operator== (const ref<Type>& that) const { return mpTarget == that.mpTarget; }
bool operator!= (const ref<Type>& that) const { return mpTarget == that.mpTarget; }
friend bool operator== (const ref<Type>& rrefA, Type* pB);
friend bool operator!= (const ref<Type>& rrefA, Type* pB);
friend bool operator== (Type* pA, const ref<Type>& rrefB);
friend bool operator!= (Type* pA, const ref<Type>& rrefB);
// I didn't test these. Therefore they are commented out for now.
// Maybe this is a change necessary for Visual Studio 7
// bool operator!() const
// {
// return (mpTarget == NULL);
// }
// bool operator<(Type* pT) const
// {
// return mpTarget < pT;
// }
// bool operator==(Type* pT) const
// {
// return mpTarget == pT;
// }
ref<Type>& TakeOver(Type * &rpOther); // To take over a pointer (actually take over its value and "being counted") The given pointer is assigned the NULL Value, since it may no more be used afterwards (it has no refcount).
// returns reference to *this, in order to catenate other method calls.
Type* GiveAway(); // The opposite of TakeOver(.) . GiveAway() returns the pointer to the target. It also passes
// its "being counted" along with the pointer. This means that it sets its internal pointer
// to NULL and the caller of GiveAway() is responsible to call Release().
// Note that GiveAway() does not call AddRef() or Release() on the target.
Type* SplitOff() const; // The less weired version of GiveAway(). SplitOff() returns the target pointer, but calls AddRef() on the target before that.
// The internal pointer to the target is not set to NULL, since there is now an additional refcount.
// One must call Release() on the returned pointer when finished with it.
// In other words, `.SplittOff()´ is another way of saying `->AddRef()´, but with the
// advantage that the pointer is returned instead of a refcount or something else.
Type* &Expose() { return mpTarget; } // Exposes the pointer - its inner value - as a C++ reference. So you can
// access the plain C Pointer when needed. If you change the pointer you
// are responsible to call AddRef() and Release() accordingly, just as with
// any other plain C Pointer.
Type* &ExposeNULL(); // Same as Expose(), but it makes the pointer to NULL calling Release() if necessary
// before exposing it.
#ifdef _COM
public:
ref(REFCLSID rClsID, REFIID rIID, HRESULT *pComErr= NULL);
ref(__QueryIfcResult& rIfc);
ref<Type>& operator =(__QueryIfcResult& rIfc);
ref(IUnknown *pUnknown, REFIID rIID, HRESULT *pComErr= NULL);
__QueryIfcResult Query(REFIID rIID, HRESULT *pComErr= NULL);
// For passing COM pointers across threads:
ref<IStream> ThreadMarshal(REFIID rIID, HRESULT *pComErr= NULL);
void ThreadUnmarshal(IStream* &rpMarshalledIfc, REFIID rIID, HRESULT *pComErr= NULL);
ref(IStream* &rpMarshalledIfc, REFIID rIID, HRESULT *pComErr= NULL);
#endif
protected:
Type* mpTarget;
};
//-----------------------------------------------------------------//
//-----------------------------------------------------------------//
template<class Type>
ref<Type>::ref()
: mpTarget(NULL)
{
ASSERT(sizeof(*this) == sizeof(Type*)); // It was an initial design goal that the size of the class is the size of a pointer.
}
//-----------------------------------------------------------------//
template<class Type>
ref<Type>::ref(Type* pTarget)
: mpTarget(pTarget)
{
ASSERT(sizeof(*this) == sizeof(Type*)); // It was an initial design goal that the size of the class is the size of a pointer.
if(mpTarget) mpTarget->AddRef();
}
// Do you get Compiler errors here?
//
// If it's about undefined type 'BlaBla', you may
// have forgotten to #include the header file that defines
// the BlaBla class. Maybe you've only declared the class
// with a
//
// class BlaBla;
//
// You can declare a class in the .h of your class (the one
// that uses the BlaBla class) and #include the class definition
// in the .cpp file of your class.
//
// See the comment at the desctructor ref<Type>::~ref()
// for more information.
//
//-----------------------------------------------------------------//
template<class Type>
ref<Type>::ref(const ref<Type>& that)
: mpTarget(that.mpTarget)
{
ASSERT(sizeof(*this) == sizeof(Type*)); // It was an initial design goal that the size of the class is the size of a pointer.
if(mpTarget) mpTarget->AddRef();
}
//-----------------------------------------------------------------//
template<class Type>
ref<Type>& ref<Type>::operator =(Type* pTarget)
{
if( pTarget) pTarget->AddRef(); // Get Compiler error due to undefined target type here?
if(mpTarget) mpTarget->Release(); // Then look at the comment on the destructur. This explains what to do.
mpTarget= pTarget;
return *this;
}
//-----------------------------------------------------------------//
template<class Type>
ref<Type>& ref<Type>::operator =(const ref<Type>& that)
{
if(that.mpTarget) that.mpTarget->AddRef();
if( mpTarget) mpTarget->Release();
mpTarget= that.mpTarget;
return *this;
}
//-----------------------------------------------------------------//
template<class Type>
ref<Type>::~ref()
{
if(mpTarget) mpTarget->Release();
// Compiler errors here?
// see comment below.
}
// Compiler errors in this destructor:
//
// Undefined type (of the target class):
// - You probably missed an #include statement for the target class' header file.
// See which .cpp file the compiler was compiling when it generated this error
// and #include the file that defines the target class (the one the compiler has
// no definition for) there.
//
// - If this ref object is part of another class and you use class declaration in
// the header file (no #include, but a class CDings; statment)
// and #include the header file in the outer classes .cpp file this compiler error
// has two reasons:
// - the destructor of the outer class has its implementation in the header file
// Move the destructor to the .cpp file.
//
// - the outer class has no destructor defined. In this case
// Then the compiler creates a destructor on the fly and has no
// definition of the target type available.
// Declare a destructor for the outer class and implement it in the
// .cpp file (most likely an empty destructor)
//
// - If the ref object points to a COM interface which has two versions,
// a unicode and an ascii version. This error happens for direct input interfaces
// like IDirectInput7. It happens even if you have correctly declared the
// interface in the .h file, #included the header defining this interface in the
// .cpp file and implemented the (possibly empty) con- and destructor in the .cpp file.
//
// The reason:
// - the name you declared in the .h file is only an alias to one of both versions
// of the interface. I.e. the header file you include in your .cpp file has some-
// thing like this:
// #ifdef UNICODE
// # define IDirectInput7 IDirectInput7W
// #else
// # define IDirectInput7 IDirectInput7A
// #endif
// A solution of this is to include this conditional #definition in
// your .h file before the
// interface IDirectInput7;
// statement.
//-----------------------------------------------------------------//
template<class Type>
ref<Type>::operator bool() const
{
return !!mpTarget;
}
//-----------------------------------------------------------------//
template<class Type>
bool ref<Type>::operator !() const
{
return !mpTarget;
}
//-----------------------------------------------------------------//
template<class Type>
ref<Type>::operator Type* () const
{
return mpTarget;
}
//-----------------------------------------------------------------//
template<class Type>
Type* ref<Type>::operator ->() const
{
return mpTarget;
}
//-----------------------------------------------------------------//
template<class Type>
Type& ref<Type>::operator *() const
{
return *mpTarget;
}
//-----------------------------------------------------------------//
template<class Type>
ref<Type>& ref<Type>::TakeOver(Type* &rpOther)
{
if(mpTarget) mpTarget->Release();
mpTarget= rpOther;
rpOther= NULL;
return *this;
}
//-----------------------------------------------------------------//
template<class Type>
Type* ref<Type>::GiveAway()
{
Type* pTarget= mpTarget;
mpTarget= NULL;
return pTarget;
}
//-----------------------------------------------------------------//
template<class Type>
Type* &ref<Type>::ExposeNULL()
{
if(mpTarget)
{
mpTarget->Release();
mpTarget= NULL;
}
return mpTarget;
}
//-----------------------------------------------------------------//
template<class Type>
Type* ref<Type>::SplitOff() const
{
if(mpTarget)
{
mpTarget->AddRef();
return mpTarget;
}else{
return NULL;
}
}
//-----------------------------------------------------------------//
template<class Type>
bool operator== (const ref<Type>& rrefA, Type* pB)
{
return rrefA.mpTarget == pB;
}
//-----------------------------------------------------------------//
template<class Type>
bool operator!= (const ref<Type>& rrefA, Type* pB)
{
return rrefA.mpTarget != pB;
}
//-----------------------------------------------------------------//
template<class Type>
bool operator== (Type* pA, const ref<Type>& rrefB)
{
return pA == rrefB.mpTarget;
}
//-----------------------------------------------------------------//
template<class Type>
bool operator!= (Type* pA, const ref<Type>& rrefB)
{
return pA != rrefB.mpTarget;
}
//-----------------------------------------------------------------//
#ifdef _COM
//-----------------------------------------------------------------//
template<class Type>
ref<Type>::ref(REFCLSID rClsID, REFIID rIID, HRESULT *pComErr)
{
ASSERT(sizeof(*this) == sizeof(Type*)); // It was an initial design goal that the size of the class is the size of a pointer.
HRESULT rc;
rc= CoCreateInstance( rClsID
, NULL
, CLSCTX_ALL
, rIID
, (void **) (&mpTarget)
);
if(pComErr)
*pComErr= rc;
if(FAILED(rc))
mpTarget= NULL;
}
//-----------------------------------------------------------------//
template<class Type>
ref<Type>::ref(IUnknown *pUnknown, REFIID rIID, HRESULT *pComErr)
{
ASSERT(sizeof(*this) == sizeof(Type*)); // It was an initial design goal that the size of the class is the size of a pointer.
HRESULT rc;
if(!pUnknown)
{
mpTarget= NULL;
if(pComErr) *pComErr= S_OK;
return;
}
rc= pUnknown->QueryInterface(rIID, (void**) (&mpTarget));
if(pComErr)
*pComErr= rc;
if(FAILED(rc))
mpTarget= NULL;
}
//-----------------------------------------------------------------//
template<class Type>
ref<Type>::ref(__QueryIfcResult& rIfc)
{
ASSERT(sizeof(*this) == sizeof(Type*)); // It was an initial design goal that the size of the class is the size of a pointer.
mpTarget= reinterpret_cast<Type*> (rIfc.GetPointer());
// An addref must not be done, because this has already been done in QueryInterface which was called by the other object.
}
//-----------------------------------------------------------------//
template<class Type>
ref<Type>& ref<Type>::operator =(__QueryIfcResult& rIfc)
{
if(mpTarget) mpTarget->Release();
mpTarget= reinterpret_cast<Type*> (rIfc.GetPointer());
return *this;
}
//-----------------------------------------------------------------//
template<class Type>
__QueryIfcResult ref<Type>::Query(REFIID rIID, HRESULT *pComErr)
{
if(!mpTarget)
{
if(pComErr) *pComErr= S_OK;
return __QueryIfcResult(NULL);
}
void* pNewIfc;
HRESULT rc= mpTarget->QueryInterface(rIID, &pNewIfc);
if(pComErr)
*pComErr= rc;
if(FAILED(rc))
return __QueryIfcResult(NULL);
return __QueryIfcResult(pNewIfc);
}
//-----------------------------------------------------------------//
template<class Type>
ref<IStream> ref<Type>::ThreadMarshal(REFIID rIID, HRESULT *pComErr)
{
ref<IStream> refStream;
HRESULT rc= CoMarshalInterThreadInterfaceInStream(rIID, mpTarget, &refStream.Expose());
if(pComErr)
*pComErr= rc;
return refStream;
}
//-----------------------------------------------------------------//
template<class Type>
void ref<Type>::ThreadUnmarshal(IStream* &rpMarshalledIfc, REFIID rIID, HRESULT *pComErr)
{
if(mpTarget) { mpTarget->Release(); mpTarget= NULL; }
HRESULT rc= CoGetInterfaceAndReleaseStream(rpMarshalledIfc, rIID, (void**)&mpTarget);
rpMarshalledIfc= NULL;
if(pComErr)
*pComErr= rc;
}
//-----------------------------------------------------------------//
template<class Type>
ref<Type>::ref(IStream* &rpMarshalledIfc, REFIID rIID, HRESULT *pComErr)
: mpTarget(NULL)
{
ASSERT(sizeof(*this) == sizeof(Type*)); // It was an initial design goal that the size of the class is the size of a pointer.
HRESULT rc= CoGetInterfaceAndReleaseStream(rpMarshalledIfc, rIID, (void**)&mpTarget);
rpMarshalledIfc= NULL;
if(pComErr)
*pComErr= rc;
}
//-----------------------------------------------------------------//
#endif
//-----------------------------------------------------------------//
// Maybe the following code can overcome
// The type insafity problem of the Query(.) mmethod.
// I haven't tested it, therefore it's commented out.
// Another solution could be to remove the Query(.) method
// and the __QueryIfcResult class completely and put the
// their code into the Query(.) and QueryR(.) #defines.
/*
template<class Type>
class __TypedQueryIfcResult
{
public:
__TypedQueryIfcResult(__QueryIfcResult& rResult)
{
pTarget= reinterpret_cast<Type*>(rResult.GetPointer());
}
~__TypedQueryIfcResult()
{
if( pTarget)
pTarget->Release();
}
protected:
friend class ref<Type>;
// only this may access pTarget.
Type* pTarget;
};
template<class Type>
ref<Type>::ref(__TypedQueryIfcResult& rResult)
: mpTarget(rResult.pTarget)
{
rResult.pTarget= NULL; // This is a TakeOver().
}
template<class Type>
ref<Type>& ref<Type>::operator= (__TypedQueryIfcResult& rResult)
{
TakeOver(rResult.pTarget);
return *this;
}
#define Query( Ifc, Ref) _QueryResult<Ifc>(Ref.Query(IID_#Ifc))
#define QueryR(Ifc, Ref, Res) _QueryResult<Ifc>(Ref.Query(IID_#Ifc, Res))
*/
#endif
|