Synopsis
========
Add an ability for a smart pointer to adopt a COM pointer
Branches: head
Reviewer: seb, jeffl
Description
===========
Let me begin with a motivating example. Consider the
old-style (non-smart
pointer) code:
// Example 1
SomeClass::someFunc(BasePacket* pPacket)
{
IHXBuffer *pBuf;
pBuf = pPacket->GetBuffer(); // GetBuffer does an
AddRef() before returning
// Do something with pBuf;
HX_RELEASE(pBuf);
}
Remembering to HX_RELEASE(pBuf) can be annoying, and if one
adds
early exit cases, it can be easily overlooked. So we want to
use smart
pointers. Alas, our first attempt will fail:
// Example 2
SomeClass::someFunc(BasePacket* pPacket)
{
SPIHXBuffer spBuf;
spBuf = pPacket->GetBuffer();
// Do something with spBuf;
}
Why does this leak? Because both pPacket->GetBuffer() and
the
SPIHXBuffer constructor do an AddRef(), but the destructor
only
does one Release().
So how can we fix this? The first two ways involve no
changes to HXCOMPtr<>:
// Example 3
SomeClass::someFunc(BasePacket* pPacket)
{
IHXBuffer *pBuf;
pBuf = pPacket->GetBuffer(); // GetBuffer does an
AddRef() before returning
SPIHXBuffer spBuf = pBuf;
HX_RELEASE(pBuf);
// Do something with spBuf;
}
// Example 4
SomeClass::someFunc(BasePacket* pPacket)
{
SPIHXBuffer spBuf;
*(spBuf.AsInOutParam()) = pPacket->GetBuffer();
// Do something with spBuf;
}
That is certainly legal, but ... ugh! In example 3, we wind
up doing
an extra and unnecessary AddRef()/Release() pair, and have
all the
pointer management headaches that smart pointers were to
eliminate;
and in example 4, we have to contort our code to access the
raw COM
pointer inside our smart pointer.
Our other choices involve modifying HXCOMPtr<>. One
possibility would
be to add another constructor, something like this:
// Example 5
SomeClass::someFunc(BasePacket* pPacket)
{
SPIHXBuffer spBuf(pPacket->GetBuffer(), FALSE);
// Do something with spBuf;
}
In this case, the constructor with signature
HXCOMPtr<T>::HXCOMPtr(T*,
HXBOOL) simply copies the T* into m_p and doesn't AddRef()
it. As an
aside, the solution could be further refined to add a
default HXBOOL
parameter to the HXCOMPtr<T>::HXCOMPtr(T*) constructor
(like so:
HXCOMPtr<T>::HXCOMPtr(T*, HXBOOL bAddRef = TRUE)), and
use the value of
the HXBOOL to decide whether to AddRef(), but I don't that
that is the
right way to go, as that limits us to making the function
call only at
construction time.
Another possibility would be to add a new method to
HXCOMPtr<> to
allow a smart pointer to "adopt" a dumb COM
pointer that has already
been AddRef()ed. So the example would look like:
// Example 6
SomeClass::someFunc(BasePacket* pPacket)
{
SPIHXBuffer spBuf;
spBuf.Adopt(pPacket->GetBuffer());
// Do something with spBuf;
}
However, after much discussion, the best option is to add a
global
free template function as follows:
// Example 7
template <typename SmartPointer, typename InterfaceT >
inline
void
HXAdoptInterfacePointer( SmartPointer& sp, InterfaceT*
pI )
{
*(sp.AsInOutParam()) = pI;
}
This is clean and simple, and has the virtue of not breaking
existing
code. Of course, new interfaces should not return such a
pointer, but
this will be quite helpful in dealing with the legacy
interfaces that
currently do this.
Files Affected
==============
common/include/hxcomptr.h
Testing Performed
=================
Verified that leaks (caused by code like example 2) were no
longer happening.
Forced macro-based smart pointers, and verified no leaks
there, either.
Platforms Tested: linux-rhel4-i686
Build verified: linux-rhel4-i686, sunos-5.10-sparc-server,
win32-i386-vc7
QA Hints
===============
Nothing special.
--
Timothy Knox <mailto:tknox real.com>
"Pain heals. Chicks dig scars. Glory ... lasts
forever."
-- Keanu Reeves, _The Replacements_
_______________________________________________
Common-dev mailing list
Common-dev helixcommunity.org
http://lists.helixcommunity.org/mailman/listinfo/comm
on-dev
|