Topic: Comments on TR1 smart pointers [LONG]


Author: emild@collectivestudios.com (Emil Dotchevski)
Date: Fri, 8 Aug 2003 23:30:54 +0000 (UTC)
Raw View
> Doing nothing is faster that doing somthing.  And yes, I have
> demonstrated that it is faster, why don't you give it a go for yourself
> and see ?

Can you provide a simple example, pointing out exactly what aspect of
it would be slower than shared_ptr? I am not challenging you, I just
want to have something concrete to argue about.

> Incorrect.  Passing a AT_LifeView is about the same as passing a raw
> pointer.

Why would anyone pass a shared_ptr or any other smart pointer by value
is beyond me. What's wrong with passing by const reference? Are you
saving on key strokes???

> Fast enough for what ?

Fast enough so that when you profile your code, you don't see
shared_ptr pop up as an issue. Show me real world application that
clearly demonstrates that shared_ptr is a performance bottleneck, and
we may have a better discussion.

> I make a claim that the fastest techique by far is AT_Life* and I can
> and have substantiated even in the test that Richard produced (with some
> minor tweaks).

I am not challenging whether AT_Life is faster to pass by value. What
I am saying is that I don't care how much faster it is if shared_ptr
is already fast enough so that its performance causes no issues.

> If you don't want
> optimal code then why bother doing it in C++ ?  Why ask people to go
> through the pain of learning C++ when they could (and should) use a
> simpler language ?

Are you saying that 100% of the C++ code you write can not be made
more optimal?

One of the most important properties of computer programs, C++ or
otherwise, is to be simple and easy to read by (other) people.
Performance is a concern in only very very limited portions of the
code. Optimizing code that is fast enough is not just redundant, it is
counter-productive because it introduces bugs, makes the code more
complex, and lowers the level of abstraction, forcing the readers to
consider details with obscure relevance to the problem at hand.

--Emil

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Wed, 30 Jul 2003 14:24:50 +0000 (UTC)
Raw View
Emil Dotchevski wrote:
>>shared_ptr is wonderful if you have cycles to waste and don't want to
>>bother having to learn how to write faster code.  If that's the case,
>>use perl, why bother with C++ ?
>
>
>>From a certain point of view, I can say the same thing about C++: it
> is wonderful if you have cycles to waste. As I already pointed out in
> my other posts, C++ optimizers are at unfair disadvantage to some
> others because in C/C++ the compiler has to assume that hidden aliases
> may exist.
>
> Your posts sound as if your smart pointer solution is the fastest
> possible. As far as I can tell, so far you have failed to provide any
> evidence to support your claim.

Doing nothing is faster that doing somthing.  And yes, I have
demonstrated that it is faster, why don't you give it a go for yourself
and see ?

>
> The only time shared_ptr could be slower than your code is when you
> construct a new object because at this time shared_ptr is likely to
> allocate memory ("likely", because it is up to each implementation to
> decide what exactly happens). If memory allocation performance is an
> issue, your design needs work, period.

Incorrect.  Passing a AT_LifeView is about the same as passing a raw
pointer.

>
> But that's not the point. Even if shared_ptr is slower in some
> situations, what matters is whether or not this difference can be
> detected in a real world application. In other words, can we afford
> shared_ptr? I think the answer is yes. Therefore, you should not focus
> on performance at all: shared_ptr is fast enough. Its usability,
> abstraction power and implementation hiding properties are what really
> sets it apart, IMO.
>

Fast enough for what ?  Before you can determine "enough" you need to
have a set of assumptions and if you can't write the assumptions with
the claim then this is totally subjective.

I make a claim that the fastest techique by far is AT_Life* and I can
and have substantiated even in the test that Richard produced (with some
minor tweaks).

You need to help me understand what you find so difficult to get.  There
is no question the the AT_Life* pointer technique is about as fast as
you can get.  It provides a model that allows the programmer to provide
some valuable knowledge as to what assumptions can be made and this can
be used to create near optimal if not optimal code.  If you don't want
optimal code then why bother doing it in C++ ?  Why ask people to go
through the pain of learning C++ when they could (and should) use a
simpler language ?


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Wed, 30 Jul 2003 14:25:17 +0000 (UTC)
Raw View
Richard Smith wrote:
> Gianni Mariani wrote:
>
>
>>If I remove the virtual AddRef/Release methods
>
>
> What do you actually mean by that?  Removing the virtual
> keyword doesn't prevent them from being virtual as they
> override virtual functions in the base class.
>
>
>>for at_test and disable
>>threading in shared_ptr, then we have:
>>
>>at_test 1.12 sec
>>
>>boost_test 1.97 sec
>
>
> What happens to AT_Assert when RELEASE_BUILD=1?  Would I be
> correct in assuming that it is a macro that exands to
> nothing?  (I.e. a bit like assert when NDEBUG is defined.)

No, it's still evaluated.  I checked before I posted.

>
> If so the inner loop of at_test has half as many function
> calls as the inner loop of boost_test.  The test was
> supposed to include evaluation of the argument to assert;  I
> could equally have written
>
>   extern bool foo;
>   foo ^= ( t.get_b() == t.get_a() );
>
> The point was to have something that the compiler wouldn't
> optimise away, *AND* that would prevent the compiler
> optimising away the previous statements.  In my experience,
> an assert like that will very rarely be optimised away.

It's not optimized away in my test.

I tested it again - using the same assert and the virtual methods
removed.  Test time is 1.11 seconds - here's the diff.

  diff ../../../../at_test.cc at_test.cpp
0a1,3
 >
 > #define BOOST_DISABLE_THREADS
 >
5c8,9
< #include "timer.h"
---
 > #include <boost/shared_ptr.hpp>
 > //#include "timer.h"
9c13
<   class data : boost::noncopyable, public AT_LifeControl {
---
 >   class data : boost::noncopyable {
14c18
<     virtual int AddRef()
---
 >     int AddRef()
16c20
<     virtual int Release()
---
 >     int Release()
40c44
<   timer t;
---
 > //  timer t;
48c52
<   std::cout << t.get_time() << "s" << std::endl;
---
 > //  std::cout << t.get_time() << "s" << std::endl;

removing the assert line bring the test down to 0.67 seconds which is
getting way too short to be meaningful but does demonstrate that it is
not being optimized away.

>
> By removing half of the innermost loop in one example, you
> shouldn't be surprised that you're code appears to be
> approaching twice the speed.  If you change the test back to
> how I wrote it (i.e. put the AT_Assert back to an assert),
> then you'll get more meaningful results.
>
>   boost_test:  1.69s
>   at_test:     1.81s
>
> (Note I have not removed the virtual function calls to
> AddRef and Remove because I don't understand how you intend
> to do it.)

In all fairness AT_Life* is a different model to shared_ptr and it's
difficult to measure apples and apples because the boundaries are different.

AT_Life* and shared_ptr differ in that the mechanism that actually
peforms the reference counting comes from different places.  AT_Life* is
only the trying to provide a solution to the smart pointer mechanism and
it's up to the application provider to provide whatever counting
implementation they desire.  If it needs to be MT safe then it's up to
the application to do that.  I have an implementation that mimics
interface of COM simply because it's a well known interface.  It can
just as easily be implemented in some other way.

While this is all very interesting, I'm loosing sight as to why I need
to defend this any more.

Let's see if I can conclude my assertions.

a) There is an interesting alternative to reference counting support
that provides a thinner layer that shared_ptr.

b) Using 3 classes you can eliminate much of the reference counting
costs. (yet provide full exception safety - some tweaks needed here but
they're fairly trivial).

c) This is an alternative approach that needs to be considered as it
(IMHO) aligns itself more closely to the principles of C++, especially
the zero overhead rule.

The basic underlying principle of the AT_Life* classes is to eliminate
the need to do anything where possible which infers pass/return
AT_LifeView where possible AT_LifeLine where you must and AT_LifeTime never.

In some ways AT_LifeLine and AT_LifeView embody the two different "move"
sematics of AT_LifeTime and so this is another way to skin the move vs
copy issue.  This model could be more generally applied but I digress...

If you apply this principle liberally to your original test, then
AT_Life* runs in 0.92 seconds compared to the boost test of 1.98
seconds.  Since we compare apples to oranges it's meaningless.

diff ../../../../at_test.cc at_test.cpp
0a1,3
 >
 > #define BOOST_DISABLE_THREADS
 >
5c8,9
< #include "timer.h"
---
 > #include <boost/shared_ptr.hpp>
 > //#include "timer.h"
9c13
<   class data : boost::noncopyable, public AT_LifeControl {
---
 >   class data : boost::noncopyable {
14c18
<     virtual int AddRef()
---
 >     int AddRef()
16c20
<     virtual int Release()
---
 >     int Release()
26,27c30,31
<   AT_LifeTime<data*> get_a() const;
<   void set_b( const AT_LifeTime<data*>& );
---
 >   AT_LifeView<data*> get_a() const;
 >   void set_b( const AT_LifeView<data*>& );
34,35c38,39
< AT_LifeTime<tester::data*> tester::get_a() const { return a; }
< void tester::set_b( const AT_LifeTime<tester::data*>& val ) { b = val; }
---
 > AT_LifeView<tester::data*> tester::get_a() const { return a; }
 > void tester::set_b( const AT_LifeView<tester::data*>& val ) { b = val; }
40c44
<   timer t;
---
 > //  timer t;
42c46
<     tester t( AT_LifeLine<tester::data*>( new tester::data ) );
---
 >     tester t( new tester::data );
48c52
<   std::cout << t.get_time() << "s" << std::endl;
---
 > //  std::cout << t.get_time() << "s" << std::endl;




---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: brangdon@cix.co.uk (Dave Harris)
Date: Wed, 30 Jul 2003 23:48:47 +0000 (UTC)
Raw View
gi2nospam@mariani.ws (Gianni Mariani) wrote (abridged):
> I have found that the best designs I have seen are ones where all
> classes are built with small fundamental sub-components to build more
> complex components.  This leads to a design principle that the
> "threshold for defining a new class is very low".  Hence hiding the
> reference counting in a separate class and adding the extra overhead to
> manage it contrary to that design principle.

Does that always work? If I have some 3rd party base class, it sounds
like I need to add intrusive reference counting with code like:

    struct Base { virtual ~Base(); };

    struct RefCountedBase : Base, RefCount {
        // duplicate constructors here.
    };

    typedef smart_ptr<RefCountedBase> BasePtr;

What happens with classes derived from Base? Such classes won't inherit
from RefCountedBase, so BasePtr can't point to them. If I make:

    struct Derived : Base {};

    struct RefCountedDerived : Derived, RefCount {
        // duplicate constructors here.
    };

then RefCountedDerived still doesn't inherit from RefCountedBase. Are you
suggesting something like:

    struct RefCountedDerived : RefCountedBase, virtual Derived {
        // duplicate constructors here.
    };

? But this won't work, and if it did work it'd be inefficient.

What happens if Derived is not even exposed - if the 3rd party interface
looks like:

    Base *make_base(); // Use delete when you are finished.

where make_base() returns some subclass I haven't seen?

Don't get me wrong, I love intrusive counting, but there seem to be a lot
of cases where it can't be used.


> If there are solutions that have 0 run-time performance costs and are
> 100% safe, then I'm all for it.  In my opinion the C++ standard should
> keep performance as a primary objective above all others because this
> is the only reason you can justify that long earning curve.  No matter
> what you add at this point, you can only increase the complexity of the
> standard and hence the trade-off should remain intact.

The AT_LifeLine stuff is orthogonal to the intrusive count issue, right?
You could presumably add something like AT_LifeLine as an extension of
shared_ptr, and submit it for approval.

As I understand it, the key idea here that move is more efficient than
copy+delete. The problem is that this is a very general insight, which
deserves a very general solution. AT_LifeLine is not general; it only
works for smart pointers.

AT_LifeLine is used to mark pointers which want to give up their
ownership. A more general approach might be to mark such pointers by
wrapping them in a type, like:

    void demo() {
        shared_ptr<int> p( new int );
        use( move_t< shared_ptr<int> >( p ) );
    }

where move_t has a partial specialisation for shared_ptr<T> which gives it
the access it needs to move pointers around without refcounting overhead.

There is a more mature proposal at:
  http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1377.htm

which involves a language extension. This was mentioned earlier, but I am
not sure the implications registered with you. The idea is to write
something like:

    void demo() {
        shared_ptr<int> p( new int );
        use( static_cast<shared_ptr<int> &&>( p ) );
    }

where the static_cast effectively tells the compiler that p is fair game
for optimisations. There are other proposals around. Of course, none of
them may be accepted, and we may be stuck with a special-case solution
like AT_LifeLine. However, in practice I doubt AT_LifeLine will be
accepted so long as more general solutions are in the offing.

-- Dave Harris, Nottingham, UK

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Fri, 1 Aug 2003 01:43:42 +0000 (UTC)
Raw View
Dave Harris wrote:
> gi2nospam@mariani.ws (Gianni Mariani) wrote (abridged):
>
>>I have found that the best designs I have seen are ones where all
>>classes are built with small fundamental sub-components to build more
>>complex components.  This leads to a design principle that the
>>"threshold for defining a new class is very low".  Hence hiding the
>>reference counting in a separate class and adding the extra overhead to
>>manage it contrary to that design principle.
>
>
> Does that always work? If I have some 3rd party base class, it sounds
> like I need to add intrusive reference counting with code like:
>
>     struct Base { virtual ~Base(); };
>
>     struct RefCountedBase : Base, RefCount {
>         // duplicate constructors here.
>     };
>
>     typedef smart_ptr<RefCountedBase> BasePtr;
>
> What happens with classes derived from Base? Such classes won't inherit
> from RefCountedBase, so BasePtr can't point to them. If I make:
>
>     struct Derived : Base {};
>
>     struct RefCountedDerived : Derived, RefCount {
>         // duplicate constructors here.
>     };
>
> then RefCountedDerived still doesn't inherit from RefCountedBase. Are you
> suggesting something like:
>
>     struct RefCountedDerived : RefCountedBase, virtual Derived {
>         // duplicate constructors here.
>     };
>
> ? But this won't work, and if it did work it'd be inefficient.
>
> What happens if Derived is not even exposed - if the 3rd party interface
> looks like:
>
>     Base *make_base(); // Use delete when you are finished.
>
> where make_base() returns some subclass I haven't seen?
>
> Don't get me wrong, I love intrusive counting, but there seem to be a lot
> of cases where it can't be used.
>

I see what you're saying but I'm lost as to your conclusion.

The class design is somthing the application writer has control over and
I suspect that if performance is important enough, the application
writer will be able to inherit or otherwise provide the right scheme to
create an optimal class.

Why not provide a class like :

class RefCountedApplication
   : public RefCounted
{
public:
 Base  m_stuff;

 Base & get_Base ()
 {
  return m_stuff;
 }
};

The point I'm making is that by the time you create the "Application"
class above you'll find that it's an interesting class 95% of the time
because you'll find there is alot more than one object that shared it's
life-time.  It's impossible to show in this example what I mean, we'll
need some real-life examples.

>
>
>>If there are solutions that have 0 run-time performance costs and are
>>100% safe, then I'm all for it.  In my opinion the C++ standard should
>>keep performance as a primary objective above all others because this
>>is the only reason you can justify that long earning curve.  No matter
>>what you add at this point, you can only increase the complexity of the
>>standard and hence the trade-off should remain intact.
>
>
> The AT_LifeLine stuff is orthogonal to the intrusive count issue, right?
> You could presumably add something like AT_LifeLine as an extension of
> shared_ptr, and submit it for approval.

OOOOh - another orthogonal kinda guy.  I lay claim to inventing that
term - wait until we get to sporadic classes ... :-)

I thought about that.  I think shared_ptr does too much already.  I
think there is room for a thin system of reference counting smart
pointers to do a very small task very efficiently.


>
> As I understand it, the key idea here that move is more efficient than
> copy+delete. The problem is that this is a very general insight, which
> deserves a very general solution. AT_LifeLine is not general; it only
> works for smart pointers.
>
> AT_LifeLine is used to mark pointers which want to give up their
> ownership. A more general approach might be to mark such pointers by
> wrapping them in a type, like:
>
>     void demo() {
>         shared_ptr<int> p( new int );
>         use( move_t< shared_ptr<int> >( p ) );
>     }
>
> where move_t has a partial specialisation for shared_ptr<T> which gives it
> the access it needs to move pointers around without refcounting overhead.

Yes that may be possible.

But I could write the code above by introducing a new simple template like:

 template <typename T>
 class ref_counted
   : public reference_counted
 {
  public:
  T m_value;
  T & get()
  {
   return m_value;
  }
 };

 void demo()
 {
  AT_LifeTime< ref_counted<int> * >
    p( new ref_counted<int> );

  use( p );
 }

 void use( AT_LifeView< ref_counted<int> > p );


Still, I think the reference_counted template above is usually a design
error.


>
> There is a more mature proposal at:
>   http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1377.htm
>
> which involves a language extension. This was mentioned earlier, but I am
> not sure the implications registered with you. The idea is to write
> something like:
>
>     void demo() {
>         shared_ptr<int> p( new int );
>         use( static_cast<shared_ptr<int> &&>( p ) );
>     }
>
> where the static_cast effectively tells the compiler that p is fair game
> for optimisations. There are other proposals around. Of course, none of
> them may be accepted, and we may be stuck with a special-case solution
> like AT_LifeLine. However, in practice I doubt AT_LifeLine will be
> accepted so long as more general solutions are in the offing.

They did register and I'm all for more general solutions.  The one issue
I have is that there are different kinds of "move" semantics and at the
end of the day, describing the semantics in a class may be all that is
needed.  In other words : What may be missing in the language wrt move
semantics that can't be described already with a few choice classes ?
Take AT_LifeLine and AT_LifeView for example, what more syntactic sugar
in C++ do you really need ?  (This question is not rhetorical - I'd like
to know, I probably need to go back and read that paper again.)

G

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: brangdon@cix.co.uk (Dave Harris)
Date: Fri, 1 Aug 2003 20:05:20 +0000 (UTC)
Raw View
gi2nospam@mariani.ws (Gianni Mariani) wrote (abridged):
> > Don't get me wrong, I love intrusive counting, but there seem to be a
> > lot of cases where it can't be used.
> >
>
> I see what you're saying but I'm lost as to your conclusion.
>
> The class design is somthing the application writer has control over

My intended point with the 3rd party classes is that the application
writer /not/ have control over them.


> Why not provide a class like :
>
> class RefCountedApplication
>    : public RefCounted
> {
> public:
>  Base  m_stuff;
>
>  Base & get_Base ()
>  {
>   return m_stuff;
>  }
> };

I don't see how this supports polymorphic pointers. That is, how a
smart_ptr<Base> can point to an instance of Derived.

What might work is to replace the:
  Base  m_stuff;

with a pointer:
  Base  *m_pStuff;

Of course this has problems of its own, and by the time we've fixed them
we are on the way to reimplementing shared_ptr.


> The point I'm making is that by the time you create the "Application"
> class above you'll find that it's an interesting class 95% of the time
> because you'll find there is alot more than one object that shared it's
> life-time.  It's impossible to show in this example what I mean, we'll
> need some real-life examples.

By "interesting", you mean such a class will acquire extra member
functions or variables? I think that's unlikely.

If you just mean that RefCountedApplication will be used in several
places, then yes of course. Nowadays I use something like:
    class Xxx { ... };
    typedef boost::shared_ptr<Xxx> XxxPtr;

because XxxPtr is used in enough places to make it worthwhile. But XxxPtr
is not interesting in the sense of deserving effort. It does not need to
be a class. New functions are added to Xxx, or to shared_ptr, not to
XxxPtr. At least in my experience.


> OOOOh - another orthogonal kinda guy.  I lay claim to inventing that
> term - wait until we get to sporadic classes ... :-)

I think "orthogonal" in the programming sense is pretty old - at least
back to the '60s.


> I think shared_ptr does too much already.  I think there is room for
> a thin system of reference counting smart pointers to do a very small
> task very efficiently.

Fair enough. Nothing in the standard prevents you writing your own
components, and they can often be more efficient than the standard ones by
being less general. Smart pointers are no exception. The controversial
step is claiming such a partial solution should be standardised.

Shared_ptr is partly about enabling interoperability between components
produced by different vendors. This means it is aimed at interfaces.
Interfaces need to be stable, which means they need to be general, so they
can adapt to future changes. So I think a general smart pointer should be
the first thing to be standardised. A less general one can come later, if
it is still needed.


> But I could write the code above by introducing a new simple template
> like:

I'm not sure what point you are making here. There are lots of ways to
denote move semantics, some simple.


> They did register and I'm all for more general solutions.  The one
> issue I have is that there are different kinds of "move" semantics and
> at the end of the day, describing the semantics in a class may be all
> that is needed.  In other words : What may be missing in the language
> wrt move semantics that can't be described already with a few choice
> classes ?

There are indeed proposals for move semantics which don't need language
changes. I don't have any references to hand, sorry.

The perceived challenge is to make the syntax transparent to the user at
the point of call. The && proposal tries to avoid the need for the user to
describe semantics explicitly by exploiting the compiler's knowledge about
temporaries.

-- Dave Harris, Nottingham, UK

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Sat, 2 Aug 2003 01:18:23 +0000 (UTC)
Raw View
gi2nospam@mariani.ws (Gianni Mariani) wrote in message news:<bg114k$bdo@dispatch.concentric.net>...
> Bob Bell wrote:
> > Irrelevant. Your argument in favor of intrusive ref-counting is not
> > the point. The point is that others have argued for why they need
> > external ref-counting, and you've ignored those arguments.
>
> I don't believe I ignored them.  I acknowledged them and I gave reason
> that a practice I use that serves me well, eliminates that need.

And it's been pointed out that your practice will not serve others as
well, therefore the need for external ref-counting has not been
eliminated. That's what you've ignored.

Most people have not said that intrusive reference counting is wrong
or bad or even unnecessary. Even I recognize the need for it.
Sometimes it's dictated to you, as when you use something like COM.
What has been said is that sometimes external reference counting is a
better alternative. I don't recall you ever acknowledging that point
-- on the contrary, it seems that you argue instead why intrusive ref
counting is all anyone would ever need.

To me, that looks a lot like ignoring the arguments for external
reference counting. (It's possible that you have acknowledged that
external reference counting is necessary sometimes and I've missed it
-- if so, then I'll retract.)

> > However, since you asked, yes I understood the argument; I just don't
> > agree with it. I personally have no use for intrusive reference
> > counting. None of the types I use support intrusive reference
> > counting, and I wouldn't design one that way without a very compelling
> > reason to do so. Mainly this is because the class involved usually has
> > some other responsibility -- the concept it represents; adding
> > reference counting means overloading the behavior of the class for
> > reasons that have nothing to do with the class' concept. Thus I find
> > external reference counting much more useful, since it doesn't require
> > me to perform such overloading.
>
> Is inheritance a part of the C++ language exactly for this purpose ?

I'm not sure what the relevance of that question is, but a practice
that I've used that serves me well is to avoid inheritance when I can.
The rule is, if two approaches solve a problem, prefer the solution
with the weaker coupling between classes. Composition (as used in
shared_ptr) is weaker than inheritance (as you're apparently
advocating with AT_* pointers).

> I have found that the best designs I have seen are ones where all
> classes are built with small fundamental sub-components to build more
> complex components.  This leads to a design principle that the
> "threshold for defining a new class is very low".  Hence hiding the
> reference counting in a separate class and adding the extra overhead to
> manage it contrary to that design principle.
>
> In other words, I think the shared_ptr extrusive reference counting
> model may be a poor design choice to the alternative described above
> almost every time, to the point where doing it at all probably leads to
> a poorer design.

I must confess that I'm not sure if I'm following these two
paragraphs. It sounds like you're advocating a modular design approach
in the first paragraph, with responsibilities separated into different
classes. That sounds good to me, but I don't see how that leads to a
rejection of external reference counting.

Perhaps you could elaborate on why external reference counting is a
poor design choice. What bad consequences does it have? Perhaps you've
already done this and I've missed it; if so, I'd appreciate a pointer
(no pun intended ;-) to it.

> > An example given to you for why external reference counting was of a
> > smart pointer to a std::map, which doesn't have reference counting
> > functions built in. Your response was essentially to build a class
> > which contains the std::map and implements the reference counting. By
> > doing this, you have merely implemented part of the external reference
> > counting that is done by types like boost::shared_ptr. The std::map is
> > still not reference counting; from the point of view of the std::map,
> > it has been placed into something which does external reference
> > counting. The only difference between this approach and something like
> > boost::shared_ptr is that you're doing more work.
>
> You omit the most important piece, I find very few classes that are
> "new()ed" are as simple as a map.  More often than not, creating a class
> which is a combination of multiple different concepts is the norm.
> Hence inheriting a reference counting class is a small complexity for
> significant impact positive to performance.

I omitted that piece because it's irrelevant. It's not a question of
how complex the dynamically allocated object is, it's a question of
whether or not that object should provide ref-counting functions. It
may be the norm to make classes that combine multiple different
concepts, but that doesn't make it a good practice. In my experience,
classes that combine different unrelated concepts are often surprising
and confusing, become maintenance/extension bottlenecks, and are often
primary targets for refactoring while making such refactoring
difficult.

Classes that combine multiple related concepts, on the other hand, are
a different story. They are often unsurprising, consistent with the
domain model and the rest of the design, easy to explain and easy to
understand. (Then there's the question of how to implement the
combination. As pointed out above, most of the time inheritance is not
the way to go.)

Thus I find that only very rarely should an object provide
ref-counting member functions. Example: Suppose I have the following
class:

class HighLevelConcept {
// Some high-level concept represented here
};

The likelihood that the concept represented by this class is related
to reference counting is quite small. Thus, mixing reference counting
into it is inappropriate. However, if I discover that I need to keep a
ref-counted shared pointer to objects of this class, the only way to
get this to work with AT_* pointers is to mix in reference counting
behavior somehow. In other words, AT_* pointers force me to corrupt
the concept represented by my class.

One approach is to add a reference-counting base class to
HighLevelConcept:

class HighLevelConcept : public RefCounter {
// Same as before, but, overloaded with reference counting
// concept
};

This option may not be available to me if I can't change
HighLevelConcept. Other possibilities:

class RC_HighLevelConcept : public HighLevelConcept, public RefCounter
{
// Same problem: high-level concept, overloaded with reference
// counting concept
};

or

class RC_HighLevelConcept : public RefCounter {
// Contains a HighLevelConcept object and provides conversions
// and/or forwarding functions to the contained object
};

Also, errors will occur if a client using an AT pointer to one of
these objects calls the object's reference counting functions
directly. In other words, the reference count (on which the pointer's
correct behavior depends) is not encapsulated by the pointer.

Another problem is the coupling between a high-level class like
HighLevelConcept and a low-level class like RefCounter; you're mixing
abstraction levels, and that always seems like a bad sign.

Further, presumably, following this technique, you'd have RefCounter
used as a base by many classes. So RefCounter becomes a dependency
bottleneck. I guess I could avoid this by doing something like:

class RC_HighLevelConcept : public HighLevelConcept {
// Defines reference counting functions directly.
// Same problem: high-level concept, overloaded with reference
// counting concept
};

But this is bad because I'm writing the same code more than once.

A problem demonstrated by all of these approaches is that AT_*
pointers require the pointed-to object to do the reference counting,
because AT is an incomplete solution. It doesn't work "out of the box"
because the client must supply missing pieces. (You may be able to
mitigate the missing functionality by providing mixin/helper classes
like RefCounter above, but it's still a hole in the system.)

Finally, as Dave Harris asked (and as far as I can see you haven't
answered), how would you obtain AT pointers to derived classes of
HighLevelConcept?

Compare that with:

boost::shared_ptr<HighLevelConcept>;

Doesn't require corrupting/overloading of the concept behind the
pointed-to class. No extraneous coupling between the pointed-to class
and low-level classes like RefCounter. Doesn't require mixing
abstraction levels. Encapsulates the reference count. No missing
pieces; works out of the box. No problem representing derived classes
of the pointed-to class.

Bob

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Thu, 24 Jul 2003 02:12:33 +0000 (UTC)
Raw View
Richard Smith wrote:
> Gianni Mariani wrote:
>
>
>>I think it's a mistake to do somthing without really understanding what
>>it is smart_ptr really solves.  As it stands at this time, I can see
>>areas that it would reduce efficiency of code that I have written and I
>>already use reference counting smart pointers.  To the projects I have
>>been involved with it's a mistake to use shared_ptr.
>
>
> I'm getting a bit fed up with these unsubstantiated claims
> that AT_Life* is faster than boost::shared_ptr, and so I've
> written some tests to see how they really compares.  First,
> some comments on how I've tested them.
>
> I've compiled everything using g++ 3.2.1, with "-O2"
> optimisation (this is a fairly standard optimisation level
> for release builds).  I've used version 1.30 of boost and
> the default STL provided with g++.  The AT_Life* code is as
> provided in your post at the beginning of this thread.
>
> I've built the boost shared_ptr with BOOST_DISABLE_THREADS
> defined; similarly, I've implemented the AddRef and Release
> methods in your hierarchy in a non-atomic manner.  This
> should be a fair comparision.
>
> I used the following correspondence between AT_Life*
> pointers and standard ones:
>
>    AT_LifeLine<T*>       std::auto_ptr<T>
>    AT_LifeTime<T*>       boost::shared_ptr<T>
>    AT_LifeView<T*>       T const&
>
> The test programs can be found at
>
>   http://www.ex-parrot.com/~richard/scratch/at_test.cc
>   http://www.ex-parrot.com/~richard/scratch/boost_test.cc
>
> The timings I got demonstrated that the standard framework
> is significantly faster than yours.  (The standard framework
> benchmarked at 1.86s, yours at 2.68s.)
>
> I suggest that you do one of the following:
>
>   1) provide alternative evidence that demostrates your
>      smart pointer framework to be faster when used as
>      intended; or
>
>   2) accept that the standard smart pointer framework is
>      faster than yours.


Richard, thanks for taking you time and putting together this benchmark.

Unfortunately you omit a number of critical points in you discussion.

Firstly, you choose to use virtual methods for performing the reference
counting on the AT example and secondly you use of auto_ptr is clearly a
specific case and not as general as AT_LifeLine.

Also, your test is not indicative of how a real-life system would work.
  Memory utilization as well as instruction count are huge factors.

I don't have time right now to toy with this, but if you need a few more
hints as to how you can create a true apples and apples comparison, drop
me a line.

G


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: richard@ex-parrot.com (Richard Smith)
Date: Fri, 25 Jul 2003 01:27:31 +0000 (UTC)
Raw View
Gianni Mariani wrote:

> Unfortunately you omit a number of critical points in you discussion.
>
> Firstly, you choose to use virtual methods for performing the reference
> counting on the AT example

Perhaps I need to requote your original code:  [slightly
abridged]

| /**
| * AT_LifeControl is an abstract base class for any class whose lifetime
|  * is managed by means of reference counting.
|  */
|
| class AT_LifeControl
| {
|     public:
|     virtual ~AT_LifeControl();  // Virtual destructor
|     virtual int AddRef() = 0;
|     virtual int Release() = 0;
| };

"AT_LifeControl is an abstract base class for any class
whose lifetime is managed by means of reference counting."
In my benchmark, tester::data is managed by means of
reference counting, so, as per your comment, I made it a
sub class of AT_LifeControl.


> and secondly you use of auto_ptr is clearly a
> specific case and not as general as AT_LifeLine.

It solved the particular problem required in the test
example, and a common requirement in many applications using
dynamically allocated memory.

> Also, your test is not indicative of how a real-life system would work.
>   Memory utilization as well as instruction count are huge factors.

Do you mean (i) memory utilisation affects speed, or (ii)
the amount of memory required by an smart pointer can be
important for its own sake?  In response to (i), I would
say, yes, it will, but a priori would expect the two smart
pointer frameworks to scale similarly in this respect.  If
you mean (ii), then your framework does save a small amount
of space per reference counted object.  This is at the
expense of not supporting (a) deletion of incomplete types,
(b) correct casting of smart pointers to objects without
virtual destructors, and (c) weak pointers.  I don't have a
problem with this extra memory overhead -- it still only
requires one additional allocation per object.

> I don't have time right now to toy with this, but if you need a few more
> hints as to how you can create a true apples and apples comparison, drop
> me a line.

I don't want a "few more hints" on how to write a test
suite.  What I want is for you to either accept that your
smart pointer framework is less efficient that Boost's, or
to provide hard evidence that I'm wrong, and yours is more
efficient.  Almost every post in this thread has asked you
for such evidence and you have failed to provide any.

As many others have said, the Standard has now accepted the
Boost smart_ptr class into TR1.  If you really feel that a
mistake has been made in accepting this, you will have to
work hard to demonstrate this and persuade others round to
your way of thinking.  If your arguments centre around
efficiency, this will require you to write a benchmark
demonstrating it.  If you're not prepared to do this, you
might as well give up.

--
Richard Smith

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Fri, 25 Jul 2003 02:32:32 +0000 (UTC)
Raw View
gi2nospam@mariani.ws (Gianni Mariani) wrote in message news:<bfmu62$gvm@dispatch.concentric.net>...
> Richard Smith wrote:

[snip Richard's telling tests...]

> Richard, thanks for taking you time and putting together this benchmark.
>
> Unfortunately you omit a number of critical points in you discussion.
>
> Firstly, you choose to use virtual methods for performing the reference
> counting on the AT example and secondly you use of auto_ptr is clearly a
> specific case and not as general as AT_LifeLine.
>
> Also, your test is not indicative of how a real-life system would work.
>   Memory utilization as well as instruction count are huge factors.
>
> I don't have time right now to toy with this, but if you need a few more
> hints as to how you can create a true apples and apples comparison, drop
> me a line.

Let's see if I can summarize:

-- you claim that shared_ptr is too slow, but have offered no proof
-- you claim that an intrusive ref-counting system is all that's
necessary, but have ignored arguments to the contrary
-- your framework has problems with exception safety, but at times you
argue that they are trivial to fix, and at others argue that they are
unimportant
-- Richard took the time to create some benchmarks which show your
performance claims are false
-- you claim Richard's tests are flawed, but offer no proof
-- you "don't have time right now to toy with this," (Richard's tests)
even though you've had time to write quite long posts explaining your
framework

There may be other things that I'm forgetting right now, but that
covers enough, I think.

It must be obvious to _everyone_ that the ball is in your court. Your
claims are hanging by the slimmest of threads.

Bob

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Fri, 25 Jul 2003 15:30:51 +0000 (UTC)
Raw View
Richard Smith wrote:

>
> I don't want a "few more hints" on how to write a test
> suite.  What I want is for you to either accept that your
> smart pointer framework is less efficient that Boost's, or
> to provide hard evidence that I'm wrong, and yours is more
> efficient.  Almost every post in this thread has asked you
> for such evidence and you have failed to provide any.
>

I tested your code as is (almost)- even though I would not write it that
way in real life.

I don't have timer.h

I used boost_1_30_0.

GCC - is 3.3 (pre-release)
Reading specs from /usr/local/lib/gcc-lib/i386-redhat-linux/3.3/specs
Configured with: ../gcc/configure --prefix=/usr/local
--mandir=/usr/local/share/man --infodir=/usr/local/share/info
--enable-shared --enable-threads=posix --disable-checking
--host=i386-redhat-linux --with-system-zlib --enable-__cxa_atexit
Thread model: posix
gcc version 3.3 20030509 (prerelease)

g++  -Wreturn-type -W -Wpointer-arith -pipe -ggdb3 -fcheck-new -fPIC -O3
-finline-functions -DRELEASE_BUILD=1 -c at_test.cpp (some -I's removed)

test time for at_test.cpp comes to 2.350sec (using the time(1) command)

g++  -Wreturn-type -W -Wpointer-arith -pipe -ggdb3 -fcheck-new -fPIC -O3
-finline-functions -DRELEASE_BUILD=1 -c boost_test.cpp (some -I's removed)

test time for boost_test.cpp comes to 3.230sec (using the time(1) command)


(3.230/2.350-1)*100=37.4%

Conclusion - I measure at_lifetime.cpp to be 37% faster than
boost::shared_ptr.

With changes I would make so that it's more like (not close) to a
real-life test the at_test.cpp execution time comes down to 1.66 seconds
and I have yet to finish - that would make it almost 100% faster than
boost and I have not even started yet.


Processor:
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 8
model name      : Pentium III (Coppermine)
stepping        : 3
cpu MHz         : 797.977
cache size      : 256 KB


btw - this is the diff of my at_test.cpp vs your at_test.cc

diff ../../../../at_test.cc at_test.cpp
5c5
< #include "timer.h"
---
 > //#include "timer.h"
40c40
<   timer t;
---
 > //  timer t;
45c45
<       assert( t.get_b() == t.get_a() );
---
 >       AT_Assert( t.get_b() == t.get_a() );
48c48
<   std::cout << t.get_time() << "s" << std::endl;
---
 > //  std::cout << t.get_time() << "s" << std::endl;

  ... similar change to boost_test.cpp


I don't think you'll end up proving what you're trying to prove in this
way.  Firstly, I would not write code like you have done, in real-life
there is way more polymorphism and the performance costs there pay off
in performance in other ways.

The issue is far more complex than a 50 line test.

Let me put it in a nutshell for you:

a) I believe it's the wrong thing to do to adopt shared_ptr as part of
the standard because it is not the right solution for the body of users
that have most to benefit from the language.  Read my previous rant if
you want detail.

b) I believe there is room for performance freaks like myself that are
able to simplyfy the task of writing code at close to 0 cost of performance.

c) I echo Peter Dimov's paragraph "Eliminating extra copies is a general
problem ..."

By no means have I done any performance testing on at_lifetime.h (other
than just now), it's really a concept at the moment that is guided by a
common sense that doing nothing is much faster than doing somthing.
This leads to a practice that tends to force writing code so that most
of the time nothing needs to be done.  In the AT_Life* world, this
infers - pass AT_LifeView where you can and AT_LifeLine where you must.
  Never pass/return AT_LifeTime's because they allways do work - again
this hits a little on c) above.


G





---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Sat, 26 Jul 2003 00:53:39 +0000 (UTC)
Raw View
Bob Bell wrote:
> gi2nospam@mariani.ws (Gianni Mariani) wrote in message news:<bfmu62$gvm@dispatch.concentric.net>...
>
>>Richard Smith wrote:
>
>
> [snip Richard's telling tests...]
>
>
>>Richard, thanks for taking you time and putting together this benchmark.
>>
>>Unfortunately you omit a number of critical points in you discussion.
>>
>>Firstly, you choose to use virtual methods for performing the reference
>>counting on the AT example and secondly you use of auto_ptr is clearly a
>>specific case and not as general as AT_LifeLine.
>>
>>Also, your test is not indicative of how a real-life system would work.
>>  Memory utilization as well as instruction count are huge factors.
>>
>>I don't have time right now to toy with this, but if you need a few more
>>hints as to how you can create a true apples and apples comparison, drop
>>me a line.
>
>
> Let's see if I can summarize:
>
> -- you claim that shared_ptr is too slow, but have offered no proof

See my results of Richard's test.

> -- you claim that an intrusive ref-counting system is all that's
> necessary, but have ignored arguments to the contrary

Did you understand my argument ?  If you can't repeat it in you own
words, then let me know, I'll try to write it in a way you will understand.

> -- your framework has problems with exception safety, but at times you
> argue that they are trivial to fix, and at others argue that they are
> unimportant

Only if used incorrectly and I'm toying with an idea that makes it
completly exception safe.  Again, there is the design balance of making
somthing exception safe as a cost or leave it up to the programmer to
make it exception safe at 0 cost.  If you don't get that, then you don't
understand the argument.  If you need me to repeat myself, I'll be happy to.

> -- Richard took the time to create some benchmarks which show your
> performance claims are false

Bogus test - see my results.

> -- you claim Richard's tests are flawed, but offer no proof

Proof given.

> -- you "don't have time right now to toy with this," (Richard's tests)
> even though you've had time to write quite long posts explaining your
> framework

Toyed with now - go see.

>
> There may be other things that I'm forgetting right now, but that
> covers enough, I think.
>
> It must be obvious to _everyone_ that the ball is in your court. Your
> claims are hanging by the slimmest of threads.

I think most people have tuned out by now :-(

If _everyone_ finds my argument so incomprehensible, I suggest that
_everyone_ takes a step back and tries to see it from the perspective of
my argument.  The argument stands on it's merits, it MAY be wrong, I
really don't mind, but it MAY be right as well.

I must admit, I was surprised that I found contradictory results to
Richard's test.  You must acknoledge that if there is such a significant
discrepancy, that they weight of my argument is even more significant.

Ball's in _everyone_'s court now ?

Cheers
G

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Sun, 27 Jul 2003 02:09:40 +0000 (UTC)
Raw View
gi2nospam@mariani.ws (Gianni Mariani) wrote in message news:<bfsh9c$be2@dispatch.concentric.net>...
> Bob Bell wrote:
> > -- you claim that an intrusive ref-counting system is all that's
> > necessary, but have ignored arguments to the contrary
>
> Did you understand my argument ?  If you can't repeat it in you own
> words, then let me know, I'll try to write it in a way you will understand.

Irrelevant. Your argument in favor of intrusive ref-counting is not
the point. The point is that others have argued for why they need
external ref-counting, and you've ignored those arguments.

However, since you asked, yes I understood the argument; I just don't
agree with it. I personally have no use for intrusive reference
counting. None of the types I use support intrusive reference
counting, and I wouldn't design one that way without a very compelling
reason to do so. Mainly this is because the class involved usually has
some other responsibility -- the concept it represents; adding
reference counting means overloading the behavior of the class for
reasons that have nothing to do with the class' concept. Thus I find
external reference counting much more useful, since it doesn't require
me to perform such overloading.

An example given to you for why external reference counting was of a
smart pointer to a std::map, which doesn't have reference counting
functions built in. Your response was essentially to build a class
which contains the std::map and implements the reference counting. By
doing this, you have merely implemented part of the external reference
counting that is done by types like boost::shared_ptr. The std::map is
still not reference counting; from the point of view of the std::map,
it has been placed into something which does external reference
counting. The only difference between this approach and something like
boost::shared_ptr is that you're doing more work.

> > -- your framework has problems with exception safety, but at times you
> > argue that they are trivial to fix, and at others argue that they are
> > unimportant
>
> Only if used incorrectly and I'm toying with an idea that makes it
> completly exception safe.  Again, there is the design balance of making
> somthing exception safe as a cost or leave it up to the programmer to
> make it exception safe at 0 cost.  If you don't get that, then you don't
> understand the argument.  If you need me to repeat myself, I'll be happy to.

I understand the argument. You prefer a high-performance, error-prone
system to a safer one which always works.

You're advocating a system which can silently become erroneous.
Example: I write unsafe-but-fast code using AT_whatever, but it's OK
because I know that the types I'm using with AT_whatever don't throw.
Six months later, as the system develops, someone (perhaps me, perhaps
not) changes the type so that it can throw. Suddenly the previous code
is erroneous.

> > -- Richard took the time to create some benchmarks which show your
> > performance claims are false
>
> Bogus test - see my results.

Perhaps you should say more about the differences. From what I can
see, the only difference is you commented out the timer code. How does
that account for the difference in your timing vs. Richard's? Or were
there other changes I missed?

> > There may be other things that I'm forgetting right now, but that
> > covers enough, I think.
> >
> > It must be obvious to _everyone_ that the ball is in your court. Your
> > claims are hanging by the slimmest of threads.
>
> I think most people have tuned out by now :-(
>
> If _everyone_ finds my argument so incomprehensible, I suggest that
> _everyone_ takes a step back and tries to see it from the perspective of
> my argument.  The argument stands on it's merits, it MAY be wrong, I
> really don't mind, but it MAY be right as well.

Sorry, I was a bit harsher than intended.

However, your argument doesn't stand in a vacuum. My overall point was
to say that there have been many counter-arguments, which as far as I
can see, you've mostly ignored.

Bob

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: richard@ex-parrot.com (Richard Smith)
Date: Sun, 27 Jul 2003 19:46:48 +0000 (UTC)
Raw View
Bob Bell wrote:

> > > -- Richard took the time to create some benchmarks which show your
> > > performance claims are false
> >
> > Bogus test - see my results.
>
> Perhaps you should say more about the differences. From what I can
> see, the only difference is you commented out the timer code. How does
> that account for the difference in your timing vs. Richard's? Or were
> there other changes I missed?

I suspect that his timings compared a thread-safe Boost
version with assertions compiled in, against a single-
threaded AT_Life* version with assertions compiled out.
This is the only way I can explain such a wildly different
timings.

--
Richard Smith

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Sun, 27 Jul 2003 22:11:58 +0000 (UTC)
Raw View
Richard Smith wrote:
> Bob Bell wrote:
>
>
>>>>-- Richard took the time to create some benchmarks which show your
>>>>performance claims are false
>>>
>>>Bogus test - see my results.
>>
>>Perhaps you should say more about the differences. From what I can
>>see, the only difference is you commented out the timer code. How does
>>that account for the difference in your timing vs. Richard's? Or were
>>there other changes I missed?
>
>
> I suspect that his timings compared a thread-safe Boost
> version with assertions compiled in, against a single-
> threaded AT_Life* version with assertions compiled out.
> This is the only way I can explain such a wildly different
> timings.
>

That would seem to make my argument even more persuasive ?

shared_ptr is wonderful if you have cycles to waste and don't want to
bother having to learn how to write faster code.  If that's the case,
use perl, why bother with C++ ?

I think you're right, it seems like the boost code that is generated
does have pthread locks in it.  It's not obvious how to turn it off.

G

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dave@boost-consulting.com (David Abrahams)
Date: Sun, 27 Jul 2003 23:34:11 +0000 (UTC)
Raw View
gi2nospam@mariani.ws (Gianni Mariani) writes:

> Richard Smith wrote:
>> Bob Bell wrote:
>>
>>>>>-- Richard took the time to create some benchmarks which show your
>>>>>performance claims are false
>>>>
>>>>Bogus test - see my results.
>>>
>>>Perhaps you should say more about the differences. From what I can
>>>see, the only difference is you commented out the timer code. How does
>>>that account for the difference in your timing vs. Richard's? Or were
>>>there other changes I missed?
>> I suspect that his timings compared a thread-safe Boost
>> version with assertions compiled in, against a single-
>> threaded AT_Life* version with assertions compiled out.
>> This is the only way I can explain such a wildly different
>> timings.
>>
>
> That would seem to make my argument even more persuasive ?
>
> shared_ptr is wonderful if you have cycles to waste and don't want to
> bother having to learn how to write faster code.  If that's the case,
> use perl, why bother with C++ ?
      ^^^^
      Python

But as someone who is firmly in the multi-language programming camp,
I have to admit that crossing the language barrier can costly in
programmer time and brain cycles -- it's not always worth it.  98% of
most program code doesn't need to be fast, and if you have to code 2%
in C++ it might be fine to use a smart pointer which optimizes for
safety over speed for the rest of it.

> I think you're right, it seems like the boost code that is generated
> does have pthread locks in it.  It's not obvious how to turn it off.

Just disable threading support in your compiler's command-line, which
should cause BOOST_HAS_THREADS to become undefined.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: terekhov@web.de (Alexander Terekhov)
Date: Mon, 28 Jul 2003 05:31:09 +0000 (UTC)
Raw View
===================================== MODERATOR'S COMMENT:
 Boost-specific followups should be in email, or elsewhere.  Thanks.


===================================== END OF MODERATOR'S COMMENT

David Abrahams wrote:
[...]
> Just disable threading support in your compiler's command-line, which
> should cause BOOST_HAS_THREADS to become undefined.

That's "oll korrect" approach only as long as the standard continues
to refuse the existence of threads. I hope that it will change in the
C++0X (and "pretty old" C0X too, BTW) "round". Some kinda "relevant"
discussion and a few details can be found here:

http://groups.google.com/groups?selm=3ECD0AC9.69B39C93%40web.de
(Subject: shared_ptr/weak_ptr and thread-safety)

http://article.gmane.org/gmane.comp.lib.boost.devel/20359
(Same subject. Pls take a closer look at the entire thread)

regards,
alexander.

P.S. Any progress on the Boost.License front, David? Hint: "forking"
(with a more "restrictive" license). Hint: "patents". Hint: ah, well.

--
"In short, the company stinks, their products stink, and you'd be
 insane to buy one of their operating systems for any environment,
 let alone a corporation with sensitive and important data. SCO
 may be the "true" Unix, but it's also the weakest."

                  -- http://www.thejemreport.com/articles/sco.htm

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "terjes."@chello.no (=?ISO-8859-1?Q?Terje_Sletteb=F8?=)
Date: Sun, 3 Aug 2003 20:17:38 +0000 (UTC)
Raw View
gi2nospam@mariani.ws (Gianni Mariani) wrote in message news:<bfip1q$bdk@dispatch.concentric.net>...
> <Bold statement>
> Traditional text based systems for describing a program are no longer
> suitable.  C++ is at a level of complexity that very few people will be
> able to master adequately in their life-time as a programmer/developer
> to be useful.  Unfortunately, adding more complexity (like smart_ptr,
> bit not neccassarily smart_ptr) only makes the learning curve longer and
> hence makes the language less useful.

Eh, no. You could say the same about std::vector and std::string.
These encapsulate memory management, so that the user don't have to
worry about that. Beginners can use them directly, without learning
how to do memory mangement, especially in the precense of exception.
Writing exception-safe containers is hard, and not something beginners
should need to know.

The same goes for shared_ptr; you may use it out of the box, without
doing manual memory management. Therefore, it, like the other
mentioned components, makes C++ _easier_ to learn and use, not harder.

Naturally, you may want to learn the details of how this works, too,
but it's not necessary to use them.

Perhaps you're thinking of learning C++ the "C first" way. Well, there
are alternatives, such as learning C++ as a new language - and
starting with the standard library - an approach that "Accelerated
C++" uses.

Regards,

Terje

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Mon, 28 Jul 2003 06:10:40 +0000 (UTC)
Raw View
gi2nospam@mariani.ws (Gianni Mariani) wrote in message news:<bg1gnf$gvs@dispatch.concentric.net>...
> Richard Smith wrote:
> > Bob Bell wrote:
> >>>>-- Richard took the time to create some benchmarks which show your
> >>>>performance claims are false
> >>>
> >>>Bogus test - see my results.
> >>
> >>Perhaps you should say more about the differences. From what I can
> >>see, the only difference is you commented out the timer code. How does
> >>that account for the difference in your timing vs. Richard's? Or were
> >>there other changes I missed?
> >
> > I suspect that his timings compared a thread-safe Boost
> > version with assertions compiled in, against a single-
> > threaded AT_Life* version with assertions compiled out.
> > This is the only way I can explain such a wildly different
> > timings.
>
> That would seem to make my argument even more persuasive ?

???

How does an unfair test that favors your AT pointers make your
argument more persuasive? Richard explicitly acknowledged that he used
single-threading for both AT and shared_ptr. If anything, this seems
to throw more weight behind the validity of Richard's tests.

Bob

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Mon, 28 Jul 2003 14:39:03 +0000 (UTC)
Raw View
Bob Bell wrote:

>
> How does an unfair test that favors your AT pointers make your
> argument more persuasive? Richard explicitly acknowledged that he used
> single-threading for both AT and shared_ptr. If anything, this seems
> to throw more weight behind the validity of Richard's tests.

Exactly what are you referring to ?  What you have said is way too
ambiguous ?

Yes - it's very unfair !

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: richard@ex-parrot.com (Richard Smith)
Date: Mon, 28 Jul 2003 19:07:31 +0000 (UTC)
Raw View
Gianni Mariani wrote:

> g++  -Wreturn-type -W -Wpointer-arith -pipe -ggdb3 -fcheck-new -fPIC -O3
> -finline-functions -DRELEASE_BUILD=1 -c at_test.cpp (some -I's removed)
>
> test time for at_test.cpp comes to 2.350sec (using the time(1) command)
>
> g++  -Wreturn-type -W -Wpointer-arith -pipe -ggdb3 -fcheck-new -fPIC -O3
> -finline-functions -DRELEASE_BUILD=1 -c boost_test.cpp (some -I's removed)
>
> test time for boost_test.cpp comes to 3.230sec (using the time(1) command)

You've omitted -DBOOST_DISABLE_THREADS.  If you want a fair
comparison, you need top compare thread-safe code against
thread-safe code, or single-threaded code with
single-threaded code.  Leaving this out, then I agree with
your results, but it is not a fair comparision.  Adding this
define makes the boost code faster than yours.

(Incidentally, I've deliberately avoided testing the boost
implementation in a thread-safe build.  This is because it
is known to be significantly less efficient implementation
that it could be.  See elsewhere in this thread for
details.)

> With changes I would make so that it's more like (not close) to a
> real-life test the at_test.cpp execution time comes down to 1.66 seconds
> and I have yet to finish - that would make it almost 100% faster than
> boost and I have not even started yet.


> diff ../../../../at_test.cc at_test.cpp

[...]

Are these the diffs you used to get a time of 2.35s or of
1.66s?  I assume the former,  as I doubt they are sufficient
to have a factor of two difference.  (Unless RELEASE_BUILD
turns off the assertions, in which case for a fair
comparision you should also define NDEBUG and
BOOST_DISABLE_ASSERTS.)

If I take all the asserts out and apply the above diff, I
*still* get boost being marginally faster than yours.  And
Boost does far, far more than yours does.

> I don't think you'll end up proving what you're trying to prove in this
> way.  Firstly, I would not write code like you have done, in real-life
> there is way more polymorphism and the performance costs there pay off
> in performance in other ways.

Feel free to write another benchmark that better reflects
what you feel to be a real-life use case.


> b) I believe there is room for performance freaks like myself that are
> able to simplyfy the task of writing code at close to 0 cost of performance.

And what do you say to people who want the facilities
provided by boost:shared_ptr, but that your framework
doesn't provide?

It is almost always possible to write a more efficient but
less general purpose class for any given task.  The question
is where to draw the line.  It's my opinion that you are
going too far towards raw efficiency at the cost of general
utility.  (And I don't doubt that you will eventually get
your version that is fractionally faster than boosts, even
though I still dispute that it currently is.)

> c) I echo Peter Dimov's paragraph "Eliminating extra copies is a general
> problem ..."

Yes.  A general problem.  Not specific to boost::shared_ptr
(or any other shared pointer).  There are various possible
mechanisms being talked about, both within and outside the
Standard committee, that address this at a more general
level: things like the rvalue reference proposal and Andrei
Alexandrescu's Mojo library.

> By no means have I done any performance testing on at_lifetime.h (other
> than just now), it's really a concept at the moment that is guided by a
> common sense that doing nothing is much faster than doing somthing.

Well, in the version you posted, virtual function calls for
every AddRef / Release were necessary.  Compared to the
boost implementation which have non-virtual, easily inlined
functions, this is not "doing nothing".

>   Never pass/return AT_LifeTime's because they allways do work - again
> this hits a little on c) above.

As I said above, if you feel the benchmark is unrealistic,
feel free to write a better one and/or modify the existing
one.

--
Richard Smith

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Mon, 28 Jul 2003 19:08:21 +0000 (UTC)
Raw View
Bob Bell wrote:
> gi2nospam@mariani.ws (Gianni Mariani) wrote in message news:<bfsh9c$be2@dispatch.concentric.net>...
>
>>Bob Bell wrote:
>>
>>>-- you claim that an intrusive ref-counting system is all that's
>>>necessary, but have ignored arguments to the contrary
>>
>>Did you understand my argument ?  If you can't repeat it in you own
>>words, then let me know, I'll try to write it in a way you will understand.
>
>
> Irrelevant. Your argument in favor of intrusive ref-counting is not
> the point. The point is that others have argued for why they need
> external ref-counting, and you've ignored those arguments.

I don't believe I ignored them.  I acknowledged them and I gave reason
that a practice I use that serves me well, eliminates that need.

>
> However, since you asked, yes I understood the argument; I just don't
> agree with it. I personally have no use for intrusive reference
> counting. None of the types I use support intrusive reference
> counting, and I wouldn't design one that way without a very compelling
> reason to do so. Mainly this is because the class involved usually has
> some other responsibility -- the concept it represents; adding
> reference counting means overloading the behavior of the class for
> reasons that have nothing to do with the class' concept. Thus I find
> external reference counting much more useful, since it doesn't require
> me to perform such overloading.

Is inheritance a part of the C++ language exactly for this purpose ?

I have found that the best designs I have seen are ones where all
classes are built with small fundamental sub-components to build more
complex components.  This leads to a design principle that the
"threshold for defining a new class is very low".  Hence hiding the
reference counting in a separate class and adding the extra overhead to
manage it contrary to that design principle.

In other words, I think the shared_ptr extrusive reference counting
model may be a poor design choice to the alternative described above
almost every time, to the point where doing it at all probably leads to
a poorer design.

>
> An example given to you for why external reference counting was of a
> smart pointer to a std::map, which doesn't have reference counting
> functions built in. Your response was essentially to build a class
> which contains the std::map and implements the reference counting. By
> doing this, you have merely implemented part of the external reference
> counting that is done by types like boost::shared_ptr. The std::map is
> still not reference counting; from the point of view of the std::map,
> it has been placed into something which does external reference
> counting. The only difference between this approach and something like
> boost::shared_ptr is that you're doing more work.

You omit the most important piece, I find very few classes that are
"new()ed" are as simple as a map.  More often than not, creating a class
which is a combination of multiple different concepts is the norm.
Hence inheriting a reference counting class is a small complexity for
significant impact positive to performance.

>
>
>>>-- your framework has problems with exception safety, but at times you
>>>argue that they are trivial to fix, and at others argue that they are
>>>unimportant
>>
>>Only if used incorrectly and I'm toying with an idea that makes it
>>completly exception safe.  Again, there is the design balance of making
>>somthing exception safe as a cost or leave it up to the programmer to
>>make it exception safe at 0 cost.  If you don't get that, then you don't
>>understand the argument.  If you need me to repeat myself, I'll be happy to.
>
>
> I understand the argument. You prefer a high-performance, error-prone
> system to a safer one which always works.

Exactly what C++ is.  C++ is full of issues like this.  If I didn't care
about performance I'd write it in perl, java or place you favorite other
safe language here.  C++, like C allows you to create code that is very
close to the iron but it's expressiveness allows you to make code that
is undefined in behaviour.  It's the trade-off of performance vs safety.
An exception safe smart pointer as a cost to performance may be the
wrong answer.

If there are solutions that have 0 run-time performance costs and are
100% safe, then I'm all for it.  In my opinion the C++ standard should
keep performance as a primary objective above all others because this is
the only reason you can justify that long earning curve.  No matter what
you add at this point, you can only increase the complexity of the
standard and hence the trade-off should remain intact.

>
> You're advocating a system which can silently become erroneous.

Only if there is a significant cost.

> Example: I write unsafe-but-fast code using AT_whatever, but it's OK
> because I know that the types I'm using with AT_whatever don't throw.
> Six months later, as the system develops, someone (perhaps me, perhaps
> not) changes the type so that it can throw. Suddenly the previous code
> is erroneous.

That's not what I said.  I said you can implement 100% exception safe
code IF you use it correctly even though AT_LifeLine is not guarenteed
to be 100% exception safe.  And I'll repeat myself again, there is
another AT_LifeLine like idea that I have that may prove to be as fast
as an AT_LifeLine that is exception safe.  I need to bench-mark it to
see if the impact is an issue, I suspect it will be very small and some
very good optimizing compilers could figure out how to eliminate the
overhead almost entirely.


>
>
>>>-- Richard took the time to create some benchmarks which show your
>>>performance claims are false
>>
>>Bogus test - see my results.
>
>
> Perhaps you should say more about the differences. From what I can
> see, the only difference is you commented out the timer code. How does
> that account for the difference in your timing vs. Richard's? Or were
> there other changes I missed?

I think I gave all the information to be able to re-create the results I
came up with.  I suspect it's the compiler version.  I think gcc-3.2 had
some issues there, I'm not certain.  I just happen to use gcc-3.3 on my
linux dev box, there is nothing sinister here.  I attempted in good
faith to re-create Richard's results.

>
>
>>>There may be other things that I'm forgetting right now, but that
>>>covers enough, I think.
>>>
>>>It must be obvious to _everyone_ that the ball is in your court. Your
>>>claims are hanging by the slimmest of threads.
>>
>>I think most people have tuned out by now :-(
>>
>>If _everyone_ finds my argument so incomprehensible, I suggest that
>>_everyone_ takes a step back and tries to see it from the perspective of
>>my argument.  The argument stands on it's merits, it MAY be wrong, I
>>really don't mind, but it MAY be right as well.
>
>
> Sorry, I was a bit harsher than intended.
>
> However, your argument doesn't stand in a vacuum.

You might not agree, but other than speculation I have yet to see why
the argument I presented is "wrong".  Once we can agree on the merits of
the agument we can argue how it reflects on all other things.

I'm feeling a little like my buddy Galileo here.

  My overall point was
> to say that there have been many counter-arguments, which as far as I
> can see, you've mostly ignored.

Every argument you have put forward, I have presented an alternative
perspective.  I don't think I ignored them, I think I presented an
alternative point of view.

G

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Mon, 28 Jul 2003 19:08:54 +0000 (UTC)
Raw View
David Abrahams wrote:
> gi2nospam@mariani.ws (Gianni Mariani) writes:
>
>
>>Richard Smith wrote:
>>
>>>Bob Bell wrote:
>>>
>>>
>>>>>>-- Richard took the time to create some benchmarks which show your
>>>>>>performance claims are false
>>>>>
>>>>>Bogus test - see my results.
>>>>
>>>>Perhaps you should say more about the differences. From what I can
>>>>see, the only difference is you commented out the timer code. How does
>>>>that account for the difference in your timing vs. Richard's? Or were
>>>>there other changes I missed?
>>>
>>>I suspect that his timings compared a thread-safe Boost
>>>version with assertions compiled in, against a single-
>>>threaded AT_Life* version with assertions compiled out.
>>>This is the only way I can explain such a wildly different
>>>timings.
>>>
>>
>>That would seem to make my argument even more persuasive ?
>>
>>shared_ptr is wonderful if you have cycles to waste and don't want to
>>bother having to learn how to write faster code.  If that's the case,
>>use perl, why bother with C++ ?
>
>       ^^^^
>       Python
>
> But as someone who is firmly in the multi-language programming camp,
> I have to admit that crossing the language barrier can costly in
> programmer time and brain cycles -- it's not always worth it.  98% of
> most program code doesn't need to be fast, and if you have to code 2%
> in C++ it might be fine to use a smart pointer which optimizes for
> safety over speed for the rest of it.

When you write libraries that will be used in places that are yet
unknown,  you need to take a more conservative approach.

>
>
>>I think you're right, it seems like the boost code that is generated
>>does have pthread locks in it.  It's not obvious how to turn it off.
>
>
> Just disable threading support in your compiler's command-line, which
> should cause BOOST_HAS_THREADS to become undefined.
>

Warning - this disabling on the command line will probably be the cause
of a whole bunch more frustrations.  Some libraries being thread safe
and others not ?  BOOST_HAS_THREADS is VERY bad mojo.

It seems like defining BOOST_DISABLE_THREADS was the only way to remove
threading.

If I remove the virtual AddRef/Release methods for at_test and disable
threading in shared_ptr, then we have:

at_test 1.12 sec

boost_test 1.97 sec

at test is still 75% faster and the test is still bogus.

I suspect a 100% safe version of AT_Life* could get close to these numbers.





---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Mon, 28 Jul 2003 19:17:51 +0000 (UTC)
Raw View
gi2nospam@mariani.ws (Gianni Mariani) wrote in message news:<bg2oe4$gvu@dispatch.concentric.net>...
> Bob Bell wrote:
> > How does an unfair test that favors your AT pointers make your
> > argument more persuasive? Richard explicitly acknowledged that he used
> > single-threading for both AT and shared_ptr. If anything, this seems
> > to throw more weight behind the validity of Richard's tests.
>
> Exactly what are you referring to ?  What you have said is way too
> ambiguous ?
>
> Yes - it's very unfair !

Sorry, let me clarify.

In replying to my question about why your timings and Richards timings
were so different, Richard wrote:

> I suspect that his timings compared a thread-safe Boost
> version with assertions compiled in, against a single-
> threaded AT_Life* version with assertions compiled out.
> This is the only way I can explain such a wildly different
> timings.

To which you replied:

> That would seem to make my argument even more persuasive ?

It appears that you are suggesting that comparing multi-threaded
shared_ptr with assertions on to single-threaded AT_Life* with
assertions off makes your argument more persuasive. Thus, you appear
to suggest that an unfair test -- unfair in AT_Life*'s favor -- makes
your argument even more persuasive.

However, in describing his tests, Richard wrote:

> I've built the boost shared_ptr with BOOST_DISABLE_THREADS
> defined; similarly, I've implemented the AddRef and Release
> methods in your hierarchy in a non-atomic manner.  This
> should be a fair comparision.

Here it seems that Richard is trying to level the playing field
between shared_ptr and AT_Life*, giving them both the same advantages.

So I attempted to summarize this by writing:

> How does an unfair test that favors your AT pointers make your
> argument more persuasive? Richard explicitly acknowledged that he used
> single-threading for both AT and shared_ptr. If anything, this seems
> to throw more weight behind the validity of Richard's tests.

Sorry for the confusion.

Bob

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: richard@ex-parrot.com (Richard Smith)
Date: Tue, 29 Jul 2003 01:34:24 +0000 (UTC)
Raw View
Gianni Mariani wrote:

> Bob Bell wrote:
>
> > How does an unfair test that favors your AT pointers make your
> > argument more persuasive? Richard explicitly acknowledged that he used
> > single-threading for both AT and shared_ptr. If anything, this seems
> > to throw more weight behind the validity of Richard's tests.
>
> Exactly what are you referring to ?  What you have said is way too
> ambiguous ?

I thought he was clear enough.

The timings I made were using a single-threaded
boost::shared_ptr boost and a single-threaded AT_Life*.  I
explained why I did this in one of my earlier posts.  You
then repeated the tests but with a thread-safe
boost::shared_ptr, which is envitably slower.  You did not,
however, use a multi-threaded AT_Life* class, so your
figures are specious.  Repeat the tests using two single-
threaded versions, and you might get some meaningful
figures.

--
Richard Smith

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jdennett@acm.org (James Dennett)
Date: Tue, 29 Jul 2003 05:05:28 +0000 (UTC)
Raw View
Gianni Mariani wrote:

[snip]


> If I remove the virtual AddRef/Release methods for at_test and disable
> threading in shared_ptr, then we have:
>
> at_test 1.12 sec
>
> boost_test 1.97 sec
>
> at test is still 75% faster and the test is still bogus.

75% faster than 1.97 sec would be just under 0.5 sec,
I believe.  Possibly you meant to say that boost_test
is 75% slower?

-- James.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Tue, 29 Jul 2003 14:50:15 +0000 (UTC)
Raw View
Richard Smith wrote:
> Gianni Mariani wrote:
>
>
>>Bob Bell wrote:
>>
>>
>>>How does an unfair test that favors your AT pointers make your
>>>argument more persuasive? Richard explicitly acknowledged that he used
>>>single-threading for both AT and shared_ptr. If anything, this seems
>>>to throw more weight behind the validity of Richard's tests.
>>
>>Exactly what are you referring to ?  What you have said is way too
>>ambiguous ?
>
>
> I thought he was clear enough.
>
> The timings I made were using a single-threaded
> boost::shared_ptr boost and a single-threaded AT_Life*.  I
> explained why I did this in one of my earlier posts.  You
> then repeated the tests but with a thread-safe
> boost::shared_ptr, which is envitably slower.  You did not,
> however, use a multi-threaded AT_Life* class, so your
> figures are specious.  Repeat the tests using two single-
> threaded versions, and you might get some meaningful
> figures.
>

Do me a favour - take the principle I wrote earlier (pass/return
AT_LifeView where you can AT_LifeLine where you must and AT_LifeTime
never) and apply it to the test you wrote.  You'll see that the test
will run much faster than shared_ptr (sub 1 second on my box) and
replace the virtual methods with inlineable static methods as well btw !
  That in my mind would be a fair test.

Given all this discussion about exception safety and AT_LifeLine I think
I'm going to change the semantics so that once an AT_LifeLine has
transferred it's pointer it will be nulled, if it is not null upon
destruction it will decrement the reference count.  After all, the whole
purpose of AT_LifeLine is to transfer the responsibility to another
AT_Life pointer, it has it's drawbacks but is certainly exception safe
and it's peformance could be near optimal.


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: emild@collectivestudios.com (Emil Dotchevski)
Date: Tue, 29 Jul 2003 17:38:26 +0000 (UTC)
Raw View
> shared_ptr is wonderful if you have cycles to waste and don't want to
> bother having to learn how to write faster code.  If that's the case,
> use perl, why bother with C++ ?

>From a certain point of view, I can say the same thing about C++: it
is wonderful if you have cycles to waste. As I already pointed out in
my other posts, C++ optimizers are at unfair disadvantage to some
others because in C/C++ the compiler has to assume that hidden aliases
may exist.

Your posts sound as if your smart pointer solution is the fastest
possible. As far as I can tell, so far you have failed to provide any
evidence to support your claim.

The only time shared_ptr could be slower than your code is when you
construct a new object because at this time shared_ptr is likely to
allocate memory ("likely", because it is up to each implementation to
decide what exactly happens). If memory allocation performance is an
issue, your design needs work, period.

But that's not the point. Even if shared_ptr is slower in some
situations, what matters is whether or not this difference can be
detected in a real world application. In other words, can we afford
shared_ptr? I think the answer is yes. Therefore, you should not focus
on performance at all: shared_ptr is fast enough. Its usability,
abstraction power and implementation hiding properties are what really
sets it apart, IMO.

--Emil

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: richard@ex-parrot.com (Richard Smith)
Date: Tue, 29 Jul 2003 17:40:08 +0000 (UTC)
Raw View
Gianni Mariani wrote:

> If I remove the virtual AddRef/Release methods

What do you actually mean by that?  Removing the virtual
keyword doesn't prevent them from being virtual as they
override virtual functions in the base class.

>for at_test and disable
> threading in shared_ptr, then we have:
>
> at_test 1.12 sec
>
> boost_test 1.97 sec

What happens to AT_Assert when RELEASE_BUILD=1?  Would I be
correct in assuming that it is a macro that exands to
nothing?  (I.e. a bit like assert when NDEBUG is defined.)

If so the inner loop of at_test has half as many function
calls as the inner loop of boost_test.  The test was
supposed to include evaluation of the argument to assert;  I
could equally have written

  extern bool foo;
  foo ^= ( t.get_b() == t.get_a() );

The point was to have something that the compiler wouldn't
optimise away, *AND* that would prevent the compiler
optimising away the previous statements.  In my experience,
an assert like that will very rarely be optimised away.

By removing half of the innermost loop in one example, you
shouldn't be surprised that you're code appears to be
approaching twice the speed.  If you change the test back to
how I wrote it (i.e. put the AT_Assert back to an assert),
then you'll get more meaningful results.

  boost_test:  1.69s
  at_test:     1.81s

(Note I have not removed the virtual function calls to
AddRef and Remove because I don't understand how you intend
to do it.)

--
Richard Smith

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pdimov@mmltd.net (Peter Dimov)
Date: Mon, 21 Jul 2003 18:31:25 +0000 (UTC)
Raw View
gi2nospam@mariani.ws (Gianni Mariani) wrote in message news:<bfa904$be0@dispatch.concentric.net>...
>
> I think it's a mistake to do somthing without really understanding what
> it is smart_ptr really solves.

shared_ptr, you mean.

Let's start from the basics. Why do we need a standard shared
ownership smart pointer?

* It is CopyConstructible and Assignable; the standard library likes
such entities. The only standard smart pointer at the time is,
unfortunately, neither.

* The standard library is value-based; an easy way to get safe
reference semantics is to use a shared ownership smart pointer.

* Nearly every project reinvents this wheel. The wheel is hard to
reinvent correctly, and it would be good to have a standard
alternative so that transfer of ownership is possible between
libraries from different authors.

* A standard pointer benefits the C++ community, you only need to
learn one tool unless you have specific needs that are not satisfied
by it.

* A standard pointer that has been proven correct, useful, and good
enough for a variety of situations simplifies design decisions and the
process of writing new code. You simply use it and save a lot of time
in the common case where it's good enough. Some programmers will
always have specific needs that aren't covered well, so they are no
better (and no worse) than before, but the majority will benefit.

There are two candidates for such a pointer, one uses intrusive
counting, the other has an external count. Both have their pros and
cons. We have decided that the non-intrusive variant is a better fit
for 'the' smart pointer because:

* A non-intrusive pointer can be used out of the box with any type,
including built-in types, 'immutable' third party types, and current
standard library types. An intrusive pointer would raise the
inevitable question "should we redesign standard library classes so
that they have a compatible embedded count?" and requires, on average,
more work from the user, often leading to virtual inheritance
scenarios.

* A separate count makes it possible for the pointer to operate
correctly when the type of the pointee is unknown. Incomplete types
and void can be supported, supporting various implementation hiding
techniques that improve the design by removing unnecessary
dependencies. Intrusive counting typically requires that at least some
of the type be visible. boost::intrusive_ptr is, to the best of my
knowledge, the only intrusive pointer that can support incomplete
types with help from the user, but it can't do void.

* A separate count leads to a more natural handling of cv-qualified
pointee types.

* A separate count leads to a more natural support of the weak pointer
concept. A weak pointer (a reference that doesn't own) is often needed
to break cycles and is required for some caching idioms. It can also
be used as an observer that requires no support from the observed type
in order to detect the end of its lifetime (the typical solution is to
make the pointee keep a list of the observers and notify them on
destruction). This, again, leads to a design with less dependencies.

* shared_ptr can easily wrap legacy interfaces of the form:

class X;
X* create();
void destroy(X*);

* shared_ptr can adapt to various counting/ownership strategies. If
you need to share ownership of an object with a library via:

void f(shared_ptr<X> const & px);

you can do so even if you use another smart pointer in your code to
manage X'es.

* shared_ptr is much more complex and hard to implement than
intrusive_ptr. If there are two competitive solutions with similar
merits, one much harder to implement than the other, and we need to
choose one, it is better to put the hard to implement solution in the
standard library, since this leads to less work for users, on average.

> As it stands at this time, I can see
> areas that it would reduce efficiency of code that I have written and I
> already use reference counting smart pointers.  To the projects I have
> been involved with it's a mistake to use shared_ptr.

I can only suggest that you revisit your assessment later when you are
more familiar with shared_ptr. There do exist projects where the
performance of the smart pointer counts, but you really should measure
the actual impact and see for yourself. Improving the performance of
code that takes 1% of the total execution time is rarely productive.

Eliminating extra copies is a general problem in C++, and many have
reinvented essentially the same (limited) technique for bypassing the
copy constructor. shared_ptr can benefit, too. We (a different we this
time) believe that a language level solution is needed, and have
submitted a proposal to that effect. But it is also true that
reference counted smart pointers benefit the least from move
semantics. Compared with types that do heap allocations and O(N) copy
loops such as std::vector or std::list, counted pointers are, in most
cases, lost in the noise.

> > 2) Show that your own solution problems with exception safety can be
> > solved without significantly reducing its performance;
>
> that's if I care about solving those things.

Exception safety is important for standard library components.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: emild@collectivestudios.com (Emil Dotchevski)
Date: Tue, 22 Jul 2003 00:23:52 +0000 (UTC)
Raw View
> I've not taken the time to look in detail at your design, and
> I do not level any criticism against it.

Just so I make myself clear, I do *not* criticise any particular
implementation of an alternative to the now standard shared_ptr. I do
criticise the criticism of shared_ptr, particularly because:

- It has been in real wold use for years;

- It has won the acceptance of the standardization committee;

- Often people who criticise it have never used it;

- My own experience with it has lead to improved design and a
reduction of dependencies in my code (and I am not aware of another
smart pointer that would have had the same effect.)

One may say that my position is too convenient for me, that is, I
don't provide any proof for the shared_ptr superiority while I have
been asking for proof of other design's qualities, but this position
is only natural once a given component has been standardized. My
attitude towards someone who claims to have invented a better std::map
would be exactly the same: proove it is better, in a measurable way,
and in a broad application domain, and we may have a discussion.

Plus, the question of superiority is irrelevant. Of course there is a
solution that will perform better in a given specific environment,
that's not the point. It is even possible that shared_ptr's design
could be improved without limiting its application domain. This still
does not mean that its standardization was a mistake, which is why my
reaction towards anyone claiming that has been this harsh.

And yes, exception safety is a must.

--Emil

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: richard@ex-parrot.com (Richard Smith)
Date: Tue, 22 Jul 2003 18:46:44 +0000 (UTC)
Raw View
Gianni Mariani wrote:

> I didn't say it won't work well with exceptions.  I said that I may not
> care to have an overhead for having the smart pointers to solve them for
> me.  VERY different.

To clarify then, can you state whether you intend your smart
pointer classes to be exception safe?  If you would like a
particular example, can you tell me what the following code
will print?

  class helper : public AT_LifeControl {
   public:
    helper() : rc(1)
      { std::cout << "Constructed" << std::endl; }
    virtual ~helper()
      { std::cout << "Destructed" << std::endl; }

   private:
    virtual int AddRef()
      { return ++rc; }
    virtual int Release()
      { if (--rc) return rc; delete this; return 0; }

    int rc;
  };

  int main()
  {
    try {
      AT_LifeLine<helper*> h( new helper );
      throw std::runtime_error("Exception");
    } catch ( const std::exception& e ) {
      std::cout << e.what() << std::endl;
    }
  }

If the code prints anything other than

  Constructed
  Destructed
  Exception

the code is not exception safe.  The code you posted earlier
does not do this and hence is not exception safe.  There are
no two ways about this:  your code (as posted) leaks
resources and is not exception safe.

As other people have pointed out, there is virtually zero
chance that any sort of smart pointer class that is not
exception safe will be accepted in the Standard.

--
Richard Smith

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Tue, 22 Jul 2003 22:57:47 +0000 (UTC)
Raw View
Peter Dimov wrote:
> gi2nospam@mariani.ws (Gianni Mariani) wrote in message news:<bfa904$be0@dispatch.concentric.net>...
>
>>I think it's a mistake to do somthing without really understanding what
>>it is smart_ptr really solves.
>
>
> shared_ptr, you mean.

:-) - yes ... mistyped.
>
> Let's start from the basics. Why do we need a standard shared
> ownership smart pointer?
>
> * It is CopyConstructible and Assignable; the standard library likes
> such entities. The only standard smart pointer at the time is,
> unfortunately, neither.
>
> * The standard library is value-based; an easy way to get safe
> reference semantics is to use a shared ownership smart pointer.
>
> * Nearly every project reinvents this wheel. The wheel is hard to
> reinvent correctly, and it would be good to have a standard
> alternative so that transfer of ownership is possible between
> libraries from different authors.
>
> * A standard pointer benefits the C++ community, you only need to
> learn one tool unless you have specific needs that are not satisfied
> by it.
>
> * A standard pointer that has been proven correct, useful, and good
> enough for a variety of situations simplifies design decisions and the
> process of writing new code. You simply use it and save a lot of time
> in the common case where it's good enough. Some programmers will
> always have specific needs that aren't covered well, so they are no
> better (and no worse) than before, but the majority will benefit.
>
> There are two candidates for such a pointer, one uses intrusive
> counting, the other has an external count. Both have their pros and
> cons. We have decided that the non-intrusive variant is a better fit
> for 'the' smart pointer because:
>
> * A non-intrusive pointer can be used out of the box with any type,
> including built-in types, 'immutable' third party types, and current
> standard library types. An intrusive pointer would raise the
> inevitable question "should we redesign standard library classes so
> that they have a compatible embedded count?" and requires, on average,
> more work from the user, often leading to virtual inheritance
> scenarios.
>
> * A separate count makes it possible for the pointer to operate
> correctly when the type of the pointee is unknown. Incomplete types
> and void can be supported, supporting various implementation hiding
> techniques that improve the design by removing unnecessary
> dependencies. Intrusive counting typically requires that at least some
> of the type be visible. boost::intrusive_ptr is, to the best of my
> knowledge, the only intrusive pointer that can support incomplete
> types with help from the user, but it can't do void.
>
> * A separate count leads to a more natural handling of cv-qualified
> pointee types.
>
> * A separate count leads to a more natural support of the weak pointer
> concept. A weak pointer (a reference that doesn't own) is often needed
> to break cycles and is required for some caching idioms. It can also
> be used as an observer that requires no support from the observed type
> in order to detect the end of its lifetime (the typical solution is to
> make the pointee keep a list of the observers and notify them on
> destruction). This, again, leads to a design with less dependencies.
>
> * shared_ptr can easily wrap legacy interfaces of the form:
>
> class X;
> X* create();
> void destroy(X*);
>
> * shared_ptr can adapt to various counting/ownership strategies. If
> you need to share ownership of an object with a library via:
>
> void f(shared_ptr<X> const & px);
>
> you can do so even if you use another smart pointer in your code to
> manage X'es.
>
> * shared_ptr is much more complex and hard to implement than
> intrusive_ptr. If there are two competitive solutions with similar
> merits, one much harder to implement than the other, and we need to
> choose one, it is better to put the hard to implement solution in the
> standard library, since this leads to less work for users, on average.
>
>
>>As it stands at this time, I can see
>>areas that it would reduce efficiency of code that I have written and I
>>already use reference counting smart pointers.  To the projects I have
>>been involved with it's a mistake to use shared_ptr.
>
>
> I can only suggest that you revisit your assessment later when you are
> more familiar with shared_ptr. There do exist projects where the
> performance of the smart pointer counts, but you really should measure
> the actual impact and see for yourself. Improving the performance of
> code that takes 1% of the total execution time is rarely productive.
>
> Eliminating extra copies is a general problem in C++, and many have
> reinvented essentially the same (limited) technique for bypassing the
> copy constructor. shared_ptr can benefit, too. We (a different we this
> time) believe that a language level solution is needed, and have
> submitted a proposal to that effect. But it is also true that
> reference counted smart pointers benefit the least from move
> semantics. Compared with types that do heap allocations and O(N) copy
> loops such as std::vector or std::list, counted pointers are, in most
> cases, lost in the noise.
>
>
>>>2) Show that your own solution problems with exception safety can be
>>>solved without significantly reducing its performance;
>>
>>that's if I care about solving those things.
>
>
> Exception safety is important for standard library components.
>

Agreed, but there are 100% solutions that have an overhead and 95%
solutions that have no overhead.

Most of (if not all, depending on how you measure) the statements above
also apply to the AT_Life* pointers.

Thanks Peter, this is very close to what I am looking for.  You touched
on some design trade-offs which I'm very interested in understanding
further and I could not agree more on the "Eliminating extra copies"
paragraph.

However, one of the biggest complaints I face when advocating C++ is
that applications written in C++ have poor performance.  Quite frankly,
if a develper does not care about performance, they will likely pick
another language, (Java, C#, perl, python, ... VBScript..) because
theoretically these languages provide (agruable, I know) runtime checks
that prevent the user from doing nasty things that a C or C++ compiler
will let you do without asking permission.

I figure that this would indicate the target application for C++ is
where performance is critical.  (because even I, a hard core C++ guy
would build a bunch o stuff with perl before embarking on C++ unless
performance was THE issue.)

C++ libraries that do not provide the fastest and most maintainable
(this is where we get subjective, your weights for "fastest" and
"maintanable" are very likley different to everyone else) solution is
the true question.  I tend to err on virtually no compromise on
"fastest" except for abstraction (pure abstract interfaces).  In this
way I can argue that C++ is truly faster than C and is appropriate for
all cases where you would use C.  An interesting example is C and
reference counted "objects".  Very few instances of complex structures
in C use reference counting becuase it can be error prone, however in
C++ with smart pointers, many of the errors can be eliminated with
virtually no overhead and hence can lead to easier to write and faster
leaner code - good.  If boost::shared_ptr can't live up to this, I fail
to see it's utility.  shared_ptr concerns me the most because in my
experience, when you have a good reference counted smart pointer
implementation it becomes prolific very fast.  In applications I have
written over the last 2 years, nearly every call has a reference counted
smart pointer in it and so my sensitivity to this issue.

The alternative view is, you don't need all libraries to run so close to
the iron.  There are more levels of performance that just fast and
slower and not all libraries need to be absolutly optimal.  This is
likely the position I will end up taking.  The AT library that I am
working on will have it's own fast smart pointer implementation (and yes
it will be 100% exception safe ...) and it's goal is to provide an
alternative to the STL where the STL is slow.  It will support mapped
files and other fast mechanisms and it will be limited in the platforms
it supports.

However, before I fall into that position, I'm ver curious as to who the
target audience is.  This is where I need to ask the hard questions.
For those that have used shared_ptr.  Is your application performance
critical ?  Are there appropriate alternatives to C++ for your
application ?  Why did you choose C++ in the first place ?

<Bold statement>
Traditional text based systems for describing a program are no longer
suitable.  C++ is at a level of complexity that very few people will be
able to master adequately in their life-time as a programmer/developer
to be useful.  Unfortunately, adding more complexity (like smart_ptr,
bit not neccassarily smart_ptr) only makes the learning curve longer and
hence makes the language less useful.

Many things are done in C++ to accomodate C and they were done in C to
map closely to the iron so that a compiler could be efficiently built.

Conclusion: C++ is ill suited to express programmer intention and for
the compiler to create an optimal program from the intent. For example;
object lifetime management should be somthing that the "language" should
be able to determine for you. (conclude here that even concering a
programmer of life-time management is an issue of undue complexity - and
granted RAII is an interesting paradigm but this is mixing too many
concepts together making it more complex for a programmer to appreciate
the complexity).

Templates and classes come are tools that allow a programmer to create a
description of intent, however it has proven too complex for too many.

A different approach is needed.  (And I have a slew of ideas here that
will bore many to tears so I'll hold off ... for now).
</Bold statement>

If you see the world from the position I have stated (regardless of if
you think I am right or wrong), a conclusion you can come to is that C++
would best meet the needs of the future custiomers if it was to provide
the leanest possible constructs. (since it is already ill suited for
anyone but the top few percentile of the programming profession).

Here I have stated my position in somewhat absolute terms however the
reality is always a compromise and so don't take what I've said here too
literally.

The core of my concern is that where I see C++ being adopted and where
the standardization heading are 2 very different places.  This can only
lead to more frustrations in adopting more complexity to the standard
and hence being VERY picky about what is standardized at this point is
better than choosing somthing a majority will end up regretting.




---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: richard@ex-parrot.com (Richard Smith)
Date: Tue, 22 Jul 2003 22:57:49 +0000 (UTC)
Raw View
Gianni Mariani wrote:

> I think it's a mistake to do somthing without really understanding what
> it is smart_ptr really solves.  As it stands at this time, I can see
> areas that it would reduce efficiency of code that I have written and I
> already use reference counting smart pointers.  To the projects I have
> been involved with it's a mistake to use shared_ptr.

I'm getting a bit fed up with these unsubstantiated claims
that AT_Life* is faster than boost::shared_ptr, and so I've
written some tests to see how they really compares.  First,
some comments on how I've tested them.

I've compiled everything using g++ 3.2.1, with "-O2"
optimisation (this is a fairly standard optimisation level
for release builds).  I've used version 1.30 of boost and
the default STL provided with g++.  The AT_Life* code is as
provided in your post at the beginning of this thread.

I've built the boost shared_ptr with BOOST_DISABLE_THREADS
defined; similarly, I've implemented the AddRef and Release
methods in your hierarchy in a non-atomic manner.  This
should be a fair comparision.

I used the following correspondence between AT_Life*
pointers and standard ones:

   AT_LifeLine<T*>       std::auto_ptr<T>
   AT_LifeTime<T*>       boost::shared_ptr<T>
   AT_LifeView<T*>       T const&

The test programs can be found at

  http://www.ex-parrot.com/~richard/scratch/at_test.cc
  http://www.ex-parrot.com/~richard/scratch/boost_test.cc

The timings I got demonstrated that the standard framework
is significantly faster than yours.  (The standard framework
benchmarked at 1.86s, yours at 2.68s.)

I suggest that you do one of the following:

  1) provide alternative evidence that demostrates your
     smart pointer framework to be faster when used as
     intended; or

  2) accept that the standard smart pointer framework is
     faster than yours.

--
Richard Smith

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: emild@collectivestudios.com (Emil Dotchevski)
Date: Sat, 19 Jul 2003 01:36:31 +0000 (UTC)
Raw View
> How does your statement relate to design goals ?
> Again, what has this to do with design trade-offs ?
> That's nice.  Why exactly is this relevant ?
> Again, that's nice but why is this relevant to my previous post ?

I demonstrated that:

A) boost::shared_ptr has features that no other smart pointers have,
and

B) Many of these features improve performance in real life situations.

I believe this is in contrast with your statement that the design goal
of boost::shared_ptr was ease of use and that because of this its
performance and feature set suffers.

> I truly don't know what the design priorities lie for boost.  I honestly
> believe that boost:smart_ptr may actually be the wrong answer for some
> designs but unless someone can describe exactly what smart_ptr's design
> goals are the discussion will be rather frustrating.

The design goals are like a wish list. Design goals are important in
the design stage, boost::shared_ptr is way past this period. You have
it, you can use it, test it, abuse it, anything you want. It either
does what you want, or it doesn't.

I am not attacking the qualities of boost::shared_ptr, you are; your
posts seem to indicate that its standardization was a mistake and that
you have a better solution. For your argument to be taken seriously,
you *must*:

1) Provide hard performance data that clearly shows that
boost::shared_ptr is slower than your own solution in real world
applications;

2) Show that your own solution problems with exception safety can be
solved without significantly reducing its performance;

3) Explain why the so-called loop holes in your design are
insignificant in real world applications;

4) Demonstrate that your solution is universal and its claimed
advantages are not limited to its use with COM objects.

Other people have also expressed an opinion that boost::shared_ptr is
sub-standard and should not have been accepted in C++. I am yet to see
anyone provide a superior alternative (backed up with hard data).

Even if a superior design does exist, AFAIK boost::shared_ptr was the
only smart pointer design presented to the committee for formal
standardization. After all, the committee can only work with formal
proposals. It would be impractical to turn down a good solution just
because in the bright future someone may come up with a better one.

--Emil

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Sat, 19 Jul 2003 06:21:24 +0000 (UTC)
Raw View
Emil Dotchevski wrote:
>>How does your statement relate to design goals ?
>>Again, what has this to do with design trade-offs ?
>>That's nice.  Why exactly is this relevant ?
>>Again, that's nice but why is this relevant to my previous post ?
>
>
> I demonstrated that:
>
> A) boost::shared_ptr has features that no other smart pointers have,
> and
>
> B) Many of these features improve performance in real life situations.
>
> I believe this is in contrast with your statement that the design goal
> of boost::shared_ptr was ease of use and that because of this its
> performance and feature set suffers.
>
>
>>I truly don't know what the design priorities lie for boost.  I honestly
>>believe that boost:smart_ptr may actually be the wrong answer for some
>>designs but unless someone can describe exactly what smart_ptr's design
>>goals are the discussion will be rather frustrating.
>
>
> The design goals are like a wish list. Design goals are important in
> the design stage, boost::shared_ptr is way past this period. You have
> it, you can use it, test it, abuse it, anything you want. It either
> does what you want, or it doesn't.
>
> I am not attacking the qualities of boost::shared_ptr, you are; your
> posts seem to indicate that its standardization was a mistake and that
> you have a better solution. For your argument to be taken seriously,
> you *must*:

Please indicate where you think I was attacking.

I think it's a mistake to do somthing without really understanding what
it is smart_ptr really solves.  As it stands at this time, I can see
areas that it would reduce efficiency of code that I have written and I
already use reference counting smart pointers.  To the projects I have
been involved with it's a mistake to use shared_ptr.

As I have already stated in this discussion, I am interested in have a
clear and open discussion.  I need to know what it

>
> 1) Provide hard performance data that clearly shows that
> boost::shared_ptr is slower than your own solution in real world
> applications;

doing nothing is certainly faster.

>
> 2) Show that your own solution problems with exception safety can be
> solved without significantly reducing its performance;

that's if I care about solving those things.

>
> 3) Explain why the so-called loop holes in your design are
> insignificant in real world applications;

"loop holes" ?  There are no loop hole problems.

>
> 4) Demonstrate that your solution is universal and its claimed
> advantages are not limited to its use with COM objects.

I never claimed it to be universal.

>
> Other people have also expressed an opinion that boost::shared_ptr is
> sub-standard and should not have been accepted in C++. I am yet to see
> anyone provide a superior alternative (backed up with hard data).

That's just it, I don't think there IS anything that merits being put
into the standard unless the goals are clearly defined.  So far, I don't
see how anyone can say that shared_ptr merits inclusion if it's not
clear what the scope of what it it trying to solve is well understood
and how that may or may not meet the needs of the users who will most
likely need it.

>
> Even if a superior design does exist, AFAIK boost::shared_ptr was the
> only smart pointer design presented to the committee for formal
> standardization. After all, the committee can only work with formal
> proposals. It would be impractical to turn down a good solution just
> because in the bright future someone may come up with a better one.

Just because it's the only kid on the block does not mean it should win
the prize.

Having said all that, I'm by no means unwilling to change my mind and if
anything one of the possible outcomes of this discussion may be a shift
in my anchor points.





---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jdennett@acm.org (James Dennett)
Date: Sun, 20 Jul 2003 00:25:30 +0000 (UTC)
Raw View
Gianni Mariani wrote:
> Emil Dotchevski wrote:
>
>> 2) Show that your own solution problems with exception safety can be
>> solved without significantly reducing its performance;
>
>
> that's if I care about solving those things.
>

You may not, for your own projects, but a component that is
unable to work "well" in the presence of exceptions is, in
my opinion, very unlikely to gain support from the committee
for inclusion in Standard C++.

-- James.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Sun, 20 Jul 2003 21:23:42 +0000 (UTC)
Raw View
James Dennett wrote:
> Gianni Mariani wrote:
>
>> Emil Dotchevski wrote:
>>
>>> 2) Show that your own solution problems with exception safety can be
>>> solved without significantly reducing its performance;
>>
>>
>>
>> that's if I care about solving those things.
>>
>
> You may not, for your own projects, but a component that is
> unable to work "well" in the presence of exceptions is, in
> my opinion, very unlikely to gain support from the committee
> for inclusion in Standard C++.
>

I didn't say it won't work well with exceptions.  I said that I may not
care to have an overhead for having the smart pointers to solve them for
me.  VERY different.

G

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jdennett@acm.org (James Dennett)
Date: Sun, 20 Jul 2003 22:03:37 +0000 (UTC)
Raw View
Gianni Mariani wrote:
> James Dennett wrote:
>
>> Gianni Mariani wrote:
>>
>>> Emil Dotchevski wrote:
>>>
>>>> 2) Show that your own solution problems with exception safety can be
>>>> solved without significantly reducing its performance;
>>>
>>>
>>>
>>>
>>> that's if I care about solving those things.
>>>
>>
>> You may not, for your own projects, but a component that is
>> unable to work "well" in the presence of exceptions is, in
>> my opinion, very unlikely to gain support from the committee
>> for inclusion in Standard C++.
>>
>
> I didn't say it won't work well with exceptions.  I said that I may not
> care to have an overhead for having the smart pointers to solve them for
> me.  VERY different.
>
> G

Emil wrote that your solution has "problems with exception
safety".  One of the goals of the boost shared pointer, it
seems to me, was to help users to write exception safe code
in the presence of dynamic allocation.  While the terminology
may be subjective, hence my use of quotation marks, I was just
pointing out that in one sense a component that has "problems
with exception safety" does NOT work well in the presence of
exceptions.

I've not taken the time to look in detail at your design, and
I do not level any criticism against it.  I was merely stating
that if the situation is as Emil writes, and that your solution
has problems with exception safety, that is likely to make it
unacceptable to the committee.

If you'd care to explain here why these concerns are unfounded
I'm sure many committee members will read what you have to say.

Regards,

James.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: SeeWebsiteForEmail@moderncppdesign.com ("Andrei Alexandrescu")
Date: Sun, 13 Jul 2003 00:13:14 +0000 (UTC)
Raw View
"Gianni Mariani" <gi2nospam@mariani.ws> wrote in message
news:bene2f$cq2@dispatch.concentric.net...
> The right answer here on this depends on a compromise of the following
> requirements:
>
> a) "zero overhead" principle
> b) exception safety
> c) correctness - or: elimination of usage errors - or : make is possible
> for the compiler to pick up usage errors.
> (and probably more)
>
> Depending on which requirement I give priority I get different answers
> to "what is right".
>
> I tend to place the "zero overhead" principle above all others which is
> NOT the right answer for everyone or all situations obviously but
> sometimes it is.  Hence when it is the overriding principle, using a
> solution that does not provide the most efficient solution can be a
> significant problem.  Also, smart pointers, or rather object lifetime
> management, is such a fundamental issue for C++ that it will become
> pervasive throughout any code base and hence will become a topic of many
> frustrations to come.  To cap this paradox off, very few programmers
> will grasp the true complexity of smart pointers (as I have already
> witnessed).  A typical formula for intractible issues.
>
> So, my intention here is to open the discussion on altenatives and
> discuss their merits.

That's a laudable initiative, and its rationale is very much to my liking.
However, by skimming over the discussion, it looks like you want to replace
TR1 smart pointer's set of tradeoffs with another set of tradeoffs. That
kind of shuffling tradeoffs around doesn't buy a lot, especially given that
there seem to be evidence that the TR1 smart pointer's set of tradeoffs tend
to please many.

This being said, have you taken a look at Loki::SmartPtr. It sports a design
in which the user is in control of the tradeoff, so you can choose
performance or safety within the same framework and without having to define
new smart pointer types from scratch.


Andrei


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Sun, 13 Jul 2003 05:37:05 +0000 (UTC)
Raw View
Andrei Alexandrescu wrote:
> "Gianni Mariani" <gi2nospam@mariani.ws> wrote in message
> news:bene2f$cq2@dispatch.concentric.net...
>
>>The right answer here on this depends on a compromise of the following
>>requirements:
>>
>>a) "zero overhead" principle
>>b) exception safety
>>c) correctness - or: elimination of usage errors - or : make is possible
>>for the compiler to pick up usage errors.
>>(and probably more)
>>
>>Depending on which requirement I give priority I get different answers
>>to "what is right".
>>
>>I tend to place the "zero overhead" principle above all others which is
>>NOT the right answer for everyone or all situations obviously but
>>sometimes it is.  Hence when it is the overriding principle, using a
>>solution that does not provide the most efficient solution can be a
>>significant problem.  Also, smart pointers, or rather object lifetime
>>management, is such a fundamental issue for C++ that it will become
>>pervasive throughout any code base and hence will become a topic of many
>>frustrations to come.  To cap this paradox off, very few programmers
>>will grasp the true complexity of smart pointers (as I have already
>>witnessed).  A typical formula for intractible issues.
>>
>>So, my intention here is to open the discussion on altenatives and
>>discuss their merits.
>
>
> That's a laudable initiative, and its rationale is very much to my liking.
> However, by skimming over the discussion, it looks like you want to replace
> TR1 smart pointer's set of tradeoffs with another set of tradeoffs.

And the reason you make assertions on my intentions is ?

  That
> kind of shuffling tradeoffs around doesn't buy a lot, especially given that
> there seem to be evidence that the TR1 smart pointer's set of tradeoffs tend
> to please many.

Are you trying to say this is a democratic decision ?  I'm lost as to
what your point might be.

>
> This being said, have you taken a look at Loki::SmartPtr. It sports a design
> in which the user is in control of the tradeoff, so you can choose
> performance or safety within the same framework and without having to define
> new smart pointer types from scratch.

Admitedly, I had forgotten about loki.  Sure enough it's in my downloads
directory from about 6 months ago.  I'll need to look at it further.

I still can't see the LifeLine/LifeView concepts there but I do see the
handlers for different kinds of reference counting control interfaces.

Thanks for the pointer.


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Mon, 14 Jul 2003 19:00:54 +0000 (UTC)
Raw View
Gianni Mariani wrote:
> Richard Smith wrote:
>
>> Gianni Mariani wrote:
>>
>>> Richard Smith wrote:
>>>
>>>> Gianni Mariani wrote:
>>
>>
>>
>> [...]
>>
>>
>>>> 1.  If you want it to be considered instead of
>>>>     boost::shared_ptr for C++0x, then write it in a way
>>>>     that looks and feels like the rest of the STL.  In
>>>>     particular, where possible make expressions that work
>>>>     on std::auto_ptr have the same meaning on your
>>>>     pointers.
>>>
>>>
>>> Fair comment, if there is merit, this is mostly cosmetic and can be
>>> easily done.  The original intent was not for a substitute for
>>> boost::shared_ptr.
>>
>>
>>
>> Errr...  At the very beginning of this thread you said "I
>> would like to see 3 more features in the libraries regarding
>> smart pointers, in particular reference counting smart
>> pointers".  Now either you want your classes to replace
>> the existing proposal based on boost::shared_ptr, or you
>> want to augment it.  Given that your AT_LifeTime class
>> serves almost exactly the same purpose as boost::shared_ptr
>> (except yours uses an intrusive reference count whereas
>> boost uses an external reference count), I can only assume
>> you want to replace it.
>>
>
> You've made a significant argument regarding the exception safety of
> AT_LifeLine and hence I need to reconsider my proposal.

Having given some thought to this, I have 2 counter proposals and a few
conclusions.

Some points to consider:

a) when an application uses smart pointers it will be pervasive and
hence the performance of the smart pointer implementation is likely
siginificant.

b) having an "optimal" or "near optimal" implementation available for a
smart reference counted pointer is likely to be used when the
performance/complexity tradeoff warrants the compromize.

Having said that, I consider that there is a possible alternative
implementation of AT_LifeLine that presents a very small overhead and in
some cases may be optimized to zero overhead (when fully inlined) with
exception safety included.  I would propose this as possibly a 4th
member of the AT_Life* members leaving AT_LifeLine as a less safe but
100% zero overhead solution.

Now, these could be implemented on top of boost::shared_ptr but it seems
like it's already trying to do too much.  The design and intent of
shared_ptr seems like it's trying to be as safe as possible at the cost
of performance and placing such a contradictory set of requirements
(absolute 100% zero overhead and absolute 100% no issues) is likely to
cause far more issues that it's worth trying to solve.  However,
interoperability between smart pointer implementations should fairly
simple so I see even less of an issue than I first did when I originally
posted.

Unfortunately the root cause for this IS the expressiveness of the
language. (no intention to start a flame war - honest !)  I agree that
C++ already suffers a significant overload of complexity and this
situation is likely not going to change.

So I've given myself yet more homework. If anyone is interested in the
alternative AT_Life* smart pointers let me know and maybe I'll work on
it sooner.

This question should be raised however: The TR1 suggested smart pointers
do not pass the "zero overhead principle" requirement so why should it
be adopted as part of the C++ standard ?


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: SeeWebsiteForEmail@moderncppdesign.com ("Andrei Alexandrescu")
Date: Mon, 14 Jul 2003 21:58:16 +0000 (UTC)
Raw View
"Gianni Mariani" <gi2nospam@mariani.ws> wrote in message
news:beqom2$cpu@dispatch.concentric.net...
> Andrei Alexandrescu wrote:
>   That
> > kind of shuffling tradeoffs around doesn't buy a lot, especially given
that
> > there seem to be evidence that the TR1 smart pointer's set of tradeoffs
tend
> > to please many.
>
> Are you trying to say this is a democratic decision ?  I'm lost as to
> what your point might be.

My point will become clearer once you look at Loki::SmartPtr's design. It
fosters a new look at how you design things and decide on tradeoffse. If
shared_ptr and your design are points on a piece of paper, SmartPtr would be
the space around the paper. Ah, bad comparison.


Andrei


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Tue, 15 Jul 2003 15:35:18 +0000 (UTC)
Raw View
Andrei Alexandrescu wrote:
> "Gianni Mariani" <gi2nospam@mariani.ws> wrote in message
> news:beqom2$cpu@dispatch.concentric.net...
>
>>Andrei Alexandrescu wrote:
>>  That
>>
>>>kind of shuffling tradeoffs around doesn't buy a lot, especially given
>
> that
>
>>>there seem to be evidence that the TR1 smart pointer's set of tradeoffs
>
> tend
>
>>>to please many.
>>
>>Are you trying to say this is a democratic decision ?  I'm lost as to
>>what your point might be.
>
>
> My point will become clearer once you look at Loki::SmartPtr's design. It
> fosters a new look at how you design things and decide on tradeoffse. If
> shared_ptr and your design are points on a piece of paper, SmartPtr would be
> the space around the paper. Ah, bad comparison.

Well, my first impression for loki is that it was trying to do too much.
  I really need to go back and look at it more but 5 template parameters ?

     template
     <
         typename T,
         class OwnershipPolicy = RefCountedWrapper,
         class ConversionPolicy = DisallowConversion,
         class CheckingPolicy = AssertCheckWrapper,
         class StoragePolicy = DefaultSPStorageWrapper
     >
     class SmartPtr;

OK, maybe 5 is right, I'm advocating 3 in my own design.

I did say "first impression" so I need really need to look at it more
clearly. ... after I get a few other things done first.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: bdawes@acm.org (Beman Dawes)
Date: Tue, 15 Jul 2003 23:49:51 +0000 (UTC)
Raw View
gi2nospam@mariani.ws (Gianni Mariani) wrote in message news:<beuu4n$cq9@dispatch.concentric.net>...

> This question should be raised however: The TR1 suggested smart pointers
> do not pass the "zero overhead principle" requirement so why should it
> be adopted as part of the C++ standard ?

The C++ committee isn't an authoritarian organization with a rigid set
of rules or formulas determining what gets accepted and what doesn't.
Rather it is a set of independent individuals who make up their own
minds about proposals. Some are primarily users, while others are
implementers, teachers, researchers, or writers. A wide range of
viewpoints.

So there is no mechanistic reason why a proposal gets accepted. Rather
it is because the collective judgment of the committee members is that
the pros considerably outweigh the cons.

You really should look more closely at Loki's SmartPtr, and
policy-based design in general, as Andrei suggested in another post.
It may give you just the ability to tinker with the behavior and
implementation that you seem to be looking for.

--Beman Dawes

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pdimov@mmltd.net (Peter Dimov)
Date: Wed, 16 Jul 2003 02:05:00 +0000 (UTC)
Raw View
gi2nospam@mariani.ws (Gianni Mariani) wrote in message news:<beuu4n$cq9@dispatch.concentric.net>...
>
> This question should be raised however: The TR1 suggested smart pointers
> do not pass the "zero overhead principle" requirement so why should it
> be adopted as part of the C++ standard ?

"Zero overhead" was a requirement (once) for core language features,
when C++ needed to survive the competition with C. The goal was that a
C program, when compiled with a C++ compiler, would be just as fast.

>From a pragmatic point of view, there is no such thing as "zero
overhead" (also known as "free lunch"). Someone will always have to
pay the price (not necessarily in performance, of course). For
example, exceptions are "zero overhead" in theory, but on Windows, the
non-zero overhead implementation has the advantage that exceptions can
safely travel through code compiled with different compilers (and even
languages).

Another example: many C++ types are position independent, and can be
moved around in memory with memmove. Even if you only have position
independent types in your program, you still suffer the overhead when
std::vector uses its copy/destroy loop to move elements around.
Clearly, std::vector violates the zero overhead principle. (In more
than one way, I might add.) One might say that so does std::map; if
you don't need the ordering, you stil pay for it.

The difference between a standard library element and a core language
feature is, of course, that you can simply decide to not use the
standard library element if you cannot afford it.

So the real question is not "Is X zero overhead" but "Is X useful, and
if so, can we afford it?" I think that yes, we can afford shared_ptr,
and it is useful.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: richard@ex-parrot.com (Richard Smith)
Date: Wed, 16 Jul 2003 16:47:39 +0000 (UTC)
Raw View
Gianni Mariani wrote:

> a) when an application uses smart pointers it will be pervasive and
> hence the performance of the smart pointer implementation is likely
> siginificant.

I don't think they necessarily will be used pervasively.  I
certainly have writen applications where several sorts of
smart pointer have been used sparingly through the code, but
no single smart pointer class could be described as deployed
'pervasively'.

> b) having an "optimal" or "near optimal" implementation available for a
> smart reference counted pointer is likely to be used when the
> performance/complexity tradeoff warrants the compromize.

Indeed.  However I've still to be convinced that your
framework is more efficient than TR1's shared_ptr perhaps
combined with TR1's weak_ptr, std::auto_ptr and/or
boost::scoped_ptr as appropriate.

> Having said that, I consider that there is a possible alternative
> implementation of AT_LifeLine that presents a very small overhead and in
> some cases may be optimized to zero overhead (when fully inlined) with
> exception safety included.

Can I ask two quick questions about it?

  1.  Are AddRef() and Release() still virtual functions?
      If so, it will be rare for even the best compiler to
      inline them.

  2.  Does AT_LifeLine's destructor Release() the held
      pointer if it has not transfered ownership away?  If
      not, I fail to see how it can be considered exception
      safe in any normal sense of the term.

>  I would propose this as possibly a 4th
> member of the AT_Life* members leaving AT_LifeLine as a less safe but
> 100% zero overhead solution.

If you want to go down this route, I'd advise (as several
others have already in this thread) a policy based framework
such as Andrei Alexandrescu's Loki::SmartPtr.  If you want
to understand how this works, you should read Chapter 7 of
his book "Modern C++ Design"; another good discussion on
policy-based smart pointers can be found in Chapter 20 of
Vandevoorde & Josuttis' book "C++ Templates: The Complete
Guide".

> Now, these could be implemented on top of boost::shared_ptr but it seems
> like it's already trying to do too much.

Depends.  Would you like a thread-safe weak_ptr to interact
correctly with your class?  If so implementing it on top of
TR1's shared_ptr may well be the best solution.  As I think
other parts of this thread have demonstrated, getting an
optimal thread-safe shared_ptr implementation that supports
weak_ptr is not entirely trivial.

> So I've given myself yet more homework. If anyone is interested in the
> alternative AT_Life* smart pointers let me know and maybe I'll work on
> it sooner.

The particular issue of rvalue references [see N1377] might
possibly be added to the language.

> This question should be raised however: The TR1 suggested smart pointers
> do not pass the "zero overhead principle" requirement so why should it
> be adopted as part of the C++ standard ?

In what way do you think TR1's shared_ptr violates this
principle?  I can think of two possibilities:

  1.  The TR1 shared_ptr class requires a virtual function
      call (or equivalent) to destroy the pointee.  This is
      due to generic deletion function object.

      There's no two ways about this: it is an additional
      overhead, though not very large.  (See one of my other
      posts to this thread.  I believe I measured a 5%
      difference using quite a contrived example.)

      In it's defence, it allows the TR1 shared_ptr to
      interoperate with other reference counted pointer
      frameworks, including intrusive ones such as COMs.  It
      also allows it to be used on incomplete types, as in
      the "grin_ptr" idiom.  (Incidentally, as shared_ptr
      can easily be made to support this idiom, would it be
      worth including wording in the proposed text to
      mandate this?)

  2.  Because of the need to support weak_ptr, and in
      particular the expired() method, shared_ptr needs to
      maintain two reference counts.

      Again, this is true.  However maintaining two
      reference counts is not, per se, any harder than
      maintaining one.  Many current implementations
      (including the Boost one) suffer greater than
      necessary overheads in multi-threaded builds because
      of the way they handle exception safety.  I think the
      discussion elsewhere in this thread has now
      demonstrated that this can be avoided.

If you think there's another way in which TR1's shared_ptr
violates the zero-overhead principle, could you let us know?

--
Richard Smith

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Wed, 16 Jul 2003 17:44:41 +0000 (UTC)
Raw View
Beman Dawes wrote:
> gi2nospam@mariani.ws (Gianni Mariani) wrote in message news:<beuu4n$cq9@dispatch.concentric.net>...
>
>
>>This question should be raised however: The TR1 suggested smart pointers
>>do not pass the "zero overhead principle" requirement so why should it
>>be adopted as part of the C++ standard ?
>
>
> The C++ committee isn't an authoritarian organization with a rigid set
> of rules or formulas determining what gets accepted and what doesn't.
> Rather it is a set of independent individuals who make up their own
> minds about proposals. Some are primarily users, while others are
> implementers, teachers, researchers, or writers. A wide range of
> viewpoints.
>
> So there is no mechanistic reason why a proposal gets accepted. Rather
> it is because the collective judgment of the committee members is that
> the pros considerably outweigh the cons.

You miss the point of the question.  It's not a procedural question as
I'm familiar with the various standardization processes and have been
involved with it in past lives. This is a question on it's meirts.  I'd
like to hear the ACTUAL pro's and cons that comittee is weighing and
which ones it has decided to favour.  I'd like to understand more about
how the comittee is balancing the decisions and I'd like to see a more
open discussion.

>
> You really should look more closely at Loki's SmartPtr, and
> policy-based design in general, as Andrei suggested in another post.
> It may give you just the ability to tinker with the behavior and
> implementation that you seem to be looking for.

What do you think I'm looking for ?

G

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Wed, 16 Jul 2003 17:51:17 +0000 (UTC)
Raw View
Peter Dimov wrote:
> gi2nospam@mariani.ws (Gianni Mariani) wrote in message news:<beuu4n$cq9@dispatch.concentric.net>...
>
>>This question should be raised however: The TR1 suggested smart pointers
>>do not pass the "zero overhead principle" requirement so why should it
>>be adopted as part of the C++ standard ?
>
>
[comments on the state of the zero overhead requirement]

>
> So the real question is not "Is X zero overhead" but "Is X useful, and
> if so, can we afford it?" I think that yes, we can afford shared_ptr,
> and it is useful.

Fair comment.

There are 3 (possibly more) fundamental knobs being teaked when making
design decisions,

a) solution complexity or programmer learning curve
b) feature set
c) performance or minimal overhead of solution

It seems that loki is an attempt to optimize on b), boost is trying to
optimize on a) while Life* is an attempt at optimizing c). (house
painter's brush being used here, don't be picky about it).

So your assesment of the "can we afford it" question is totally
subjective and in the bigger picture `could' be totally off.

It seems like there is a slightly more complex soution than boost with a
less rich feature set than loki that may provide ultimately a solution
that can replace C in all circumstances.

For example, there is a "belief" that C++ is unsuitable for kernel
development because the "overhead" of C++ is just too much.  Without
getting too far into religion, the mindset is one of maximal performance
at all cost.

It seems as though the consensus in this group is "to heck with optimal
performance" what we want is a solution space with less complexity
(lower learning costs) at the expense of performance.

My ultimate concern is that abandoning performance for reducing the
learning curve is probably the wrong trade-off for C++.  If I wanted a
reduced learning curve solution I'd pick a totally different language,
java, perl, python, pick your favourite lower learning curve language
here.  C++ has been the language that allows a programmer to have the
full flexibility of C with a whole bunch of ways to improve type safety
and actually improve on the performance of applications written in C.

Or said in another way: Who will using boost smart pointers please and
are these the appropriate target community for C++ ?  So who in the
standard's committee is thinking at this level and is there a concensus?



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kanze@gabi-soft.fr
Date: Wed, 16 Jul 2003 17:51:19 +0000 (UTC)
Raw View
pdimov@mmltd.net (Peter Dimov) wrote in message
news:<7dc3b1ea.0307150812.57ed71d9@posting.google.com>...

> The difference between a standard library element and a core language
> feature is, of course, that you can simply decide to not use the
> standard library element if you cannot afford it.

That's a tenuous difference at best.  There's nothing that requires you
to use templates, or exceptions (except maybe common sense).  And
there's certainly nothing to require you to use double or float -- most
of my programs don't.  And I actually once ran into a C programmer who
didn't even know that there were operators like '&' or '|'.

I think about the only thing that you absolutely must use is a function
definition -- a C++ program must include a definition of the function
main.

> So the real question is not "Is X zero overhead" but "Is X useful, and
> if so, can we afford it?" I think that yes, we can afford shared_ptr,
> and it is useful.

The zero overhead has always been a bit of a misleader, I think.  But
the goal has always been zero overhead if you don't use it -- not zero
overhead, absolutely.  I presume that if I don't use smart pointers, my
program will not run slower or use more memory than it would otherwise.
Zero overhead if I don't use it.  The fact that for some more or less
small subset of the feature may have a cheaper solution is not
forbidden.

--
James Kanze           GABI Software        mailto:kanze@gabi-soft.fr
Conseils en informatique orient   e objet/     http://www.gabi-soft.fr
                    Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: richard@ex-parrot.com (Richard Smith)
Date: Wed, 16 Jul 2003 21:28:23 +0000 (UTC)
Raw View
Gianni Mariani wrote:

> I have yet to come across the need for an externally reference counted
> facility.

A point that no-one has yet mentioned is that supporting
thread-safe weak pointers with an intrusive reference
counted pointer is very difficult.  (By intrusive, I mean
that the shared count, the weak count and any syncronisation
objects such as mutexes or spinlocks are held by value in
the class.  This means that the shared pointer never
dynamically allocates any memory.)  Without weak pointers,
it is hard to get around the cyclic references that are
often encountered in real-world applications.

I think I have finally come up with a solution, but it
relies on some pretty nasty trickery.  I think it's legal,
but I'd like a second opinion.  The potentially dubious bit
can be collapsed into the following code:

  struct foo {
   ~foo() {}
    foo() {} // Does not initialise x
    int x;
  };

  class bar : public foo {};

  int main() {
    foo* f = new bar;
    void* p = dynamic_cast<void*>(f);

    f->x = 42;
    f->~foo();
    assert( ++f->x == 43 );   // I think this is legal ...
    new(f) foo;
    assert( --f->x == 42 );   // ... but is this?

    f->foo::~foo();
    operator delete(p);
  }

Now, by my reading of the Standard, foo's destructor
implictly calls this->x.~int() [12.4/6].  5.2.4/1 says that
for a non-class type, such as int, the only affect of this
is to evaluate this->x.  This presumably means that the
following assert line is guaranteed to succeed.  (I think
this is consistent with 12.4/14.)

The placement new operator implicitly called by the new
expression does not alter the memory pointed to by f
[18.4.1.3/3].  Next, the default constructor of foo is
called.  This does not explicitly initialise x, this means
that 12.6.2/4, bullet 2 applies, which says "the entity is
not initialised".  It seems to me that this means the memory
is unmodified -- but is this actually true?  If so, the
following assert is guaranteed to succeed, otherwise it
invokes undefined behaviour.

--
Richard Smith

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pdimov@mmltd.net (Peter Dimov)
Date: Wed, 16 Jul 2003 22:14:23 +0000 (UTC)
Raw View
richard@ex-parrot.com (Richard Smith) wrote in message news:<Pine.LNX.4.55.0307161216240.21674@sphinx.mythic-beasts.com>...
>       [...]  It
>       also allows it to be used on incomplete types, as in
>       the "grin_ptr" idiom.  (Incidentally, as shared_ptr
>       can easily be made to support this idiom, would it be
>       worth including wording in the proposed text to
>       mandate this?)

This is an inadvertent omission, the intent has always been for
17.4.3.6/2 last bullet to not apply to shared_ptr, weak_ptr and
enable_shared_from_this. We need to remember to add this to the TR1
issues list once it's established.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: andys@despammed.com (Andy Sawyer)
Date: Thu, 17 Jul 2003 01:45:59 +0000 (UTC)
Raw View
In article <bf28l1$cq1@dispatch.concentric.net>,
 on Wed, 16 Jul 2003 17:44:41 +0000 (UTC),
 gi2nospam@mariani.ws (Gianni Mariani) wrote:

> You miss the point of the question.  It's not a procedural question as
> I'm familiar with the various standardization processes and have been
> involved with it in past lives. This is a question on it's meirts.
> I'd like to hear the ACTUAL pro's and cons that comittee is weighing
> and which ones it has decided to favour.  I'd like to understand more
> about how the comittee is balancing the decisions and I'd like to see
> a more open discussion.

 The discussion IS open - it's not held in smoke-filled rooms behind
locked doors and armed guards :). Much of the discussion happens online
via email between committee meetings. If, as you say, you're familiar
with standardization processes, then you know you have the opportunity
to participate in the discussion. Join your NSB's (National Standards
Body) C++ standards group. Depending on your NSB, this may or may not
cost you money=B9 - but that's outside the control of the C++ commitee.
If your NSB has no such group (or has unacceptably high membership
fees), then perhaps another one would allow you membership (this,
obviously, will depend on the NSB in question, but there *are* people
who have participated in the WG21 discussion as representatives of
countries other than that in which they live). Join the committe. Go to
the meetings. It's as open as you want it to be.

 If you're expecting to see detailed minutes of every single point
raised during committee discussion, then you're probably out of luck
unless you attend and make such minutes yourself, for a couple of
reasons. Firstly, a WG21 meeting usually lasts a week or so, and much of
the time is split into several subgroups - thats an awful lot of meeting
time to minute. Secondly, most of the people attending the meetings are
far too busy participating in the discussion to make such detailed
minutes, and generally only key points are recorded (this, I find, is
common whenever technically capable people are allowed to hold meetings
without the overhead of having some PHB present :)

 What you *can't* necessarily expect is for *your* viewpoint to be
expressed in a meeting - unless you actually attend and express it
yourself. That doesn't mean it *won't* be expressed, especially if you
can persuade someone who does attend a meeting to express it for you. If
you participate in the process, then comitee members are likely to be
aware of it and take it into consideration when forming their own
opinions.

 In the current context (smart pointers), there are several commitee
members who have expended considerable amounts of time and energy in
this area. Some (but not all) of them post in (and, presumably, read)
this newsgroup. They also participate in other forums (notably Boost).
Since they _have_ expended that time and effort, you may have a hard
time persuading them that your approach has benefits over other
techniques - but you won't know until you try.

Regards,
 Andy S.
=B9  In my case, membership doesn't cost me anything. Your situation may
be different.
--=20
"Light thinks it travels faster than anything but it is wrong. No matter
 how fast light travels it finds the darkness has always got there first,
 and is waiting for it."                  -- Terry Pratchett, Reaper Man

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: emild@collectivestudios.com (Emil Dotchevski)
Date: Thu, 17 Jul 2003 02:42:31 +0000 (UTC)
Raw View
> There are 3 (possibly more) fundamental knobs being teaked when making
> design decisions,
>
> a) solution complexity or programmer learning curve
> b) feature set
> c) performance or minimal overhead of solution
>
> It seems that loki is an attempt to optimize on b), boost is trying to
> optimize on a) while Life* is an attempt at optimizing c).

This classification is misleading. Loki::SmartPtr does *not* provide a
superset of the boost::shared_ptr functionality: you can't define a
set of policies for Loki::SmartPtr that can make it act "just like" a
boost::shared_ptr.

> It seems as though the consensus in this group is "to heck with optimal
> performance" what we want is a solution space with less complexity
> (lower learning costs) at the expense of performance.

Don't confuse complexity with usability: boost::shared_ptr is very
very usable while being very very simple to use. This, together with
its robust design and years of real world use is what makes
boost::shared_ptr the only candidate for standardization. You can't
standardize something that has not been proven useful and bug-free,
and this comes with years of real life usage.

Before you say boost::shared_ptr is slow or whatever, you should take
the time to study it -- if for no other reason simply because it is
now standardized.

One of the most useful features of boost::shared_ptr is the fact that
it can safely delete objects of incomplete classes, as long as their
complete definition was present at the time when the shared_ptr was
initialized. This can be combined with the use of protected,
non-virtual base destructors to further improve safety (by making it
impossible to use operator delete to dispose of a managed object)
while improving performance (you pay for a single indirection only,
just as in a normal use of a virtual destructor).

The ability to define boost::shared_ptr to an object of incomplete
class is also an important feature of the raw C/C++ pointers. This
makes it possible to use boost::shared_ptr to hide implementation
details (PIMPL) without compromising type safety. Boost::shared_ptr
aside, the only other option you have in this case is to use a raw
pointer.

Another use of boost::shared_ptr that only raw pointers can match is
that you can completely erase the type information from it, except
that it is perfectly safe:

//Header
boost::shared_ptr<void> create_foo();

//CPP
class Foo
{
  ...
};
boost::shared_ptr<void> create_foo()
{
  return boost::shared_ptr<void>(new Foo());
}

//Somewhere else completely
{
  boost::shared_ptr<void> p( create_foo() );
  ...
} //Calls ~Foo, without even having a declaration of class Foo.

You can also define a custom deleter when you initialize a
boost::shared_ptr and this does *not* involve additional template
arguments: all shared_ptr<T> objects are of the same class for the
same T, regardless of whether custom deleter was specified or not.
This is yet another example of a useful feature that actualy improves
the performance by reducing the number of automatic temporaries. For
comparison, using a policy-based design could sometimes require a
temporary object when passing a smart pointer to a function, even if
the function takes its argument by const reference.

> My ultimate concern is that abandoning performance for reducing the
> learning curve is probably the wrong trade-off for C++.

Can you be more specific? What makes you think boost::shared_ptr is
slow? Any concrete performance data that shows another solution to be
any faster?

> Or said in another way: Who will using boost smart pointers please and
> are these the appropriate target community for C++ ?  So who in the
> standard's committee is thinking at this level and is there a concensus?

Obviously, the anwser to the last question is 'yes'. Therefore, the
answer to the first question is also 'yes', as it is the committee
that decides what is appropriate for C++ and what isn't. Everything
else is simply a personal opinion, often combined with lack of
experience and/or understanding of C++.

--Emil

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Fri, 18 Jul 2003 02:36:30 +0000 (UTC)
Raw View
Emil Dotchevski wrote:
>>There are 3 (possibly more) fundamental knobs being teaked when making
>>design decisions,
>>
>>a) solution complexity or programmer learning curve
>>b) feature set
>>c) performance or minimal overhead of solution
>>
>>It seems that loki is an attempt to optimize on b), boost is trying to
>>optimize on a) while Life* is an attempt at optimizing c).
>
>
> This classification is misleading. Loki::SmartPtr does *not* provide a
> superset of the boost::shared_ptr functionality: you can't define a
> set of policies for Loki::SmartPtr that can make it act "just like" a
> boost::shared_ptr.
>

How does your statement relate to design goals ?

>
>>It seems as though the consensus in this group is "to heck with optimal
>>performance" what we want is a solution space with less complexity
>>(lower learning costs) at the expense of performance.
>
>
> Don't confuse complexity with usability: boost::shared_ptr is very
> very usable while being very very simple to use. This, together with
> its robust design and years of real world use is what makes
> boost::shared_ptr the only candidate for standardization. You can't
> standardize something that has not been proven useful and bug-free,
> and this comes with years of real life usage.

Again, what has this to do with design trade-offs ?

>
> Before you say boost::shared_ptr is slow or whatever, you should take
> the time to study it -- if for no other reason simply because it is
> now standardized.
>

I have.  It makes some things that I normally do, slower.  Especially
when dealing with COM or COM-Like pointers.  The technique I have
described, which you evidently failed to mention here, eliminates the
need to perform reference counting in most of the uses of the smart
pointer. (using AT_LifeView, check it out).

> One of the most useful features of boost::shared_ptr is the fact that
> it can safely delete objects of incomplete classes, as long as their
> complete definition was present at the time when the shared_ptr was
> initialized. This can be combined with the use of protected,
> non-virtual base destructors to further improve safety (by making it
> impossible to use operator delete to dispose of a managed object)
> while improving performance (you pay for a single indirection only,
> just as in a normal use of a virtual destructor).

That's nice.  Why exactly is this relevant ?

>
> The ability to define boost::shared_ptr to an object of incomplete
> class is also an important feature of the raw C/C++ pointers. This
> makes it possible to use boost::shared_ptr to hide implementation
> details (PIMPL) without compromising type safety. Boost::shared_ptr
> aside, the only other option you have in this case is to use a raw
> pointer.
>
> Another use of boost::shared_ptr that only raw pointers can match is
> that you can completely erase the type information from it, except
> that it is perfectly safe:
>
> //Header
> boost::shared_ptr<void> create_foo();
>
> //CPP
> class Foo
> {
>   ...
> };
> boost::shared_ptr<void> create_foo()
> {
>   return boost::shared_ptr<void>(new Foo());
> }
>
> //Somewhere else completely
> {
>   boost::shared_ptr<void> p( create_foo() );
>   ...
> } //Calls ~Foo, without even having a declaration of class Foo.


Your assertion being ?  You can do this with AT_Life* pointers and I
suspect you can do this with loki pointers as well.  I fail to see what
your talking about and how this relates to design goals.

>
> You can also define a custom deleter when you initialize a
> boost::shared_ptr and this does *not* involve additional template
> arguments: all shared_ptr<T> objects are of the same class for the
> same T, regardless of whether custom deleter was specified or not.
> This is yet another example of a useful feature that actualy improves
> the performance by reducing the number of automatic temporaries. For
> comparison, using a policy-based design could sometimes require a
> temporary object when passing a smart pointer to a function, even if
> the function takes its argument by const reference.

Again, that's nice but why is this relevant to my previous post ?

>
>
>>My ultimate concern is that abandoning performance for reducing the
>>learning curve is probably the wrong trade-off for C++.
>
>
> Can you be more specific? What makes you think boost::shared_ptr is
> slow? Any concrete performance data that shows another solution to be
> any faster?
>

Slow ?  How did you come to this conclusion ? Still, this does not
relate to design goals.

>
>>Or said in another way: Who will using boost smart pointers please and
>>are these the appropriate target community for C++ ?  So who in the
>>standard's committee is thinking at this level and is there a concensus?
>
>
> Obviously, the anwser to the last question is 'yes'. Therefore, the
> answer to the first question is also 'yes', as it is the committee
> that decides what is appropriate for C++ and what isn't. Everything
> else is simply a personal opinion, often combined with lack of
> experience and/or understanding of C++.
>

If the answer is so obvious then I suspect you'll be more than willing
to explain it since you've inadvertently missed placing it in the prior
paragraph.

If you cannot describe the design goals of boost:shared_ptr in 200 words
or less, you may not understand the meaning of my previous post.

I truly don't know what the design priorities lie for boost.  I honestly
believe that boost:smart_ptr may actually be the wrong answer for some
designs but unless someone can describe exactly what smart_ptr's design
goals are the discussion will be rather frustrating.

It seems like many differences in opinion in this thread arise from an
difference in experience.  For example you'll see in my smart pointer
design (AT_Life*) there is no weak poiinter.  A thing that is missing
here is a concept I call "twin" which performs much of what a weak
pointer does but is exceedingly different and needs no support from
smart_ptr. std:list frustrates me because I would like an element of a
list to be able to manage itself without any smart pointers.  These are
examples of where I would choose a design which is subjectively
"complex" but less code is being generated and hence subjectively
"simpler".  I personally find the kinds of designs that std::list is
used for optimal in both expression and peformance.  These are personal
differences.  The prior post HAS NOTHING TO do with this. This
discussion comes after we have some stated goals.

Your last sentence is half right.  Design can be somewhat based on
personal opinion but the underlying foundations are based on fact. I'm
trying to establish a statement of fact which is the answer to the
question, "what are the design goals of smart_ptr ?", in particular what
is the priority of design decisions and the idioms it is indended to
solve most efficiently.  Again this should be small, one pager kinds of
descriptions.

I can make a cut at it for AT_Life* smart pointers :

AT_Life* smart pointers are intended to provide a framework for managing
reference counted objects in an "intrusive" manner as optimally as
possible.  Design decisions have favoured elimination of operations (see
AT_LifeView) through providing a mechanism where reference counted
pointer policies are described in interfaces and appropriate operations
deduced when creating temporary objects.  The temporary objects are
usually no more expensive than operations on a raw pointer.  This
"policy" paradigm has a small number of "holes" and it is a design
choice to leave the holes in favour of maintaining an extremely light
weight implementation.

Can you describe the design trade-off formula boost::smart_ptr ?

/G


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gi2nospam@mariani.ws (Gianni Mariani)
Date: Fri, 11 Jul 2003 01:43:21 +0000 (UTC)
Raw View
Richard Smith wrote:
> Gianni Mariani wrote:

Ricahrd,

Thanks, you make some good points I need to consider further,  However I
will answer some of your questions.

>
>
>>Howard Hinnant wrote:
>>
>>>My day is simply too full to drop everything everytime somebody says:
>>>I've got a better idea, want to know more?
>>
>>Hint of arrogance I hear ?
>
>
> I doubt it; more likely a statement of fact.
>

Not touching this ... :)

>
>>I have attached the header that defines the templates.  If you're
>>interested I'll make an alpha release of the entire library soon as it
>>stands soon.
>
>
> Could I draw your attention to Howard Hinnant's comment "it
> would be to your advantage to compare and contrast with it
> (with code examples!) in order to shorten your reader's
> learning curve".  I think you would have been wise to have
> followed it.  I suspect you'll find relatively few people
> will be prepared to wade through a 90k post in order to
> separate the germane differences from the TR1 shared_ptr
> from differences in style or syntax.
>
> I've made some detailed comments on your suggestion, below.
> For those who don't want to wade through them, here's a
> summary:
>
>  1.  If you want it to be considered instead of
>      boost::shared_ptr for C++0x, then write it in a way
>      that looks and feels like the rest of the STL.  In
>      particular, where possible make expressions that work
>      on std::auto_ptr have the same meaning on your
>      pointers.
>

Fair comment, if there is merit, this is mostly cosmetic and can be
easily done.  The original intent was not for a substitute for
boost::shared_ptr.

>  2.  You seem to be advocating an intrusive shared_ptr
>      implementation to the exclusion of any possible
>      externally reference counted implementation.  I've
>      nothing against and intrusive implementation -- indeed
>      I think it would be a good thing.  HOWEVER, I wouldn't
>      want to require all shared pointers to be intrusive.

Are not these 2 different concepts (intrusive vs externally reference
counted ?).

I have yet to come across the need for an externally reference counted
facility.  I'll need to consider this further.

>
>  3.  Your AT_LifeLine class is attempting to solve the same
>      problem as std::auto_ptr, and in the same way.  You'd
>      be better off looking at std::auto_ptr, learning
>      thoroughly how it works (it is decidedly non-trivial)
>      and thinking about how it might need to be changed (if
>      at all) in the presence of additional standardised
>      smart pointers.

Yes, perhaps.  I have yet to find a case where auto_ptr was satisfactory
and hence I overlooked it.  Need to reconsider since it's been a while
since I gave up on it (auto_ptr that is).

>
>  4.  Your smart pointer classes have a great many implicit
>      conversions to and from raw pointers.  This is
>      dangerous: even though an intrusive implementation
>      allows smart pointers to be converted to and from raw
>      pointers, it is very easy to get resource leaks using
>      this.

Yes, the model requies that no raw pointers are used in the code except
from a new, or when a pointer is "transferred" which admitedly is a
hang-over from a while back.  That could go.

>
>  5.  The idea of using optional run-time assertions to catch
>      usage errors is in principle good, but in this case
>      leads to poor exception safety.  Consider what happens
>      when you construct a AT_LifeLine (your std::auto_ptr
>      clone) and an exception causes it to be destroyed
>      before relinquishing owenership.  Depending on whether
>      or not you have debugging turned on, you'll either get
>      an assertion or a resource leakk, either of which are
>      bad.

This is probably the most significant issue.  Since I shy away from
using exceptions due to the significant complexity, this has never been
an issue.  Having said that, expception safe code is a practice I
advocate and hence your observation is correct.

I'm not as concerned about the assertion as I am with the resource leak.
  The assertion is there to catch the very errors it asserts for.


>
>  6.  Your AT_LifeTime class is basically the same as
>      boost::shared_ptr, other than it uses an intrusive
>      reference count.  Can you summarise exactly what you
>      think this class offers that boost::shared_ptr does
>      not.  In particular, can we see example usages of both,
>      and, if your arguments are based on efficiency, some
>      reproducible figures backing up your claims.

I can.

>
>  7.  I'm not at all convinced that all of the conversions
>      between AT_LifeTime, AT_LifeLine and AT_LifeView
>      increment and decrement the reference counts as
>      appropriate.

It has passed a set of tests ...

>
>  8.  The AT_LifeView class doesn't (so far as I can see)
>      provide any functionality that a raw pointer or
>      reference doesn't provide.  Therefore I don't see any
>      point to it.

Except that the semantics of a raw pointer are ambiguous while that of a
LifeView is not.

>
>  9.  AT_Pointer seems to be another attempt to reimplement
>      std::auto_ptr.  Why bother re-implementing it?  Your
>      version doesn't work as well as the standard one.

AT_Pointer happens to be in the same file.  AT_Pointer works like so:

std::list< AT_Pointer< foo * > >

auto_ptr does not.

>
> In short, I don't think you'll generate much enthusiasm for
> your implementation, however, if you think there are
> worthwhile points to be considered, do suggest them, but as
> modifications to std::auto_ptr, TR1's std::shared_ptr or
> std::weak_ptr.  Proposing a wholesale alternative without
> clear discussion on its benefits over the existing one will
> not win you many supporters.
>
>
> ------------------------------------------------------------
> Specific comments on your code follow:
>
>
>
>>template < class w_ClassRef > class AT_ReferenceTraits;
>
>
> If you're attempting to propose something as an alternative
> to the TR1 shared_ptr, and hence eventually for probable
> addition to the Standard, you should use the same naming
> convention as the Standard, viz, lower_case class and
> function names, and InterCaps for template parameters.
> Oh, and no reverse Polish.  This may sound pedantic, but
> people used to reading the Standard library will find it
> easier to parse.

When and if we get to that point, we'll worry about that.

>
> If you want serious consideration to be given to your
> suggestions, you have to go out of your way to make it easy
> for people to quickly see its salient points.  This also
> means removing the extraneous clutter from the file so that
> the *interface* is immediately clear.  (I've done this to
> the bits of your design that I've quoted.)
>
>
>>/**
>> * AT_LifeControl is an abstract base class for any class whose lifetime
>> * is managed by means of reference counting.
>> */
>>class AT_LifeControl
>>{
>>  public:
>>    virtual ~AT_LifeControl();
>>    virtual int AddRef() = 0;
>>    virtual int Release() = 0;
>>};
>
>
> You're now advocating an intrusive implementation, are you?
> This is not a bad idea, as long as an externally reference
> counted implementation is also possible, ideally without
> exposing the difference to end user.

I need to see why you would want to mix these concepts.

>
> (Clearly there are potential differences in interface
> between external and intrusive shared pointers.  For
> example, it could be safe to do
>
>   shared_ptr<T> a( new T ), b( a.get() );
>
> with a intrusive shared pointer, but rarely will be with an
> external one.  IMHO, this is best left illegal for all
> shared pointers, leaving the interfaces to intrusive and
> external pointers exactly the same.)

What would be the purpose of designing a class that required external
reference counting ?  If this is an issue primarily of legacy
compatability, then I have still not run into this issue.

I'd need to see what real-life problems are being solved with external
reference counting.

>
>
>>template <class w_ClassRef> class AT_ReferenceTraits
>>{
>>  public:
>>    static inline void IncRefCount( w_ClassRef i_ptr )
>>      { i_ptr->AddRef(); }
>>    static inline void DecRefCount( w_ClassRef i_ptr )
>>      { i_ptr->Release(); }
>>};
>
>
> When an object is created, does it have a reference count of
> 1, or must IncRefCount() be called?

has a reference count of 1.

>
> [quoting out of order]
>
>> * If you are using your own reference-counting scheme, i.e. one not
>> * based on AT_LifeControl, then you will need to define your own
>> * helper class that implements the same interface as AT_ReferenceTraits.
>
>
> Can you write me such a policy class that handles external
> reference counting?  What are the function arguments to
> IncRefCount?  And how is the reference count accessed from
> them?
>

I suspect so.

>
>> * (Incidentally, the name "AT_LifeLine" is meant to evoke the image
>> * of someone tossing a rope to someone adrift at sea, saying, in
>> * effect, "Here, this is yours, you'd better take ownership of it.")
>
>
> Evoking the image of a shared pointer might be more
> appropriate.  Seriously, though, I really don't like your
> choices of names.
>

Seriously, I don't care.  ... I truly don't mean to be rude, in my
career I have wasted more time arguing about aesthetics and I have very
little intention to waste much more time.  Having said that I can
discuss this topic ad nauseum which is not very productive.  My
threshold however to a better reccomendation is very low.  In other
words, if you have a better name, I'll change it.  "shared_ptr" is
probably better.

>
>> * Instrumenting an AT_LifeLine object to check for memory leaks is a
>> * useful debugging aid, but is expensive in terms of execution time.
>
>
> This is an quality-of-implementation issue, and really isn't
> relevant to this discussion.  (I've removed all such
> debugging hooks from the quoted text.)
>
>
>>template <
>>    class w_ClassRef,
>>    class w_RefTraits = AT_ReferenceTraits< w_ClassRef >
>>
>>class AT_LifeLine
>>{
>>    w_ClassRef m_ptrVal;
>
>
> So, w_ClassRef is supposed to be a pointer type.  I.e.
> completely at odds to almost all existing smart pointer
> implementions where the first argument is the pointee.  Thus
> you want to write AT_LifeLine<T*> rather than shared_ptr<T>.

yes.

or

typedef X * Xp;

AT_Lifetime<Xp>

>
>
>>        inline AT_LifeLine(
>>            const AT_LifeLine< w_ClassRef, w_RefTraits > & i_ptr
>>  )
>>          : m_ptrVal( i_ptr.m_ptrVal )
>>        {}
>
>
> You avoid incrementing the reference count here, using the
> principle that ownership flows from the RHS to the LHS
> without ever being shared between them.  A few points:-
>
>  *  The RHS is being semantically modified by the
>     constructor, and therefore should be taken by
>     non-const reference.

hmm, ok, you've hit a point that I glossed over during implementation.
The reference count is somewhat separate from the rest of the class
implementation.  Hence the reference count is mutable.

>
>  *  I know you had MadeTransfer functions to assert in
>     "debug" builds, but is it really your intention that a
>     "non-debug" build can produce subtle bugs (e.g. double
>     destruction) by copying such a pointer twice?  I would
>     think that just setting m_ptrVal to NULL on the RHS
>     would be safest.  At least that way, you'll get a SEGV
>     fairly rapidly when something goes wrong.

The original intent was that the LifeLine is akin to passing a raw
pointer and the LifeLine was a way to mark such a pointer as passing the
responsibility to decrement the reference count.  Your suggestion
changes that intent but requires consideration.

>
>  *  It looks to me like you're getting very close to
>     reimplementing std::auto_ptr.
>
>
>>        inline AT_LifeLine( w_ClassRef i_ptr = 0 )
>>          : m_ptrVal( i_ptr )
>>        {}
>
>
> Should be explicit.

yes.

>
>
>>        inline AT_LifeLine( w_ClassRef i_ptr, bool i_takeOwnership )
>>          : m_ptrVal( i_ptr )
>>        {
>>            if ( i_takeOwnership ) {
>>                if ( i_ptr ) {
>>                    w_RefTraits::IncRefCount( i_ptr );
>>                }
>>            }
>>        }
>
>
> What's wrong with combining this with the previous
> constructor by defaulting i_takeOwnership to false?
>
> Hang on.  Is the intention that I must write
>
>   AT_LifeLine<T*>( new T, true )

No. This constructor can go.

>
> to take ownership of the pointer?  This will be very error
> prone, as people are used to using smart pointers that use
> the syntax
>
>   shared_ptr<T>( new T )
>

AT_LifeLine<T*>( new T )

does the same.

> Having it *not* take ownership by default is very
> counter-intuitive.
>

Right.

>
>>        inline ~AT_LifeLine() {}
>
>
> What happens when an exception results in the pointer being
> destroyed before it has transfered ownership away?  Like
> this it leaks resources, or with your debugging hooks, will
> assert.  This is a very bad.

The alternative may also be "bad".

>
>
>>        inline AT_LifeLine & operator= ( const w_ClassRef i_ptr )
>>        {
>>          m_ptrVal = i_ptr;
>>          return *this;
>>        }
>
>
> I don't think you want an operator= that has a raw pointer
> on its RHS.  And I note that it doesn't take ownership of
> the pointer.

 AT_LifeLine<T*> p;

 ...

 p = new T;

... I think you found a bug.

>
>
>>        inline w_ClassRef operator-> () const
>>        {
>>            return m_ptrVal;
>>        }
>
>
> It's normal to supply an operator*() as well.
>

This would provide a raw pointer ?

>
>>        inline operator w_ClassRef () const
>>        {
>>            return m_ptrVal;
>>        }
>
>
> Eurgh.  Implicit conversions to the raw pointer type.  Have
> you considered how easy it would be to implicitly convert it
> to a raw pointer and thus destroy the smart pointer,
> probably causing a resource leak.  (Unless the destructor
> asserts, which doesn't seem much better.)
>

No.  In the intended use, this would never happen.

>
>>        inline operator bool () const
>>        {
>>            return m_ptrVal != 0;
>>        }
>
>
> Look at how boost::shared_ptr makes use of
>
>   typedef T* (shared_ptr<T>::*boolean)() const;
>
>   operator boolean() const
>     { return px == 0? 0: &shared_ptr<T>::get; }
>
> This is a far better solution to this problem as your
> solution implicitly allows expressions such as
>
>   AT_LifeLine a, b, c( a / b );
>

OK,

>
>
>
>>template < class w_ClassRef, class w_RefTraits >
>>class AT_LifeTime
>
>
> Many of my comments about AT_LifeLine apply again here.  I
> won't bother repeating them.
>
>
>>        inline AT_LifeTime( w_ClassRef ptr, bool i_takeOwnership )
>>          : m_ptrVal( ptr )
>>        {
>>            if ( i_takeOwnership ) {
>>                if ( ptr ) {
>>                    w_RefTraits::IncRefCount(ptr);
>>                }
>>            }
>>        }
>
>
> What if w_RefTraits::IncRefCount() throws an exception?
> (If it's managing an externally reference count, it might
> need to allocate one as you've provided no hook for the
> constructor to do that otherwise.  Having said that, I'm not
> at all sure it's possible to do external reference counting
> at all sanely with your design.)
>
>
>
>
>>        template < class w_ClassRefRhs, class w_RefTraitsRhs >
>>        inline AT_LifeTime(
>>            const AT_LifeView< w_ClassRefRhs, w_RefTraitsRhs > & i_ptr
>>  )
>>          : m_ptrVal( i_ptr.m_ptrVal )
>>        {
>>            // Bump the pointee's reference count.
>
> [...]
>
>>        template < class w_ClassRefRhs, class w_RefTraitsRhs >
>>        inline AT_LifeTime(
>>            const AT_LifeLine< w_ClassRefRhs, w_RefTraitsRhs > & i_ptr
>>  )
>>          : m_ptrVal( i_ptr.m_ptrVal )
>>        {}
>
>
> So, if I construct a LifeTime from a LifeView, I increment
> the refcount, but if I constructor a LifeTime from a
> LifeLine, I don't.  And, if I construct a LifeView from
> a LifeLine, I don't increment the RC.  Thus,
>
>   LifeLine ll;
>
>   LifeTime( LifeView(ll) );  // Increments ref count
>   LifeTime( ll );            // Does not increment ref count
>
> Yet, in both cases, the LifeTime's destructor will do the
> same thing.  How can this be right?

LifeTime( ll ); transfers responsibility

LifeTime( LifeView(ll) ); does not

It works.

>
>
>>        inline w_ClassRef * InnerReference()
>>        {
>>            ReleasePointee( 0 );
>>            return & m_ptrVal;
>>        }
>
>
> What's wrong with just assigning the new value into the
> pointer?  If a legacy (e.g. C) function needs a
> pointer-to-pointer, then you can always do

>
>   myLifeTime<T*> ptr;
>   T* tmp = 0;
>   legacy_function( &tmp );
>   ptr = tmp;  // Or whatever the correct way to transfer
>               // ownershipe is.
>
>

 legacy_function( ptr.InnerReference() );

replaces 3 of the lines above.


>
>>        inline w_ClassRef Adopt( const w_ClassRef i_ptr )
>
>
> The conventional name for this method is reset.  This is
> what std::auto_ptr calls it.
>
>
>
>
>> * The AT_LifeView smart pointer is intended to be used in a very
>> * specific situation:  You need to pass an object's smart pointer
>> * to a function so the function can do something with the object.
>
>
> Why not just pass a reference to the underlying type? (Not
> even the pointer, just a plain old reference.)

how would you resolve the ambiguity of

T * ptr = new T;
...
shared_ptr<T> sptr = ptr;

Method( ptr );
...

void Method( T * ptr )
{
 shared_ptr<T> sptr = ptr;
}

Obviously somthing is wrong here. *1


>
>
>> * While this isn't incorrect, it is unnecessary for this
>> * kind of function, and thus adds a performance penalty for no
>> * reason.
>
>
> If you make the mechanism for altering the refence count
> inlineable, then this becomes a trivial penalty.  This is
> what the Boost implementation does.  See other posts in this
> thread for more details.
>

At an inevitable cost.

>
>
>>template < class w_ClassRef, class w_RefTraits >
>>class AT_LifeView
>
>
> I don't see what this class does that a raw pointer or
> reference doesn't do.  If you don't want to transfer
> ownership, this is precisely when you should be using a raw
> pointer or reference.
>

It resolves the issue *1 above.

>
>
>
>> * The AT_Pointer smart pointer works essentially like the auto_ptr
>> * of the Standard Template Library (STL).
>
>
> Well, why not just use std::auto_ptr?  There are a lot of
> subtleties to its design that you've overlooked.  (Consider
> std::auto_ptr<T>::auto_ptr_ref<U> for example.  Do you
> understand why that exists?)

AT_Pointer was somthing I used to work around issues with a std::list
class I was using.  It's been a while since I've looked at it.  I'm not
proposing to discuss this.  As far as I am concerned it's incomplete but
useful in a narrow way.

However, while we're on the topic, does:
 std::list< auto_ptr<T> >

do what I expect ?

>
>> *  Therefore, one should use
>> * an AT_Pointer only if the pointee is not capable of reference
>> * counting.
>
>
> External reference counting allows any type to be reference
> counted.  If your smart pointer framework does not provide
> for external reference counting, it is highly unlikely to be
> considered as a possible alternative to TR1's smart pointer
> framework.
>

Again, I raise the issue of mixing too many concepts in the same class.
  Mixing multiple concepts inevitably reduces the utility of a class
(yes contrary to popular opinion.)

>
>>        inline AT_Pointer( w_ClassRef i_ptr = 0 )
>
>
> Should be explicit.
>
>
>>        inline AT_Pointer(
>>            const AT_Pointer< w_ClassRef > & i_ptr )
>
>
> Should have a non-const RHS.
>
> --
> Richard Smith
>

Thanks.
G

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: richard@ex-parrot.com (Richard Smith)
Date: Fri, 11 Jul 2003 17:52:34 +0000 (UTC)
Raw View
Gianni Mariani wrote:
> Richard Smith wrote:
> > Gianni Mariani wrote:

[...]

> >  1.  If you want it to be considered instead of
> >      boost::shared_ptr for C++0x, then write it in a way
> >      that looks and feels like the rest of the STL.  In
> >      particular, where possible make expressions that work
> >      on std::auto_ptr have the same meaning on your
> >      pointers.
>
> Fair comment, if there is merit, this is mostly cosmetic and can be
> easily done.  The original intent was not for a substitute for
> boost::shared_ptr.

Errr...  At the very beginning of this thread you said "I
would like to see 3 more features in the libraries regarding
smart pointers, in particular reference counting smart
pointers".  Now either you want your classes to replace
the existing proposal based on boost::shared_ptr, or you
want to augment it.  Given that your AT_LifeTime class
serves almost exactly the same purpose as boost::shared_ptr
(except yours uses an intrusive reference count whereas
boost uses an external reference count), I can only assume
you want to replace it.

Irrespective of how you see your suggestion with respect to
the TR1 shared_ptr, the comment about making it feel more
like the STL, and in particular std::auto_ptr, still stands.
If you still think there is merit in your suggestions and
would like to see them considered for the next Standard, you
have to sell it to people ... and this will be hard work.


> >  2.  You seem to be advocating an intrusive shared_ptr
> >      implementation to the exclusion of any possible
> >      externally reference counted implementation.  I've
> >      nothing against and intrusive implementation -- indeed
> >      I think it would be a good thing.  HOWEVER, I wouldn't
> >      want to require all shared pointers to be intrusive.
>
> Are not these 2 different concepts (intrusive vs externally reference
> counted ?).

Yes, they're two different concepts, but semantically they
achieve the same things.  Intrusive reference counting can
be more efficient than external reference counting as it
avoids the overhead of allocating an separate block of
memory to hold the reference count.  (Good allocator
technology can help reduce this difference, though.)  As far
as a user of the shared pointer is considered, he doesn't
care whether or not it's intrusive or extrusive -- this is
purely and implementation detail.  Therefore, I think it is
desireable that if both intrusive and external reference
counting are included, they should seemlessly interoperate
so I can just write

  shared_ptr< foo >;   // foo supports intrusive reference
                       // counting, so this is used

  shared_ptr< bar >;   // bar does not support intrusive
                       // reference counting, so and
                       // external reference count is
                       // allocated


> I have yet to come across the need for an externally reference counted
> facility.  I'll need to consider this further.

Really?  I'm very surprised.  Suppose I have (for what ever
reason) a

  std::map< std::string, std::vector<std::string> >

-- quite an expensive object to be copying around.  Now, how
do I avoid copying it?  Obviously I can pass it by reference
rather than by value, or I perhaps I can heap-allocate
it and pass it by pointer.  If I do this, how do I handle
its lifetime?  Reference counting is the obvious way, but
std::map is not derived from AT_LifeControl, so I can't use
it with your smart pointer.  With boost::shared_ptr, this is
trivial: I just write

  boost::shared_ptr< std::map< ... > >

and an external refernce count is allocated and handled for
me.  With your scheme, my only option is to create a wrapper
class aggregating the map and deriving from AT_LifeControl,
and then I have the problem of forwarding all the functions
in the interface.

> >
> >  3.  Your AT_LifeLine class is attempting to solve the same
> >      problem as std::auto_ptr, and in the same way.  You'd
> >      be better off looking at std::auto_ptr, learning
> >      thoroughly how it works (it is decidedly non-trivial)
> >      and thinking about how it might need to be changed (if
> >      at all) in the presence of additional standardised
> >      smart pointers.
>
> Yes, perhaps.  I have yet to find a case where auto_ptr was satisfactory
> and hence I overlooked it.  Need to reconsider since it's been a while
> since I gave up on it (auto_ptr that is).
>

> >  4.  Your smart pointer classes have a great many implicit
> >      conversions to and from raw pointers.
[...]
> the model requies that no raw pointers are used in the code except
> from a new, or when a pointer is "transferred" which admitedly is a
> hang-over from a while back.  That could go.

Good.  If you need to access a raw pointer (and from time to
time you will), provide a get() method to do it.

[...]
> Since I shy away from
> using exceptions due to the significant complexity

What "significant complexity"?  Exceptions are a damn sight
easier to program with than C-style error codes, and once
your used to RAII, writing exception safe code is easy.

> I'm not as concerned about the assertion as I am with the resource leak.
>   The assertion is there to catch the very errors it asserts for.

But it (the assertion) also creates the error.  Consider:
somewhere an exception gets thrown -- this means "something
has gone wrong, but I think it quite possible that someone
higher up the stack can recover from it".  However, if the
exception is thrown between the AT_LifeLine being created
and it transferring ownership,

> >  6.  Your AT_LifeTime class is basically the same as
> >      boost::shared_ptr, other than it uses an intrusive
> >      reference count.  Can you summarise exactly what you
> >      think this class offers that boost::shared_ptr does
> >      not.
>
> I can.

Well, will you then, please?

> >  8.  The AT_LifeView class doesn't (so far as I can see)
> >      provide any functionality that a raw pointer or
> >      reference doesn't provide.  Therefore I don't see any
> >      point to it.
>
> Except that the semantics of a raw pointer are ambiguous while that of a
> LifeView is not.

There's no ambiguity with a reference.  If you want to avoid
the overhead of reference counting on a particular function
call, you can do one of two things:

  class foo {};

  void fn1( const foo& );
  void fn2( const boost::shared_ptr<foo>& );

  int main() {
    boost::shared_ptr< foo > f( new foo );
    fn1(*f);
    fn2(*f);
  }

There's no doubt about fn1:  no transfer of owenship is ever
intended.  The call to fn2 also avoids the overhead of
reference counting, but allows fn2 to have ownership if it's
necessary.

> >  9.  AT_Pointer seems to be another attempt to reimplement
> >      std::auto_ptr.  Why bother re-implementing it?  Your
> >      version doesn't work as well as the standard one.
>
> AT_Pointer happens to be in the same file.  AT_Pointer works like so:
>
> std::list< AT_Pointer< foo * > >
>
> auto_ptr does not.

No, there is no guarantee that your AT_Pointer will work
correctly like this.  Look again at it's copy constructor:

  inline AT_Pointer(
      const AT_Pointer< w_ClassRef > & i_ptr )
    : m_ptrVal( i_ptr.m_ptrVal )
  {
      // Zero out the pointee address in the AT_Pointer
      // being copied, so that it is impossible to delete
      // the pointee twice accidentally.
      // ------------------------------------------------
      i_ptr.m_ptrVal = 0;
  }

Ownership is transfered from the RHS to the LHS, and the LHS
pointer is nullified.  This is precisely the same semantics
that std::auto_ptr has, and exactly the issue that prevents
std::auto_ptr from being used in an STL container.  In
particular, AT_Pointer does not model the CopyConstructible
and Assignable concept defined in 20.1.3.  According to
23.1/3, "the type of objects stored in these components must
meet the requirement of CopyConstructible".  Therefore you
are not allowed to put AT_Pointers in std::lists.

> > You're now advocating an intrusive implementation, are you?
> > This is not a bad idea, as long as an externally reference
> > counted implementation is also possible, ideally without
> > exposing the difference to end user.
>
> I need to see why you would want to mix these concepts.

I don't see that a intrusive implementation alone can
provide a complete solution, whereas I think a external
implementation by itself could.  (See my example, above,
using std::map for reasons.)

> What would be the purpose of designing a class that required external
> reference counting ?  If this is an issue primarily of legacy
> compatability, then I have still not run into this issue.
>
> I'd need to see what real-life problems are being solved with external
> reference counting.

Not legacy code.  See above.

> > Can you write me such a policy class that handles external
> > reference counting?  What are the function arguments to
> > IncRefCount?  And how is the reference count accessed from
> > them?
>
> I suspect so.

Well, could you show me one, please?  I don't see how it can
feasibly be handled in your framework (short of having a
global map from pointer to ref count, or something equally
ugly).  Demonstrate to me that I'm wrong.

[About class names:]
> Seriously, I don't care.

Well I do, and you just have to take a look at the boost
mailing list to see that lots of highly respected developers
will spend significant amounts of time finding well-chosen
names for classes.

> > So, w_ClassRef is supposed to be a pointer type.  I.e.
> > completely at odds to almost all existing smart pointer
> > implementions where the first argument is the pointee.  Thus
> > you want to write AT_LifeLine<T*> rather than shared_ptr<T>.
>
> yes.
>
> or
>
> typedef X * Xp;
>
> AT_Lifetime<Xp>

I think your missing the point:  this is not the interface
that other smart pointer classes use, and I can see no
advantage to doing it differently.

> >  *  The RHS is being semantically modified by the
> >     constructor, and therefore should be taken by
> >     non-const reference.
>
> hmm, ok, you've hit a point that I glossed over during implementation.
> The reference count is somewhat separate from the rest of the class
> implementation.  Hence the reference count is mutable.

The point is not whether the argument needs to be a
non-const reference to compile, but whether it semantically
should be non-const.  Clearly, the copy constructor changes
the observable state of the source smart pointer.
(Initially it is a useable smart pointer, afterwards,
ownership has been transferred away and the pointer is
effectively unusable.)  As it changes the state of the
source object, it should take the source by non-const
reference.  The fact that this allows you to remove the
'mutable' keyword is incidental.

> >>        inline ~AT_LifeLine() {}
> >
> > What happens when an exception results in the pointer being
> > destroyed before it has transfered ownership away?  Like
> > this it leaks resources, or with your debugging hooks, will
> > assert.  This is a very bad.
>
> The alternative may also be "bad".

Why?  Why should it be "bad" to call DecRefCount on
destruction if the ownership has not been transfered away?
Can you give me a concrete example of how this might cause
problems?

> > I don't think you want an operator= that has a raw pointer
> > on its RHS.  And I note that it doesn't take ownership of
> > the pointer.
>
>  AT_LifeLine<T*> p;
>
>  ...
>
>  p = new T;

That's fine, but the problem is in accidental assignment
from a pointer where you should not take ownership.  Having
a method to do this, e.g.

  p.reset( new T );

makes this more explicit.

> > It's normal to supply an operator*() as well.
> >
>
> This would provide a raw pointer ?

No, a raw reference.  This makes the following work:

  void function( const T& );

  AP_LifeLine<T*> ll;

  function( *ll );


> > Eurgh.  Implicit conversions to the raw pointer type.  Have
> > you considered how easy it would be to implicitly convert it
> > to a raw pointer and thus destroy the smart pointer,
> > probably causing a resource leak.  (Unless the destructor
> > asserts, which doesn't seem much better.)
> >
>
> No.  In the intended use, this would never happen.

Well, don't provide the conversion operators then.  If
they're not needed in the intended use, and are unsafe,
get rid of them.

> >>        inline w_ClassRef * InnerReference()
> >
> > What's wrong with just assigning the new value into the
> > pointer?
> >
> >   myLifeTime<T*> ptr;
> >   T* tmp = 0;
> >   legacy_function( &tmp );
> >   ptr = tmp;  // Or whatever the correct way to transfer
> >               // ownershipe is.
>
>  legacy_function( ptr.InnerReference() );
>
> replaces 3 of the lines above.

The point is that legacy code will attach many different
semantics to a T**, so there is no one correct answer.  For
example, why do you release and nullify the original
pointer?  Some applications might want T** to point to a
valid pointer which they possibly update.  And what about
legacy code that tries to create the object using the wrong
heap (e.g. using malloc rather than new)?

Furthermore, there's no reason why this needs to be a member
function -- as I've shown above, everything can be done
using the public interface.  If your legacy libraries attach
particular semantics to T** pointers, then, fine provide
your own helper function, but don't make it part of the
smart pointer's interface.

> >> * The AT_LifeView smart pointer is intended to be used in a very
> >> * specific situation:  You need to pass an object's smart pointer
> >> * to a function so the function can do something with the object.
> >
> >
> > Why not just pass a reference to the underlying type? (Not
> > even the pointer, just a plain old reference.)
>
> how would you resolve the ambiguity of
>
> T * ptr = new T;
> ....
> shared_ptr<T> sptr = ptr;
>
> Method( ptr );
> ....
>
> void Method( T * ptr )
> {
>  shared_ptr<T> sptr = ptr;
> }
>
> Obviously somthing is wrong here. *1

For a start, with boost::shared_ptr this won't compile.
Boost's shared_ptr does not allow you to write

  shared_ptr<T> sptr = ptr;

instead you must either write

  shared_ptr<T> sptr( ptr );

or call reset.

Secondly, you'd be much better off passing T by reference
rather that by pointer.  This would have avoided the
problem.

Thirdly, it's a bloody stupid thing to do.  You should
never, ever pass anything to a boost::shared_ptr other than
the immediate return value from a new expression.
Following this and a few other simple rules avoids any
problems.

I repeat what I said in my first post to this thread:
aside from problems with cyclic ownership, the majority of
problems I've had using boost::shared_ptr have been due to
compiler bugs.  It really is a very simple class to use
correctly.


[Regarding spurious increments / decrements to the ref
count:]

> > If you make the mechanism for altering the refence count
> > inlineable, then this becomes a trivial penalty.  This is
> > what the Boost implementation does.  See other posts in this
> > thread for more details.
>
> At an inevitable cost.

Insignificant.  How many instructions do you think it takes
to do ++*ptr->refcount when inlined?

And you still haven't what's wrong with:

  void foo( const boost::shared_ptr<T>& )

This doesn't modify the refcount at all.

> >>template < class w_ClassRef, class w_RefTraits >
> >>class AT_LifeView
> >
> >
> > I don't see what this class does that a raw pointer or
> > reference doesn't do.  If you don't want to transfer
> > ownership, this is precisely when you should be using a raw
> > pointer or reference.
> >
>
> It resolves the issue *1 above.

Explain how issue 1, above, applies when you pass the raw
object by reference, e.g.

 void foo( const T& );
 boost::shared_ptr<T> t;
 foo( *t );

> AT_Pointer was somthing I used to work around issues with a std::list
> class I was using.

It doesn't work around the problems at all.  You may have
not have noticed them, and it's possible that your
particular STL's std::list broadly works with your class,
but it isn't portable and probably doesn't even work
correctly in all cases in your STL.

> As far as I am concerned it's incomplete but
> useful in a narrow way.

In what way is incomplete?

> However, while we're on the topic, does:
>  std::list< auto_ptr<T> >
>
> do what I expect ?

It does exactly the same as

  std::list< AT_Pointer<T> >

i.e. it invokes undefined behaviour.  Don't do either.

> Again, I raise the issue of mixing too many concepts in the same class.

What I'm trying to point out, is that the problem that
you say AT_Pointer is there to solve -- namely that not all
classes can be reference counted -- is only an issue because
you've chosen to ignore external reference counting.  I
really can't see a general smart pointer library being
accepted into the Standard without support for external
reference counting.  As your stated aim is to get this
accepted into the Standard, you MUST consider external
reference counting.

--
Richard Smith

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dave@boost-consulting.com (David Abrahams)
Date: Fri, 11 Jul 2003 17:52:50 +0000 (UTC)
Raw View
richard@ex-parrot.com (Richard Smith) writes:

> I've made some detailed comments on your suggestion, below.
> For those who don't want to wade through them, here's a
> summary:

Wow, Richard, that's some pretty detailed commentary!  I hope you will
participate in the next Boost library review.  You could make a great
contribution!

-Dave

P.S. Nice email address.  A Python fan, perhaps?

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Gianni Mariani <gi2nospam@mariani.ws>
Date: 12 Jul 2003 05:30:01 GMT
Raw View
Richard Smith wrote:
> Gianni Mariani wrote:
>
>>Richard Smith wrote:
>>
>>>Gianni Mariani wrote:
>
>
> [...]
>
>
>>> 1.  If you want it to be considered instead of
>>>     boost::shared_ptr for C++0x, then write it in a way
>>>     that looks and feels like the rest of the STL.  In
>>>     particular, where possible make expressions that work
>>>     on std::auto_ptr have the same meaning on your
>>>     pointers.
>>
>>Fair comment, if there is merit, this is mostly cosmetic and can be
>>easily done.  The original intent was not for a substitute for
>>boost::shared_ptr.
>
>
> Errr...  At the very beginning of this thread you said "I
> would like to see 3 more features in the libraries regarding
> smart pointers, in particular reference counting smart
> pointers".  Now either you want your classes to replace
> the existing proposal based on boost::shared_ptr, or you
> want to augment it.  Given that your AT_LifeTime class
> serves almost exactly the same purpose as boost::shared_ptr
> (except yours uses an intrusive reference count whereas
> boost uses an external reference count), I can only assume
> you want to replace it.
>

You've made a significant argument regarding the exception safety of
AT_LifeLine and hence I need to reconsider my proposal.

The right answer here on this depends on a compromise of the following
requirements:

a) "zero overhead" principle
b) exception safety
c) correctness - or: elimination of usage errors - or : make is possible
for the compiler to pick up usage errors.
(and probably more)

Depending on which requirement I give priority I get different answers
to "what is right".

I tend to place the "zero overhead" principle above all others which is
NOT the right answer for everyone or all situations obviously but
sometimes it is.  Hence when it is the overriding principle, using a
solution that does not provide the most efficient solution can be a
significant problem.  Also, smart pointers, or rather object lifetime
management, is such a fundamental issue for C++ that it will become
pervasive throughout any code base and hence will become a topic of many
frustrations to come.  To cap this paradox off, very few programmers
will grasp the true complexity of smart pointers (as I have already
witnessed).  A typical formula for intractible issues.

So, my intention here is to open the discussion on altenatives and
discuss their merits.

The argument becomes far more complex than just smart pointers when you
start considering GC in the picture.  GC schemes can be quite successful
and require very little in the way of "smart pointer" support.  However
the GC behaviour can make it difficult to succeed with real-time
applications or can still lead to leaks of resources through errors in
programming so it is not a panacea.  However if there exists an easy
method of life-time management (GC) why would you want to provide a less
than absolute optimal solution for smart pointers.

Anyhow, I have no point to press here other than I am still considering
all the points that have been made.  There are 3 options I see moving
forward.

a) abandon AT_Life* and hence eliminate the issues with LifeLine
b) augment shared_ptr with LifeLine and LifeView lookalikes and include
a template parameter to enable it for legacy apps.
c) have 2 separate smart pointer systems, one meant for optimizing speed
  at the cost of potential correctness issues and shared_ptr remains for
optimizing "correctness".

.... fourth option is to explode in disgust.


> Irrespective of how you see your suggestion with respect to
> the TR1 shared_ptr, the comment about making it feel more
> like the STL, and in particular std::auto_ptr, still stands.
> If you still think there is merit in your suggestions and
> would like to see them considered for the next Standard, you
> have to sell it to people ... and this will be hard work.
>

I'll need help on the "feel" thing.

If this is more than aesthetics, please explain, otherwise my prior
comments on this topic apply.

>
>
>>> 2.  You seem to be advocating an intrusive shared_ptr
>>>     implementation to the exclusion of any possible
>>>     externally reference counted implementation.  I've
>>>     nothing against and intrusive implementation -- indeed
>>>     I think it would be a good thing.  HOWEVER, I wouldn't
>>>     want to require all shared pointers to be intrusive.
>>
>>Are not these 2 different concepts (intrusive vs externally reference
>>counted ?).
>
>
> Yes, they're two different concepts, but semantically they
> achieve the same things.  Intrusive reference counting can
> be more efficient than external reference counting as it
> avoids the overhead of allocating an separate block of
> memory to hold the reference count.  (Good allocator
> technology can help reduce this difference, though.)  As far
> as a user of the shared pointer is considered, he doesn't
> care whether or not it's intrusive or extrusive -- this is
> purely and implementation detail.  Therefore, I think it is
> desireable that if both intrusive and external reference
> counting are included, they should seemlessly interoperate
> so I can just write
>
>   shared_ptr< foo >;   // foo supports intrusive reference
>                        // counting, so this is used
>
>   shared_ptr< bar >;   // bar does not support intrusive
>                        // reference counting, so and
>                        // external reference count is
>                        // allocated
>

If shared_ptr had a second template arg that allowed you to do this:

shared_ptr< bar, iunknown_shared_ptr_helper >

  .... now shared_ptr would be able to use bar's legacy reference
counting sematics, it would satisfy one of my issues regarding shared_ptr.


>
>
>>I have yet to come across the need for an externally reference counted
>>facility.  I'll need to consider this further.
>
>
> Really?  I'm very surprised.  Suppose I have (for what ever
> reason) a
>
>   std::map< std::string, std::vector<std::string> >
>
> -- quite an expensive object to be copying around.  Now, how
> do I avoid copying it?  Obviously I can pass it by reference
> rather than by value, or I perhaps I can heap-allocate
> it and pass it by pointer.  If I do this, how do I handle
> its lifetime?  Reference counting is the obvious way, but
> std::map is not derived from AT_LifeControl, so I can't use
> it with your smart pointer.  With boost::shared_ptr, this is
> trivial: I just write
>
>   boost::shared_ptr< std::map< ... > >
>
> and an external refernce count is allocated and handled for
> me.  With your scheme, my only option is to create a wrapper
> class aggregating the map and deriving from AT_LifeControl,
> and then I have the problem of forwarding all the functions
> in the interface.
>

That map class is somthing I would allways put in another class.  One of
my design principles is that encapsulating is a low (very low) threshold
act.  I would almost never create an object like the one above whose
lifetime was not managed by class that contained it.

I could/would also argue this to be "implementation" and hence not
worthy of being passed around and managed by a smart pointer. It would
also be unlikely that I would expose such a member as a part of the
"interface".

This is may be why I never seem to have found it nessasary to use
"extrusive" RC.

>
>>> 3.  Your AT_LifeLine class is attempting to solve the same
>>>     problem as std::auto_ptr, and in the same way.  You'd
>>>     be better off looking at std::auto_ptr, learning
>>>     thoroughly how it works (it is decidedly non-trivial)
>>>     and thinking about how it might need to be changed (if
>>>     at all) in the presence of additional standardised
>>>     smart pointers.
>>
>>Yes, perhaps.  I have yet to find a case where auto_ptr was satisfactory
>>and hence I overlooked it.  Need to reconsider since it's been a while
>>since I gave up on it (auto_ptr that is).
>>
>
>
>>> 4.  Your smart pointer classes have a great many implicit
>>>     conversions to and from raw pointers.
>
> [...]
>
>>the model requies that no raw pointers are used in the code except
>>from a new, or when a pointer is "transferred" which admitedly is a
>>hang-over from a while back.  That could go.
>
>
> Good.  If you need to access a raw pointer (and from time to
> time you will), provide a get() method to do it.
>
> [...]
>
>>Since I shy away from
>>using exceptions due to the significant complexity
>
>
> What "significant complexity"?  Exceptions are a damn sight
> easier to program with than C-style error codes, and once
> your used to RAII, writing exception safe code is easy.
>

Treating errors as exceptions seems to hide too much for less
experienced programmers to truly understand what is going on.

Treating errors as a "normal" operation tends to force programmers to
produce code that deals with errors more appropriatly.

While I say this, I advocate the idea of exception safety mainly because
that means RAII is adopted and hence a "return" or a "break" placed in
the code means that things clean up correctly which leads to fewer
errors during the maintenace phase.

"Exceptions may be easier" does not mean necassarily better simply
because considerations regarding the consequences of errors can easily
be (and regularly are) overlooked.

For the purposes of this discussion however, our differences in opinion
regarding exceptions are irrelevant.

>
>>I'm not as concerned about the assertion as I am with the resource leak.
>>  The assertion is there to catch the very errors it asserts for.
>
>
> But it (the assertion) also creates the error.  Consider:
> somewhere an exception gets thrown -- this means "something
> has gone wrong, but I think it quite possible that someone
> higher up the stack can recover from it".  However, if the
> exception is thrown between the AT_LifeLine being created
> and it transferring ownership,


Yes, however in practice, the assertion is thrown if the code has an
error.  Correcting the error is trivial to the point where the error is
no longer able to occur and hence the check is no longer needed which is
why AT_LifeLine exists.  While it is a class with a personality it is
more aptly a description of policy or programmer intention.

>
>
>>> 6.  Your AT_LifeTime class is basically the same as
>>>     boost::shared_ptr, other than it uses an intrusive
>>>     reference count.  Can you summarise exactly what you
>>>     think this class offers that boost::shared_ptr does
>>>     not.
>>
>>I can.
>
>
> Well, will you then, please?
>

a) Support for any intrusive reference counting interface
b) Highly optimal implementation (at the expense of exception safety and
increased complexity)

Not implemented in the AT_Life* I posted is a 3rd feature.  Support for
a thread safe AT_LifeTime (using a 3rd template parameter).

>
>>> 8.  The AT_LifeView class doesn't (so far as I can see)
>>>     provide any functionality that a raw pointer or
>>>     reference doesn't provide.  Therefore I don't see any
>>>     point to it.
>>
>>Except that the semantics of a raw pointer are ambiguous while that of a
>>LifeView is not.
>
>
> There's no ambiguity with a reference.  If you want to avoid
> the overhead of reference counting on a particular function
> call, you can do one of two things:
>
>   class foo {};
>
>   void fn1( const foo& );
>   void fn2( const boost::shared_ptr<foo>& );
>
>   int main() {
>     boost::shared_ptr< foo > f( new foo );
>     fn1(*f);
>     fn2(*f);
>   }
>
> There's no doubt about fn1:  no transfer of owenship is ever
> intended.  The call to fn2 also avoids the overhead of
> reference counting, but allows fn2 to have ownership if it's
> necessary.

I think this is where we have a fundamental disagreement.  I can cite
examples where I would like to do the contrary.  Notion of constness
does not indicate a notion of lifetime.  These are 2 separate concepts
and should not be mixed.

>
>
>>> 9.  AT_Pointer seems to be another attempt to reimplement
>>>     std::auto_ptr.  Why bother re-implementing it?  Your
>>>     version doesn't work as well as the standard one.
>>
>>AT_Pointer happens to be in the same file.  AT_Pointer works like so:
>>
>>std::list< AT_Pointer< foo * > >
>>
>>auto_ptr does not.
>
>
> No, there is no guarantee that your AT_Pointer will work
> correctly like this.  Look again at it's copy constructor:
>
>   inline AT_Pointer(
>       const AT_Pointer< w_ClassRef > & i_ptr )
>     : m_ptrVal( i_ptr.m_ptrVal )
>   {
>       // Zero out the pointee address in the AT_Pointer
>       // being copied, so that it is impossible to delete
>       // the pointee twice accidentally.
>       // ------------------------------------------------
>       i_ptr.m_ptrVal = 0;
>   }
>
> Ownership is transfered from the RHS to the LHS, and the LHS
> pointer is nullified.  This is precisely the same semantics
> that std::auto_ptr has, and exactly the issue that prevents
> std::auto_ptr from being used in an STL container.  In
> particular, AT_Pointer does not model the CopyConstructible
> and Assignable concept defined in 20.1.3.  According to
> 23.1/3, "the type of objects stored in these components must
> meet the requirement of CopyConstructible".  Therefore you
> are not allowed to put AT_Pointers in std::lists.

You are right, but it works and will likely continue working correctly
and there would be serious implications for it to no longer work.

However, for this reason alone I would not advocate adding such a
facility into C++ but it would be higly unlikely that in the narrow
scope in which it is used it will not work.

Again, AT_Pointer is here inadvertently, it happens to be in the same
file.  If you wish to discuss the merits of AT_Pointer we'll never
finish.  I have also implemented an alternative to std:list that is
guarenteed to work correctly with AT_Pointer.

>
>
>>>You're now advocating an intrusive implementation, are you?
>>>This is not a bad idea, as long as an externally reference
>>>counted implementation is also possible, ideally without
>>>exposing the difference to end user.
>>
>>I need to see why you would want to mix these concepts.
>
>
> I don't see that a intrusive implementation alone can
> provide a complete solution, whereas I think a external
> implementation by itself could.  (See my example, above,
> using std::map for reasons.)

In my experience, I find very few examples where intrusive RC is needed
and hence I would find it a waste of resources.

>
>
>>What would be the purpose of designing a class that required external
>>reference counting ?  If this is an issue primarily of legacy
>>compatability, then I have still not run into this issue.
>>
>>I'd need to see what real-life problems are being solved with external
>>reference counting.
>
>
> Not legacy code.  See above.

covered above.

>
>
>>>Can you write me such a policy class that handles external
>>>reference counting?  What are the function arguments to
>>>IncRefCount?  And how is the reference count accessed from
>>>them?
>>
>>I suspect so.
>
>
> Well, could you show me one, please?  I don't see how it can
> feasibly be handled in your framework (short of having a
> global map from pointer to ref count, or something equally
> ugly).  Demonstrate to me that I'm wrong.
>

I'll need to do this later.

> [About class names:]
>
>>Seriously, I don't care.
>
>
> Well I do, and you just have to take a look at the boost
> mailing list to see that lots of highly respected developers
> will spend significant amounts of time finding well-chosen
> names for classes.

Yes, you are quite correct.  This should be an alarm for you where to
spend your time.

You're welcome to suggest new names.

>
>
>>>So, w_ClassRef is supposed to be a pointer type.  I.e.
>>>completely at odds to almost all existing smart pointer
>>>implementions where the first argument is the pointee.  Thus
>>>you want to write AT_LifeLine<T*> rather than shared_ptr<T>.
>>
>>yes.
>>
>>or
>>
>>typedef X * Xp;
>>
>>AT_Lifetime<Xp>
>
>
> I think your missing the point:  this is not the interface
> that other smart pointer classes use, and I can see no
> advantage to doing it differently.

Create a shared_ptr to an Xp.

>
>
>>> *  The RHS is being semantically modified by the
>>>    constructor, and therefore should be taken by
>>>    non-const reference.
>>
>>hmm, ok, you've hit a point that I glossed over during implementation.
>>The reference count is somewhat separate from the rest of the class
>>implementation.  Hence the reference count is mutable.
>
>
> The point is not whether the argument needs to be a
> non-const reference to compile, but whether it semantically
> should be non-const.  Clearly, the copy constructor changes
> the observable state of the source smart pointer.
> (Initially it is a useable smart pointer, afterwards,
> ownership has been transferred away and the pointer is
> effectively unusable.)  As it changes the state of the
> source object, it should take the source by non-const
> reference.  The fact that this allows you to remove the
> 'mutable' keyword is incidental.
>

Well, this is a fundamentally different approach than that of LifeTime
as I described earlier.  Reference counting controls the life of the
object.  Constness controls the modifyability of the object.  I would
think that somthing that is not allowed to be modified should still be
able te be referenced ?

Still, I think this has little bearing on

>
>>>>       inline ~AT_LifeLine() {}
>>>
>>>What happens when an exception results in the pointer being
>>>destroyed before it has transfered ownership away?  Like
>>>this it leaks resources, or with your debugging hooks, will
>>>assert.  This is a very bad.
>>
>>The alternative may also be "bad".
>
>
> Why?  Why should it be "bad" to call DecRefCount on
> destruction if the ownership has not been transfered away?
> Can you give me a concrete example of how this might cause
> problems?
>

Because I'd rather do nothing.

>
>>>I don't think you want an operator= that has a raw pointer
>>>on its RHS.  And I note that it doesn't take ownership of
>>>the pointer.
>>
>> AT_LifeLine<T*> p;
>>
>> ...
>>
>> p = new T;
>
>
> That's fine, but the problem is in accidental assignment
> from a pointer where you should not take ownership.  Having
> a method to do this, e.g.
>
>   p.reset( new T );
>
> makes this more explicit.

I might steal this idea.  Thanks.

>
>
>>>It's normal to supply an operator*() as well.
>>>
>>
>>This would provide a raw pointer ?
>
>
> No, a raw reference.  This makes the following work:
>
>   void function( const T& );
>
>   AP_LifeLine<T*> ll;
>
>   function( *ll );
>

OK.

>
>
>>>Eurgh.  Implicit conversions to the raw pointer type.  Have
>>>you considered how easy it would be to implicitly convert it
>>>to a raw pointer and thus destroy the smart pointer,
>>>probably causing a resource leak.  (Unless the destructor
>>>asserts, which doesn't seem much better.)
>>>
>>
>>No.  In the intended use, this would never happen.
>
>
> Well, don't provide the conversion operators then.  If
> they're not needed in the intended use, and are unsafe,
> get rid of them.
>

agreed.

>
>>>>       inline w_ClassRef * InnerReference()
>>>
>>>What's wrong with just assigning the new value into the
>>>pointer?
>>>
>>>  myLifeTime<T*> ptr;
>>>  T* tmp = 0;
>>>  legacy_function( &tmp );
>>>  ptr = tmp;  // Or whatever the correct way to transfer
>>>              // ownershipe is.
>>
>> legacy_function( ptr.InnerReference() );
>>
>>replaces 3 of the lines above.
>
>
> The point is that legacy code will attach many different
> semantics to a T**, so there is no one correct answer.  For
> example, why do you release and nullify the original
> pointer?  Some applications might want T** to point to a
> valid pointer which they possibly update.

The intended use is very simply for the creation of a new object.
The semantics are very narrow but very common in COM.  I can see this
being a helper friend class/function instead of a member method to make
it appear more separate.  I've also used this in an ACE contruction
call.  It happens frequenly.

   And what about
> legacy code that tries to create the object using the wrong
> heap (e.g. using malloc rather than new)?

And what about that ?

It's up to our interface to make sure the right thing happens.

Am I missing somthing ?

>
> Furthermore, there's no reason why this needs to be a member
> function -- as I've shown above, everything can be done
> using the public interface.  If your legacy libraries attach
> particular semantics to T** pointers, then, fine provide
> your own helper function, but don't make it part of the
> smart pointer's interface.
>

OK.

>
>>>>* The AT_LifeView smart pointer is intended to be used in a very
>>>>* specific situation:  You need to pass an object's smart pointer
>>>>* to a function so the function can do something with the object.
>>>
>>>
>>>Why not just pass a reference to the underlying type? (Not
>>>even the pointer, just a plain old reference.)
>>
>>how would you resolve the ambiguity of
>>
>>T * ptr = new T;
>>....
>>shared_ptr<T> sptr = ptr;
>>
>>Method( ptr );
>>....
>>
>>void Method( T * ptr )
>>{
>> shared_ptr<T> sptr = ptr;
>>}
>>
>>Obviously somthing is wrong here. *1
>
>
> For a start, with boost::shared_ptr this won't compile.
> Boost's shared_ptr does not allow you to write
>
>   shared_ptr<T> sptr = ptr;
>
> instead you must either write
>
>   shared_ptr<T> sptr( ptr );
>
> or call reset.
>
> Secondly, you'd be much better off passing T by reference
> rather that by pointer.  This would have avoided the
> problem.

Except that null is a valid T* and that null is not a valid T&.

>
> Thirdly, it's a bloody stupid thing to do.  You should
> never, ever pass anything to a boost::shared_ptr other than
> the immediate return value from a new expression.
> Following this and a few other simple rules avoids any
> problems.

You're probably right and they should be removed but I need to go
through and figure out where I had problems and see if they are now
resolved with the other classes (View and Line).

>
> I repeat what I said in my first post to this thread:
> aside from problems with cyclic ownership, the majority of
> problems I've had using boost::shared_ptr have been due to
> compiler bugs.  It really is a very simple class to use
> correctly.
>
>
> [Regarding spurious increments / decrements to the ref
> count:]
>
>
>>>If you make the mechanism for altering the refence count
>>>inlineable, then this becomes a trivial penalty.  This is
>>>what the Boost implementation does.  See other posts in this
>>>thread for more details.
>>
>>At an inevitable cost.
>
>
> Insignificant.  How many instructions do you think it takes
> to do ++*ptr->refcount when inlined?

If you can inline the increment AND decrement, none.  But inlining is
not what I'm concerned about.

There is no question that AT_Life* will allow the compiler to make more
significant optimizations when you cannot inline and that they can be
used to manage virtually any type of intrusive reference counted class.

If shared_ptr could do these things then I would walk away.

>
> And you still haven't what's wrong with:
>
>   void foo( const boost::shared_ptr<T>& )
>
> This doesn't modify the refcount at all.

Another level of indirection ?

>
>
>>>>template < class w_ClassRef, class w_RefTraits >
>>>>class AT_LifeView
>>>
>>>
>>>I don't see what this class does that a raw pointer or
>>>reference doesn't do.  If you don't want to transfer
>>>ownership, this is precisely when you should be using a raw
>>>pointer or reference.
>>>
>>
>>It resolves the issue *1 above.
>
>
> Explain how issue 1, above, applies when you pass the raw
> object by reference, e.g.
>
>  void foo( const T& );
>  boost::shared_ptr<T> t;
>  foo( *t );

Don't do that.

>
>
>>AT_Pointer was somthing I used to work around issues with a std::list
>>class I was using.
>
>
> It doesn't work around the problems at all.  You may have
> not have noticed them, and it's possible that your
> particular STL's std::list broadly works with your class,
> but it isn't portable and probably doesn't even work
> correctly in all cases in your STL.
>

correct.

>
>>As far as I am concerned it's incomplete but
>>useful in a narrow way.
>
>
> In what way is incomplete?

I don't know.  I use it in very limited ways so it's complete enough for
what I use it for.  I mostly use AT_Life*.

>
>
>>However, while we're on the topic, does:
>> std::list< auto_ptr<T> >
>>
>>do what I expect ?
>
>
> It does exactly the same as
>
>   std::list< AT_Pointer<T> >

hmm, except I couldn't get std::list< auto_ptr<T> > to do what I expected.

>
> i.e. it invokes undefined behaviour.  Don't do either.

It can and does have defined behaviour in a narrow way and since you
know alot about what you're talking about, you can figure it out.

>
>
>>Again, I raise the issue of mixing too many concepts in the same class.
>
>
> What I'm trying to point out, is that the problem that
> you say AT_Pointer is there to solve -- namely that not all
> classes can be reference counted -- is only an issue because
> you've chosen to ignore external reference counting.  I
> really can't see a general smart pointer library being
> accepted into the Standard without support for external
> reference counting.  As your stated aim is to get this
> accepted into the Standard, you MUST consider external
> reference counting.
>

My use of AT_Pointer is really not the topic of discssion.  I certainly
do not advocate using AT_Pointer in the way I have used it.  Having said
that, if you have a very narrow problem to solve it can be done.  I have
found that it is the least used smart pointer by far because most things
are done with "intrusive" reference counting.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: richard@ex-parrot.com (Richard Smith)
Date: Thu, 10 Jul 2003 16:21:54 +0000 (UTC)
Raw View
Gianni Mariani wrote:

> Howard Hinnant wrote:
> >
> > My day is simply too full to drop everything everytime somebody says:
> > I've got a better idea, want to know more?
>
> Hint of arrogance I hear ?

I doubt it; more likely a statement of fact.

> I have attached the header that defines the templates.  If you're
> interested I'll make an alpha release of the entire library soon as it
> stands soon.

Could I draw your attention to Howard Hinnant's comment "it
would be to your advantage to compare and contrast with it
(with code examples!) in order to shorten your reader's
learning curve".  I think you would have been wise to have
followed it.  I suspect you'll find relatively few people
will be prepared to wade through a 90k post in order to
separate the germane differences from the TR1 shared_ptr
from differences in style or syntax.

I've made some detailed comments on your suggestion, below.
For those who don't want to wade through them, here's a
summary:

 1.  If you want it to be considered instead of
     boost::shared_ptr for C++0x, then write it in a way
     that looks and feels like the rest of the STL.  In
     particular, where possible make expressions that work
     on std::auto_ptr have the same meaning on your
     pointers.

 2.  You seem to be advocating an intrusive shared_ptr
     implementation to the exclusion of any possible
     externally reference counted implementation.  I've
     nothing against and intrusive implementation -- indeed
     I think it would be a good thing.  HOWEVER, I wouldn't
     want to require all shared pointers to be intrusive.

 3.  Your AT_LifeLine class is attempting to solve the same
     problem as std::auto_ptr, and in the same way.  You'd
     be better off looking at std::auto_ptr, learning
     thoroughly how it works (it is decidedly non-trivial)
     and thinking about how it might need to be changed (if
     at all) in the presence of additional standardised
     smart pointers.

 4.  Your smart pointer classes have a great many implicit
     conversions to and from raw pointers.  This is
     dangerous: even though an intrusive implementation
     allows smart pointers to be converted to and from raw
     pointers, it is very easy to get resource leaks using
     this.

 5.  The idea of using optional run-time assertions to catch
     usage errors is in principle good, but in this case
     leads to poor exception safety.  Consider what happens
     when you construct a AT_LifeLine (your std::auto_ptr
     clone) and an exception causes it to be destroyed
     before relinquishing owenership.  Depending on whether
     or not you have debugging turned on, you'll either get
     an assertion or a resource leakk, either of which are
     bad.

 6.  Your AT_LifeTime class is basically the same as
     boost::shared_ptr, other than it uses an intrusive
     reference count.  Can you summarise exactly what you
     think this class offers that boost::shared_ptr does
     not.  In particular, can we see example usages of both,
     and, if your arguments are based on efficiency, some
     reproducible figures backing up your claims.

 7.  I'm not at all convinced that all of the conversions
     between AT_LifeTime, AT_LifeLine and AT_LifeView
     increment and decrement the reference counts as
     appropriate.

 8.  The AT_LifeView class doesn't (so far as I can see)
     provide any functionality that a raw pointer or
     reference doesn't provide.  Therefore I don't see any
     point to it.

 9.  AT_Pointer seems to be another attempt to reimplement
     std::auto_ptr.  Why bother re-implementing it?  Your
     version doesn't work as well as the standard one.

In short, I don't think you'll generate much enthusiasm for
your implementation, however, if you think there are
worthwhile points to be considered, do suggest them, but as
modifications to std::auto_ptr, TR1's std::shared_ptr or
std::weak_ptr.  Proposing a wholesale alternative without
clear discussion on its benefits over the existing one will
not win you many supporters.


------------------------------------------------------------
Specific comments on your code follow:


> template < class w_ClassRef > class AT_ReferenceTraits;

If you're attempting to propose something as an alternative
to the TR1 shared_ptr, and hence eventually for probable
addition to the Standard, you should use the same naming
convention as the Standard, viz, lower_case class and
function names, and InterCaps for template parameters.
Oh, and no reverse Polish.  This may sound pedantic, but
people used to reading the Standard library will find it
easier to parse.

If you want serious consideration to be given to your
suggestions, you have to go out of your way to make it easy
for people to quickly see its salient points.  This also
means removing the extraneous clutter from the file so that
the *interface* is immediately clear.  (I've done this to
the bits of your design that I've quoted.)

> /**
>  * AT_LifeControl is an abstract base class for any class whose lifetime
>  * is managed by means of reference counting.
>  */
> class AT_LifeControl
> {
>   public:
>     virtual ~AT_LifeControl();
>     virtual int AddRef() = 0;
>     virtual int Release() = 0;
> };

You're now advocating an intrusive implementation, are you?
This is not a bad idea, as long as an externally reference
counted implementation is also possible, ideally without
exposing the difference to end user.

(Clearly there are potential differences in interface
between external and intrusive shared pointers.  For
example, it could be safe to do

  shared_ptr<T> a( new T ), b( a.get() );

with a intrusive shared pointer, but rarely will be with an
external one.  IMHO, this is best left illegal for all
shared pointers, leaving the interfaces to intrusive and
external pointers exactly the same.)

> template <class w_ClassRef> class AT_ReferenceTraits
> {
>   public:
>     static inline void IncRefCount( w_ClassRef i_ptr )
>       { i_ptr->AddRef(); }
>     static inline void DecRefCount( w_ClassRef i_ptr )
>       { i_ptr->Release(); }
> };

When an object is created, does it have a reference count of
1, or must IncRefCount() be called?

[quoting out of order]
>  * If you are using your own reference-counting scheme, i.e. one not
>  * based on AT_LifeControl, then you will need to define your own
>  * helper class that implements the same interface as AT_ReferenceTraits.

Can you write me such a policy class that handles external
reference counting?  What are the function arguments to
IncRefCount?  And how is the reference count accessed from
them?

>  * (Incidentally, the name "AT_LifeLine" is meant to evoke the image
>  * of someone tossing a rope to someone adrift at sea, saying, in
>  * effect, "Here, this is yours, you'd better take ownership of it.")

Evoking the image of a shared pointer might be more
appropriate.  Seriously, though, I really don't like your
choices of names.

>  * Instrumenting an AT_LifeLine object to check for memory leaks is a
>  * useful debugging aid, but is expensive in terms of execution time.

This is an quality-of-implementation issue, and really isn't
relevant to this discussion.  (I've removed all such
debugging hooks from the quoted text.)

> template <
>     class w_ClassRef,
>     class w_RefTraits = AT_ReferenceTraits< w_ClassRef >
> >
> class AT_LifeLine
> {
>     w_ClassRef m_ptrVal;

So, w_ClassRef is supposed to be a pointer type.  I.e.
completely at odds to almost all existing smart pointer
implementions where the first argument is the pointee.  Thus
you want to write AT_LifeLine<T*> rather than shared_ptr<T>.

>         inline AT_LifeLine(
>             const AT_LifeLine< w_ClassRef, w_RefTraits > & i_ptr
>   )
>           : m_ptrVal( i_ptr.m_ptrVal )
>         {}

You avoid incrementing the reference count here, using the
principle that ownership flows from the RHS to the LHS
without ever being shared between them.  A few points:-

 *  The RHS is being semantically modified by the
    constructor, and therefore should be taken by
    non-const reference.

 *  I know you had MadeTransfer functions to assert in
    "debug" builds, but is it really your intention that a
    "non-debug" build can produce subtle bugs (e.g. double
    destruction) by copying such a pointer twice?  I would
    think that just setting m_ptrVal to NULL on the RHS
    would be safest.  At least that way, you'll get a SEGV
    fairly rapidly when something goes wrong.

 *  It looks to me like you're getting very close to
    reimplementing std::auto_ptr.

>         inline AT_LifeLine( w_ClassRef i_ptr = 0 )
>           : m_ptrVal( i_ptr )
>         {}

Should be explicit.

>         inline AT_LifeLine( w_ClassRef i_ptr, bool i_takeOwnership )
>           : m_ptrVal( i_ptr )
>         {
>             if ( i_takeOwnership ) {
>                 if ( i_ptr ) {
>                     w_RefTraits::IncRefCount( i_ptr );
>                 }
>             }
>         }

What's wrong with combining this with the previous
constructor by defaulting i_takeOwnership to false?

Hang on.  Is the intention that I must write

  AT_LifeLine<T*>( new T, true )

to take ownership of the pointer?  This will be very error
prone, as people are used to using smart pointers that use
the syntax

  shared_ptr<T>( new T )

Having it *not* take ownership by default is very
counter-intuitive.

>         inline ~AT_LifeLine() {}

What happens when an exception results in the pointer being
destroyed before it has transfered ownership away?  Like
this it leaks resources, or with your debugging hooks, will
assert.  This is a very bad.

>         inline AT_LifeLine & operator= ( const w_ClassRef i_ptr )
>         {
>           m_ptrVal = i_ptr;
>           return *this;
>         }

I don't think you want an operator= that has a raw pointer
on its RHS.  And I note that it doesn't take ownership of
the pointer.

>         inline w_ClassRef operator-> () const
>         {
>             return m_ptrVal;
>         }

It's normal to supply an operator*() as well.

>         inline operator w_ClassRef () const
>         {
>             return m_ptrVal;
>         }

Eurgh.  Implicit conversions to the raw pointer type.  Have
you considered how easy it would be to implicitly convert it
to a raw pointer and thus destroy the smart pointer,
probably causing a resource leak.  (Unless the destructor
asserts, which doesn't seem much better.)

>         inline operator bool () const
>         {
>             return m_ptrVal != 0;
>         }

Look at how boost::shared_ptr makes use of

  typedef T* (shared_ptr<T>::*boolean)() const;

  operator boolean() const
    { return px == 0? 0: &shared_ptr<T>::get; }

This is a far better solution to this problem as your
solution implicitly allows expressions such as

  AT_LifeLine a, b, c( a / b );



> template < class w_ClassRef, class w_RefTraits >
> class AT_LifeTime

Many of my comments about AT_LifeLine apply again here.  I
won't bother repeating them.

>         inline AT_LifeTime( w_ClassRef ptr, bool i_takeOwnership )
>           : m_ptrVal( ptr )
>         {
>             if ( i_takeOwnership ) {
>                 if ( ptr ) {
>                     w_RefTraits::IncRefCount(ptr);
>                 }
>             }
>         }

What if w_RefTraits::IncRefCount() throws an exception?
(If it's managing an externally reference count, it might
need to allocate one as you've provided no hook for the
constructor to do that otherwise.  Having said that, I'm not
at all sure it's possible to do external reference counting
at all sanely with your design.)



>         template < class w_ClassRefRhs, class w_RefTraitsRhs >
>         inline AT_LifeTime(
>             const AT_LifeView< w_ClassRefRhs, w_RefTraitsRhs > & i_ptr
>   )
>           : m_ptrVal( i_ptr.m_ptrVal )
>         {
>             // Bump the pointee's reference count.
[...]
>         template < class w_ClassRefRhs, class w_RefTraitsRhs >
>         inline AT_LifeTime(
>             const AT_LifeLine< w_ClassRefRhs, w_RefTraitsRhs > & i_ptr
>   )
>           : m_ptrVal( i_ptr.m_ptrVal )
>         {}

So, if I construct a LifeTime from a LifeView, I increment
the refcount, but if I constructor a LifeTime from a
LifeLine, I don't.  And, if I construct a LifeView from
a LifeLine, I don't increment the RC.  Thus,

  LifeLine ll;

  LifeTime( LifeView(ll) );  // Increments ref count
  LifeTime( ll );            // Does not increment ref count

Yet, in both cases, the LifeTime's destructor will do the
same thing.  How can this be right?

>         inline w_ClassRef * InnerReference()
>         {
>             ReleasePointee( 0 );
>             return & m_ptrVal;
>         }

What's wrong with just assigning the new value into the
pointer?  If a legacy (e.g. C) function needs a
pointer-to-pointer, then you can always do

  myLifeTime<T*> ptr;
  T* tmp = 0;
  legacy_function( &tmp );
  ptr = tmp;  // Or whatever the correct way to transfer
              // ownershipe is.


>         inline w_ClassRef Adopt( const w_ClassRef i_ptr )

The conventional name for this method is reset.  This is
what std::auto_ptr calls it.



>  * The AT_LifeView smart pointer is intended to be used in a very
>  * specific situation:  You need to pass an object's smart pointer
>  * to a function so the function can do something with the object.

Why not just pass a reference to the underlying type? (Not
even the pointer, just a plain old reference.)

>  * While this isn't incorrect, it is unnecessary for this
>  * kind of function, and thus adds a performance penalty for no
>  * reason.

If you make the mechanism for altering the refence count
inlineable, then this becomes a trivial penalty.  This is
what the Boost implementation does.  See other posts in this
thread for more details.


> template < class w_ClassRef, class w_RefTraits >
> class AT_LifeView

I don't see what this class does that a raw pointer or
reference doesn't do.  If you don't want to transfer
ownership, this is precisely when you should be using a raw
pointer or reference.



>  * The AT_Pointer smart pointer works essentially like the auto_ptr
>  * of the Standard Template Library (STL).

Well, why not just use std::auto_ptr?  There are a lot of
subtleties to its design that you've overlooked.  (Consider
std::auto_ptr<T>::auto_ptr_ref<U> for example.  Do you
understand why that exists?)

>  *  Therefore, one should use
>  * an AT_Pointer only if the pointee is not capable of reference
>  * counting.

External reference counting allows any type to be reference
counted.  If your smart pointer framework does not provide
for external reference counting, it is highly unlikely to be
considered as a possible alternative to TR1's smart pointer
framework.

>         inline AT_Pointer( w_ClassRef i_ptr = 0 )

Should be explicit.

>         inline AT_Pointer(
>             const AT_Pointer< w_ClassRef > & i_ptr )

Should have a non-const RHS.

--
Richard Smith

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]