Re: Smart Pointers and Microsoft Foundation Classes
This is a multi-part message in MIME format.
------=_NextPart_000_01DB_01C87312.5ACC1970
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
Well out of boredom, I cracked open Visual C++ 2008 and asked the search =
box for "lock" and it coughed up some code sample that demonstrated the =
CLR method.
I am not going to paste it all, but it uses...
#include <msclr/lock.h>
using namespace System;
using namespace System::Threading;
using namespace msclr;
so now I can conjure up a ref class CounterClass but I am more trying to =
make a proxy instead for a simple object
my goal is to proxy operator++ so I can sneak in a piece of code while =
minimizing the modifications needed elsewhere
smart pointers can deliver generic programming, generic is good isn't =
it?
now inside the counter proxy, I can use try { lock l(this); ... } =
everywhere and catch it if it chokes so with a lock in the overload, =
that can cure most of my ills?
code reuse, what have I done!
"Joseph M. Newcomer" <newcomer@flounder.com> wrote in message =
news:eipmr3ll6cs45s2l379av0j6unnnrvjrvk@4ax.com...
The use of ->Lock suggests you are going to use one of the MFC =
synchronization classes,
such as CMutex or CCriticalSection. These classes are deeply flawed =
and should not be
relied on for useful behavior.
Also, locking does not necessarily allow you to deal with the fact =
that one thread is
decrementing to 0 while another thread is incrementing. Also, it does =
not appear the ++
operation is locked correctly, which means it would not be =
thread-safe.
Doug Harrison is probably our resident expert on smart pointers, and =
you should listen to
his advice.
Frankly, I'd skip the whole thing and just make sure that the threads =
are shut down before
the class is destroyed. There are several other issues here that come =
into play and smart
pointers are not going to solve all of them.
joe
On Tue, 19 Feb 2008 14:01:25 -0800, "Roger Rabbit" <roger@rrabbit.com> =
wrote:
Well I can always use a proxy? Why not? This allows me to fix up my =
crappy
old way with a new idea.
template <class T>
class LockProxy {
public:
LockProxy(T* pObj) : pointee (pObj)
{ pointee->Lock(); }
~LockProxy()
{ pointee->Unlock(); }
T* operator->() const
{ return pointee; }
T* operator++() const
{return pointee&++;}
private:
LockProxy& operator=(const LockProxy&);
T* pointee;
};
"Joseph M. Newcomer" <newcomer@flounder.com> wrote in message
news:fkhmr3tut3m85hkq1phgb2n02e02s51g3k@4ax.com...
See below...
On Tue, 19 Feb 2008 11:41:38 -0800, "Roger Rabbit" =
<roger@rrabbit.com>
wrote:
Well I could post my reference counter code, it's a standard =
approach to a
simple counted pointer template I wrote last night to better =
illustrate my
thinking. This code works fine in a single threaded application as =
is. I
was
pondering the move to using this kind of abstraction in a =
multithreaded
situation.
#ifndef counted_pointer_h
#define counted_pointer_h
// class for counted reference semantics
// deletes the object to which it refers when the last CountedPtr =
that
refers to it is destroyed
template <class T>
class CountedPtr {
public:
// initialize pointer with existing pointer
// requires that the pointer p is a return value of new
explicit CountedPtr (T* p=0): ptr(p), count(new long(1)) {}
****
That would be T* p = NULL, not 0; 0 is an integer, NULL should be =
used for
pointers. If
you need to do allocation of count, then you need to do =
initialization,
and there is no
alternative to using 'new'
****
// copy pointer (add another owner)
CountedPtr (const CountedPtr<T>& p) throw(): ptr(p.ptr),
count(p.count) {
++*count;
****
That should be InterlockedIncrement(count) to provide thread safety. =
It
is not clear that
this guarantees safety everywhere, but ++*count is not thread-safe.
****
}
// destructor (if this was the last owner)
~CountedPtr () throw() {
dispose();
}
// assignment (unshare old and share new value)
CountedPtr<T>& operator= (const CountedPtr<T>& p) throw() {
if (this != &p) {
dispose();
ptr = p.ptr;
count = p.count;
++*count;
****
InterlockedIncrement(count);
****
}
return *this;
}
// access the value to which the pointer refers
T& operator*() const throw() {
return *ptr;
}
T* operator->() const throw() {
return ptr;
}
private:
T* ptr; // pointer to the value
long* count; // shared number of owners
void dispose() {
if (--*count == 0) {
****
It should be
if(InterlockedDecrement(count) == 0)
****
delete count;
****
Note this is not thread-safe, really, because while this code is
decrementing the pointer,
another thread could be incrementing it. Furthermore, there is no
possible way of
executing any locking sequence that will make this work.
****
delete ptr;
}
}
};
#endif // counted_pointer_h
"Joseph M. Newcomer" <newcomer@flounder.com> wrote in message
news:r59mr3pu9c2sqhuaadps3o1r9ie67nklp8@4ax.com...
I had to create my own reference-counted pointer class because =
none of
the
classes I could
find (a couple years ago) handled reference-counted semantics.
Because I knew there were several kinds of "smart pointers" =
around,
which
all had
different semantics, I decided that anyone who used this term =
without
actualy saying
std::auto_ptr or boost::shared_ptr probably didn't understand even =
the
basic problems
involved.
joe
On Tue, 19 Feb 2008 11:26:17 -0600, "Doug Harrison [MVP]" =
<dsh@mvps.org>
wrote:
On Tue, 19 Feb 2008 11:42:47 -0500, Joseph M. Newcomer
<newcomer@flounder.com> wrote:
"Joseph M. Newcomer" <newcomer@flounder.com> ha scritto nel
messaggio
news:o65lr31ims8isd5klmg68vfov39cberave@4ax.com...
Smart pointers do not maintain reference counts, as
far as I can tell
If the boost library is being used, SURELY the OP would have said =
"In
using
boost::shared_ptr..."
joe
I think Giovanni's point was that "smart pointer" is a pretty =
generic
term.
For example, I named the smart pointer classes I wrote 10 years =
ago due
to
dissatisfaction with std::auto_ptr "nc_ptr" and "rc_ptr", where =
"nc"
means
"Non-Copyable" and "rc" means "Reference-Counted". It's really
std::auto_ptr that is the oddball here; its weird (though very
occasionally
useful) copy semantics are sort of an unsatisfactory compromise =
between
NC
and RC, which happens sometimes when a standards group invents
something.
Concerning the OP's post, I kind of gave up when he said he was =
using
smart
pointers "to guard against the issues of multithreaded processes". =
That
didn't make any sense to me. Also, when he said, "I do not know if =
my
pointer is unique and owns the object or whether it's a shared =
pointer
to
the pointee object or whether it's a copy on write type of =
object", the
answer to that is, if he needs to know those things, he should use =
a
smart
pointer class that supports them. The Boost library offers several =
types
of
smart pointers with different capabilities that may help that =
aspect of
his
problem.
Joseph M. Newcomer [MVP]
email: newcomer@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Joseph M. Newcomer [MVP]
email: newcomer@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Joseph M. Newcomer [MVP]
email: newcomer@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
------=_NextPart_000_01DB_01C87312.5ACC1970
Content-Type: text/html;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=Content-Type content="text/html; =
charset=unicode">
<META content="MSHTML 6.00.6000.16587" name=GENERATOR></HEAD>
<BODY id=MailContainerBody
style="PADDING-RIGHT: 10px; PADDING-LEFT: 10px; PADDING-TOP: 15px"
bgColor=#ffffff leftMargin=0 topMargin=0 CanvasTabStop="true"
name="Compose message area">
<DIV><FONT size=4>Well out of boredom, I cracked open Visual C++ 2008 =
and asked
the search box for "lock" and it coughed up some code sample that =
demonstrated
the CLR method.<BR><BR>I am not going to paste it all, but it
uses...<BR><BR></FONT><FONT face=Courier size=4>#include
<msclr/lock.h><BR>using namespace System;<BR>using namespace
System::Threading;<BR>using namespace msclr;<BR></FONT></DIV>
<DIV><FONT size=4>so now I can conjure up a <FONT face="Courier =
New">ref class
CounterClass</FONT> but I am more trying to make a proxy instead for a =
simple
object</FONT></DIV>
<DIV><FONT size=4>my goal is to proxy <FONT face="Courier =
New">operator++</FONT>
so I can sneak in a piece of code while minimizing the modifications =
needed
elsewhere</FONT></DIV>
<DIV><FONT size=4>smart pointers can deliver generic programming, =
generic is
good isn't it?</FONT></DIV>
<DIV><FONT size=4></FONT> </DIV>
<DIV><FONT size=4>now inside the counter proxy, I can use <FONT
face="Courier New">try { lock l(this); ... }</FONT> everywhere and =
catch it if
it chokes so with a lock in the overload, that can cure most of my
ills?</FONT></DIV>
<DIV><FONT size=4></FONT> </DIV>
<DIV><FONT size=4>code reuse, what have I done!</FONT></DIV>
<DIV><FONT size=4></FONT> </DIV>
<DIV><FONT size=4></FONT> </DIV>
<DIV><BR>"Joseph M. Newcomer" <newcomer@flounder.com> wrote in =
message
news:eipmr3ll6cs45s2l379av0j6unnnrvjrvk@4ax.com...<BR>> The use of =
->Lock
suggests you are going to use one of the MFC synchronization =
classes,<BR>>
such as CMutex or CCriticalSection. These classes are deeply =
flawed and
should not be<BR>> relied on for useful behavior. <BR>> =
<BR>>
Also, locking does not necessarily allow you to deal with the fact that =
one
thread is<BR>> decrementing to 0 while another thread is =
incrementing.
Also, it does not appear the ++<BR>> operation is locked correctly, =
which
means it would not be thread-safe.<BR>> <BR>> Doug Harrison is =
probably
our resident expert on smart pointers, and you should listen to<BR>> =
his
advice.<BR>> <BR>> Frankly, I'd skip the whole thing and just make =
sure
that the threads are shut down before<BR>> the class is =
destroyed.
There are several other issues here that come into play and =
smart<BR>>
pointers are not going to solve all of them. <BR>> joe<BR>> =
<BR>>
On Tue, 19 Feb 2008 14:01:25 -0800, "Roger Rabbit" =
<roger@rrabbit.com>
wrote:<BR>> <BR>>>Well I can always use a proxy? Why not? This =
allows
me to fix up my crappy <BR>>>old way with a new
idea.<BR>>><BR>>>template <class T><BR>>>class =
LockProxy
{<BR>>>public:<BR>>> LockProxy(T* pObj) : =
pointee
(pObj)<BR>>> { pointee->Lock(); =
}<BR>>>
~LockProxy()<BR>>> { pointee->Unlock();
}<BR>>> T* operator->() =
const<BR>>> {
return pointee; }<BR>>> T* operator++()
const<BR>>> {return
pointee&++;}<BR>>>private:<BR>>> =
LockProxy&
operator=(const LockProxy&);<BR>>> T*
pointee;<BR>>>};<BR>>><BR>>><BR>>>"Joseph M. =
Newcomer"
<newcomer@flounder.com> wrote in message
<BR>>>news:fkhmr3tut3m85hkq1phgb2n02e02s51g3k@4ax.com...<BR>>>=
;>
See below...<BR>>>> On Tue, 19 Feb 2008 11:41:38 -0800, "Roger =
Rabbit"
<roger@rrabbit.com> <BR>>>>
wrote:<BR>>>><BR>>>>>Well I could post my reference =
counter
code, it's a standard approach to a<BR>>>>>simple counted =
pointer
template I wrote last night to better illustrate =
my<BR>>>>>thinking.
This code works fine in a single threaded application as is. I
<BR>>>>>was<BR>>>>>pondering the move to using =
this kind
of abstraction in a
multithreaded<BR>>>>>situation.<BR>>>>><BR>>&g=
t;>>#ifndef
counted_pointer_h<BR>>>>>#define
counted_pointer_h<BR>>>>>// class for counted reference
semantics<BR>>>>>// deletes the object to which it refers =
when the
last CountedPtr that<BR>>>>>refers to it is
destroyed<BR>>>>>template <class =
T><BR>>>>>class
CountedPtr =
{<BR>>>>>public:<BR>>>>> //
initialize pointer with existing =
pointer<BR>>>>>
// requires that the pointer p is a return value of
new<BR>>>>> explicit CountedPtr (T* =
p=0): ptr(p),
count(new long(1)) {}<BR>>>> ****<BR>>>> That would be =
T* p =
NULL, not 0; 0 is an integer, NULL should be used for <BR>>>>
pointers. If<BR>>>> you need to do allocation of count, =
then you
need to do initialization, <BR>>>> and there is =
no<BR>>>>
alternative to using 'new'<BR>>>>
****<BR>>>>> // copy pointer (add another
owner)<BR>>>>> CountedPtr (const
CountedPtr<T>& p) throw():
ptr(p.ptr),<BR>>>>>count(p.count)
{<BR>>>>>
++*count;<BR>>>> ****<BR>>>> That should be
InterlockedIncrement(count) to provide thread safety. It =
<BR>>>>
is not clear that<BR>>>> this guarantees safety everywhere, but =
++*count is not thread-safe.<BR>>>>
****<BR>>>>>
}<BR>>>>> // destructor (if this was the =
last
owner)<BR>>>>> ~CountedPtr () throw()
{<BR>>>>>
dispose();<BR>>>>>
}<BR>>>>> // assignment (unshare old and =
share new
value)<BR>>>>> CountedPtr<T>& =
operator=
(const CountedPtr<T>& p) throw()
{<BR>>>>> if (this =
!=
&p)
{<BR>>>>> &nbs=
p;
dispose();<BR>>>>> &=
nbsp;
ptr =
p.ptr;<BR>>>>>  =
;
count =
p.count;<BR>>>>> &nb=
sp;
++*count;<BR>>>> ****<BR>>>>
InterlockedIncrement(count);<BR>>>>
****<BR>>>>>
}<BR>>>>> return
*this;<BR>>>>>
}<BR>>>>> // access the value to which the =
pointer
refers<BR>>>>> T& operator*() const =
throw()
{<BR>>>>> return
*ptr;<BR>>>>> =
}<BR>>>>> T*
operator->() const throw()
{<BR>>>>> return
ptr;<BR>>>>> }<BR>>>>>
private:<BR>>>>> T*
ptr; // pointer to the
value<BR>>>>> long* count; // =
shared
number of owners<BR>>>>> void dispose()
{<BR>>>>> if =
(--*count ==
0) {<BR>>>> ****<BR>>>> It should be<BR>>>>
if(InterlockedDecrement(count) == 0)<BR>>>>
****<BR>>>>> &=
nbsp;
delete count;<BR>>>> ****<BR>>>> Note this is not =
thread-safe,
really, because while this code is <BR>>>> decrementing the
pointer,<BR>>>> another thread could be incrementing it.
Furthermore, there is no <BR>>>> possible way =
of<BR>>>>
executing any locking sequence that will make this work.<BR>>>> =
****<BR>>>>> &=
nbsp;
delete =
ptr;<BR>>>>>
}<BR>>>>>
}<BR>>>>>};<BR>>>>>#endif //
counted_pointer_h<BR>>>>><BR>>>>><BR>>>>=
>"Joseph
M. Newcomer" <newcomer@flounder.com> wrote in
message<BR>>>>>news:r59mr3pu9c2sqhuaadps3o1r9ie67nklp8@4ax.co=
m...<BR>>>>>>
I had to create my own reference-counted pointer class because none of
<BR>>>>>> the<BR>>>>>> classes I
could<BR>>>>>> find (a couple years ago) handled
reference-counted =
semantics.<BR>>>>>><BR>>>>>>
Because I knew there were several kinds of "smart pointers" around,
<BR>>>>>> which<BR>>>>>> all
had<BR>>>>>> different semantics, I decided that anyone =
who used
this term without<BR>>>>>> actualy =
saying<BR>>>>>>
std::auto_ptr or boost::shared_ptr probably didn't understand even
the<BR>>>>>> basic problems<BR>>>>>>
involved.<BR>>>>>>
joe<BR>>>>>><BR>>>>>> On Tue, 19 Feb 2008 =
11:26:17
-0600, "Doug Harrison [MVP]" =
<dsh@mvps.org><BR>>>>>>
wrote:<BR>>>>>><BR>>>>>>>On Tue, 19 Feb =
2008
11:42:47 -0500, Joseph M.
Newcomer<BR>>>>>>><newcomer@flounder.com>
wrote:<BR>>>>>>><BR>>>>>>>>>>=
;
"Joseph M. Newcomer" <newcomer@flounder.com> ha scritto nel
<BR>>>>>>>>>>
messaggio<BR>>>>>>>>>>
news:o65lr31ims8isd5klmg68vfov39cberave@4ax.com...<BR>>>>>>=
;>>>>>
Smart pointers do not maintain reference counts,
as<BR>>>>>>>>>>> far as I can
tell<BR>>>>>>>><BR>>>>>>>>If =
the boost
library is being used, SURELY the OP would have said
"In<BR>>>>>>>>using<BR>>>>>>>>b=
oost::shared_ptr..."<BR>>>>>>>>
joe<BR>>>>>>><BR>>>>>>>I think =
Giovanni's
point was that "smart pointer" is a pretty
generic<BR>>>>>>>term.<BR>>>>>>>For =
example,
I named the smart pointer classes I wrote 10 years ago due
<BR>>>>>>>to<BR>>>>>>>dissatisfaction=
with
std::auto_ptr "nc_ptr" and "rc_ptr", where "nc"
<BR>>>>>>>means<BR>>>>>>>"Non-Copyabl=
e" and
"rc" means "Reference-Counted". It's
really<BR>>>>>>>std::auto_ptr that is the oddball =
here; its
weird (though
very<BR>>>>>>>occasionally<BR>>>>>>>u=
seful)
copy semantics are sort of an unsatisfactory compromise between
<BR>>>>>>>NC<BR>>>>>>>and RC, which =
happens
sometimes when a standards group invents
<BR>>>>>>>something.<BR>>>>>>><BR>>=
;>>>>>Concerning
the OP's post, I kind of gave up when he said he was
using<BR>>>>>>>smart<BR>>>>>>>pointer=
s "to
guard against the issues of multithreaded processes".
That<BR>>>>>>>didn't make any sense to me. Also, when =
he said,
"I do not know if my<BR>>>>>>>pointer is unique and =
owns the
object or whether it's a shared pointer
<BR>>>>>>>to<BR>>>>>>>the pointee =
object or
whether it's a copy on write type of object",
the<BR>>>>>>>answer to that is, if he needs to know =
those
things, he should use a
<BR>>>>>>>smart<BR>>>>>>>pointer =
class that
supports them. The Boost library offers several
types<BR>>>>>>>of<BR>>>>>>>smart =
pointers
with different capabilities that may help that aspect
of<BR>>>>>>>his<BR>>>>>>>problem.<BR>=
>>>>>
Joseph M. Newcomer [MVP]<BR>>>>>> email:
newcomer@flounder.com<BR>>>>>> Web:
http://www.flounder.com<BR>>>>>> MVP Tips:
http://www.flounder.com/mvp_tips.htm<BR>>>> Joseph M. Newcomer
[MVP]<BR>>>> email: newcomer@flounder.com<BR>>>> Web:
http://www.flounder.com<BR>>>> MVP Tips:
http://www.flounder.com/mvp_tips.htm <BR>> Joseph M. Newcomer =
[MVP]<BR>>
email: newcomer@flounder.com<BR>> Web: =
http://www.flounder.com<BR>> MVP
Tips: http://www.flounder.com/mvp_tips.htm</DIV></BODY></HTML>
------=_NextPart_000_01DB_01C87312.5ACC1970--