Edit
Attach
Printable
topic end
<!-- * Set TOPICTITLE = Claus Brod: Fingerpointing at smart pointers (31 Dec 2005) --> <style type="text/css"> pre {background-color:#ffeecc;} </style> %STARTINCLUDE% <a name="31"></a> #FingerPointingAtSmartPointers ---+++ [[BlogOnSoftware20051231][Fingerpointing at smart pointers]] (31 Dec 2005) In an ATL COM client which uses =#import= to generate wrapper code for objects, I recently tracked down a subtle reference-counting issue down to this single line: <verbatim> IComponentArray *compArray = app->ILoadComponents(); </verbatim> This code calls a method =ILoadComponents= on an application object which returns an array of components. Innocent-looking as it is, this one-liner caused me quite a bit of grief. If you can already explain what the reference counting issue is, you shouldn't be wasting your time reading this blog. For the rest of us, I'll try to dissect the problem. (And for those who don't want to rely on my explanation: After I had learnt enough about the problem so that I could intelligently feed Google with search terms, I discovered a Microsoft [[http://support.microsoft.com/default.aspx?scid=kb;en-us;242527][Knowledge Base]] article on this very topic. However, even after reading the article, some details were still unclear to me, especially since I don't live and breathe ATL all day.) The =#import= statement automatically generates COM wrapper functions. For =ILoadComponents=, the wrapper looks like this: <verbatim> inline IComponentArrayPtr IApplication::ILoadComponents () { struct IComponentArray * _result = 0; HRESULT _hr = raw_ILoadComponents(&_result); if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this)); return IComponentArrayPtr(_result, false); } </verbatim> =IComponentArrayPtr= is a typedef-ed template instance of [[http://msdn.microsoft.com/library/en-us/vclang/html/_pluslang__com_ptr_t.asp][_com_ptr_t]]. The constructor used in the code snippet above will only call =AddRef= on the interface pointer if its second argument is =true=. In our case, however, the second arg is =false=, so =AddRef= will _not_ be called. The =IComponentArrayPtr= destructor, however, _always_ calls =Release()=. Feeling uneasy already? Yeah, me too. But let's follow the course of action a little bit longer. When returning from the wrapper function, the copy constructor of the class will be called, and intermediate =IComponentArrayPtr= objects will be created. As those intermediate objects are destroyed, =Release()= is called. Now let us assume that the caller looks like above, i.e. we assign the return value of the wrapper function to a =CComPtr<IComponentArray>= type. The sequence of events is as follows: * Wrapper function for =ILoadComponents= is called. * Wrapper function calls into the COM server. The server returns an interface pointer for which =AddRef()= was called (at least) once inside the server. The reference count is 1. * Wrapper function constructs an =IComponentArrayPtr= smart pointer object which simply copies the interface pointer value, but does _not_ call =AddRef()=. The refcount is still 1. Now we return from the wrapper function. In C++, [[http://msdn.microsoft.com/library/en-us/vclang/html/_pluslang_temporary_objects.asp][temporary objects]] are destroyed at the end of the "full expression" which creates them. See also section 6.3.2 in Stroustrup's "Design and Evolution of C++". This means that the following assignment is safe: <verbatim> CComPtr<IComponentArray> components = app->ILoadComponents(); </verbatim> =ILoadComponents= returns an object of type =IComponentArrayPtr=. At this point, the reference count for the interface is 1 (see above). The The compiler casts =IComponentArrayPtr= to =IComponentArray*=, then calls the =CComPtr= assignment operator which copies the pointer and calls =AddRef= on it. The refcount is now 2. At the completion of the statement, the temporary =IComponentArrayPtr= is destroyed and calls =Release= on the interface. The refcount is 1. Just perfect. Now back to the original client code: <verbatim> IComponentArray *compArray = app->ILoadComponents(); </verbatim> Here, we assign to a "raw" interface pointer, rather than to a =CComPtr=, When returning from the wrapper function, the refcount for the interface is 1. The compiler casts =IComponentArrayPtr= to =IComponentArray*= and directly assigns the pointer. At the end of the statement (i.e. the end of the "full expression"), the temporary =IComponentArrayPtr= is destroyed and calls =Release=, decrementing the refcount is 0. The object behind the interface pointer disappears, and subsequent method calls on =compArray= will fail miserably or crash! So while ATL, in conjunction with the compiler's =#import= support, is doing its best to shield us from the perils of reference counting bugs, it won't help us if someone pulls the plug from the ATL force-field generator by incorrectly mixing smart and raw pointers. _This_ kind of reference counting bug would not have occurred if I had used raw interface pointers throughout; the mismatch in calls to =AddRef= and =Release= would be readily apparent in such code. However, those smart pointers _are_ indeed really convenient in practice because they make C++ COM code so much simpler to read. However, they do not alleviate the programmer from learning about the intricacies of reference counting. You better learn your =IUnknown= before you do =CComPtr=. This reminds me of Joel Spolsky's [[http://www.joelonsoftware.com/articles/ThePerilsofJavaSchools.html][The Perils of <nop>JavaSchools]], which is soooo 1990 (just like myself), but good fun to read. %COMMENT{type="below" nonotify="on"}% --- %STOPINCLUDE%
to top
End of topic
Skip to action links
|
Back to top
Edit
|
Attach image or document
|
Printable version
|
Raw text
|
Refresh
|
More topic actions
Revisions: | r1.1
|
Total page history
|
Backlinks
You are here:
Blog
>
BlogOnSoftware20051231
r1.1 - 17 Feb 2006 - 06:51 -
ClausBrod
to top
Blog
This site
2017
:
12
-
11
-
10
2016
:
10
-
7
-
3
2015
:
11
-
10
-
9
-
4
-
1
2014
:
5
2013
:
9
-
8
-
7
-
6
-
5
2012
:
2
-
10
2011
:
1
-
8
-
9
-
10
-
12
2010
:
11
-
10
-
9
-
4
2009
:
11
-
9
-
8
-
7
-
6
-
5
-
4
-
3
2008
:
5
-
4
-
3
-
1
2007:
12
-
8
-
7
-
6
-
5
-
4
-
3
-
1
2006:
4
-
3
-
2
-
1
2005:
12
-
6
-
5
-
4
2004:
12
-
11
-
10
C++
CoCreate Modeling
COM & .NET
Java
Mac
Lisp
OpenSource
Scripting
Windows
Stuff
Changes
Index
Search
Maintenance
Impressum
Datenschutzerklärung
Home
Webs
Atari
Blog
Claus
CoCreateModeling
Klassentreffen
Main
Sandbox
Sommelier
TWiki
Xplm
Jump:
Copyright © 1999-2024 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki?
Send feedback