Home

hersto:  ref.h

Source of ref.h:

Here is a link for right-clicking and downloading the file: ref.h (20.6 K Byte),

and below is its content to view online.



//
//  (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




__.-.__
end of document