boost::scoped_ptr rationale?

J

Juha Nieminen

Can someone give me an example where it makes any sense to use
boost::scoped_ptr?

I mean: As opposed to instantiating the object locally (ie. on the
stack). A stack-allocated object not only achieves the same automatic
lifetime, but it's also much more efficient (allocating an object on
the stack can be tens of times faster than allocating it with 'new').
 
M

Marcel Müller

Juha said:
Can someone give me an example where it makes any sense to use
boost::scoped_ptr?

I mean: As opposed to instantiating the object locally (ie. on the
stack). A stack-allocated object not only achieves the same automatic
lifetime, but it's also much more efficient (allocating an object on
the stack can be tens of times faster than allocating it with 'new').

Did you ever succeed to instantiate a polymorphic object on the stack?
Probably not. The exact type must be known at compile time for this
purpose. So, no factory pattern.

Did you ever need to deal with different instances and controlled
lifetime? The lifetime of stack objects is restricted to {} blocks. At
least they cannot interdigitate.

scoped_ptr is one of the easiest ways to provide exception safety for
local dynamic objects.


Marcel
 
Ö

Öö Tiib

  Can someone give me an example where it makes any sense to use
boost::scoped_ptr?

There is nothing better for a polymorphic component of object.
boost::scoped_ptr data member automatically disables default
assignment and copy construction unlike std::auto_ptr.
  I mean: As opposed to instantiating the object locally (ie. on the
stack). A stack-allocated object not only achieves the same automatic
lifetime, but it's also much more efficient (allocating an object on
the stack can be tens of times faster than allocating it with 'new').

I have used it less often for function local objects. Leigh and Marcel
gave most of the good reasons. Additionally there exist objects that
protect themselves against being allocated statically or automatically
(some techniques give undefined results with objects of static or
automatic storage). If you need local temporary instance of such then
boost::scoped_ptr is better than raw pointer.
 
J

James Kanze

Can someone give me an example where it makes any sense to use
boost::scoped_ptr?
I mean: As opposed to instantiating the object locally (ie. on
the stack). A stack-allocated object not only achieves the
same automatic lifetime, but it's also much more efficient
(allocating an object on the stack can be tens of times faster
than allocating it with 'new').

class B { public : virual ~B() {} ... };
class D1 : public B { ... };
class D2 : public B { ... };

void f(bool condition)
{
boost::scoped_ptr<B> p(condition ? new D1 : new D2);
...
}

It can also be useful as a member of a class.
 
F

Francesco S. Carta

class B { public : virual ~B() {} ... };
class D1 : public B { ... };
class D2 : public B { ... };

void f(bool condition)
{
boost::scoped_ptr<B> p(condition ? new D1 : new D2);
...
}

I don't know if it's expected or not, but MinGW 4.4.0 rejects the above
with the following error:

"conditional expression between distinct pointer types 'D1*' and 'D2*'
lacks a cast"
 
B

Balog Pal

Francesco S. Carta said:
I don't know if it's expected or not, but MinGW 4.4.0 rejects the above
with the following error:

"conditional expression between distinct pointer types 'D1*' and 'D2*'
lacks a cast"

The error message is correct and good: the ?: expression has a single type.
If the types of the two parts differ, it tries to convert B to a and A to B,
expecting exactly one of them succeeding.

In the above case a cast to B* (or another subclass of B that is common for
D1 and D2 ) shall be there on one side.
 
F

Francesco S. Carta

The error message is correct and good: the ?: expression has a single
type. If the types of the two parts differ, it tries to convert B to a
and A to B, expecting exactly one of them succeeding.

In the above case a cast to B* (or another subclass of B that is common
for D1 and D2 ) shall be there on one side.

Ah, thank you very much for the explanation, I never incurred in such an
error... I must have missed to set to mind that the ternary conditional
operator must resolve to exactly one type - assuming I ever read about
it ;-)
 
C

Chris Ahlstrom

Leigh Johnston posted this message in ROT13 encoding:
If the stack is too small for the object and yet you still want stack object
semantics (including RAII/exception support).

You can always increase stack size. I came across a program that demanded
32 Mb of stack.

Actually, that's not really true. Try writing some code to time the
differences as a function of space allocated, sometime. It's closer than
you'd think -- it sure surprised me. Here's a note from the code:

Now, this speed-up is actually quite minimal -- using the heap and
malloc() is a little faster, depending on the operating system and the
amount of thread data being allocated. Not until the amount of thread
data exceeds 1 Mb does the stack implementation become significantly
faster.

This experiment came about because I implemented as stack-based thread
handler based on someone else's implemenation, because he said it was
faster. I posted that on a newsgroup, and someone said I was wrong.
And sure enough, I was, to some extent.
 
J

Juha Nieminen

Leigh Johnston said:
If the stack is too small for the object and yet you still want stack object
semantics (including RAII/exception support).

That may indeed be one situation. I have had situations (in small test
programs) where trying to eg. use a stack-allocated array which was too
large made the program fail.
 
J

Juha Nieminen

Marcel Müller said:
Did you ever succeed to instantiate a polymorphic object on the stack?
Probably not. The exact type must be known at compile time for this
purpose. So, no factory pattern.

If you don't know the actual type of the object at compile time in
the current scope, but nevertheless want to use scoped_ptr to handle
it, it would mean that there has to be some function which you call from
the current scope and which returns a raw pointer to the object it
allocated (leaving the responsibility of deleting the object up to the
calling code). While this is certainly something you can do, I would say
it's a pretty bad design (allocating something and then leaving the
deletion up to the calling code is asking for trouble in a non-garbage-
collected environment).

If I understood correctly, that "factory" function can certainly not
return a scoped_ptr (because it's non-copyable).

Of course it could take a scoped_ptr reference as parameter and put
the allocated object there. Feels a bit contrived, though.
The lifetime of stack objects is restricted to {} blocks.

So is the lifetime of an object handled by scoped_ptr (the object cannot
be shared nor transferred to anything else). So?
scoped_ptr is one of the easiest ways to provide exception safety for
local dynamic objects.

I didn't doubt that. My question is why you would allocate the object
dynamically in the first place.
 
J

Juha Nieminen

Öö Tiib said:
There is nothing better for a polymorphic component of object.
boost::scoped_ptr data member automatically disables default
assignment and copy construction unlike std::auto_ptr.

I suppose that makes sense.

(OTOH, it might be a bit more obfuscated to get an error message related
to some private member of a class when you try to copy instances of it,
than it would be to get a direct error message that says "sorry, the copy
constructor / assignment operator of this class is not accessible". If you
want to make it more explicit that you really have disabled copying, then
you would declare the copy constructor / assignment operator private, in
which case it doesn't really matter if you use scoped_ptr, auto_ptr or
something else for your polymorphic Pimpl opject...)
 
Ö

Öö Tiib

  If you don't know the actual type of the object at compile time in
the current scope, but nevertheless want to use scoped_ptr to handle
it, it would mean that there has to be some function which you call from
the current scope and which returns a raw pointer to the object it
allocated (leaving the responsibility of deleting the object up to the
calling code). While this is certainly something you can do, I would say
it's a pretty bad design (allocating something and then leaving the
deletion up to the calling code is asking for trouble in a non-garbage-
collected environment).

Yes, usually producing factory is in pair with recycling factory, but
not always. When there is such pair then boost::shared_ptr is better
since you can attach deleter (recycling factory) to it. I am not sure
why deleter can not be attached to boost::scoped_ptr, makes me to use
shared_ptr too often for RAII and in wrong place (for something i do
not want to be shared).
  If I understood correctly, that "factory" function can certainly not
return a scoped_ptr (because it's non-copyable).

Of course it could take a scoped_ptr reference as parameter and put
the allocated object there. Feels a bit contrived, though.

Factory can return raw pointer. Factory pattern is most elegant when
you have an input (lets say stream) whose contents you know that will
form some Animal, but not sure if it is a Cat or a Dog or a Frog.
  So is the lifetime of an object handled by scoped_ptr (the object cannot
be shared nor transferred to anything else). So?

It can be ptr.reset() or even ptr.reset(to_something_else) before
going out of scope.
 
M

Michael Doubez

  I suppose that makes sense.

  (OTOH, it might be a bit more obfuscated to get an error message related
to some private member of a class when you try to copy instances of it,
than it would be to get a direct error message that says "sorry, the copy
constructor / assignment operator of this class is not accessible". If you
want to make it more explicit that you really have disabled copying, then
you would declare the copy constructor / assignment operator private, in
which case it doesn't really matter if you use scoped_ptr, auto_ptr or
something else for your polymorphic Pimpl opject...)

But it make sense if you forgot to handle the copy semantic.

As an extra, you don't need to add the destruction of the pointed
object in the destructor, which improve somewhat the locality of code
changes.
 
Ö

Öö Tiib

  I suppose that makes sense.

  (OTOH, it might be a bit more obfuscated to get an error message related
to some private member of a class when you try to copy instances of it,

Depends on compiler perhaps. I think most will just say that you do
not have copy constructor or assignment operator of class available
and not drop too deeply into details why it is so.
than it would be to get a direct error message that says "sorry, the copy
constructor / assignment operator of this class is not accessible". If you
want to make it more explicit that you really have disabled copying, then
you would declare the copy constructor / assignment operator private, in
which case it doesn't really matter if you use scoped_ptr, auto_ptr or
something else for your polymorphic Pimpl opject...)

OK, may be matter of taste. I like complex class interface definition
arranged like that:

class Something // :,, possible bases
{
public:
// ... interface type defs
public:
Something(); // may be missing
Something(Something const& that); // may be missing
// ... other constructors
virtual ~Something(); // may be missing
Something& operator=(Something const& that); // may be missing
// **comment**
// ... rest of public interface
// ... is sometimes not that short
protected:
// ...
private:
// ...
};

Like you see private section is at bottom and i do everything i can to
not make someone who just wants to use the class for something not to
need to read the protected section or below. There are too lot of
possible class layouts to make interface intuitive without commenting
construction/destruction/copying within public section.

That **comment** therefore explains all oddities of construction/
destruction/copying. What about missing ones from that section? Are
default, compiler-generated things OK? Why? Was something from default
interface disabled by something? Why? Was something moved to non-
public for some reason? Why? Are there factories/recyclers? Why?
Where? ... so on.

It is perhaps matter of taste, but i feel it is one place where i am
pondering "why? .. why? ... wtf?" most often when looking at someones
class definition and comments help there greatly. This pondering gets
boring after a while when there are hundreds of classes.
 
Ö

Öö Tiib

It can be ptr.reset() or even ptr.reset(to_something_else) before
going out of scope.

Note ... quite often the constructors may throw. Your algorithm may
have ways to survive some of exceptions (for example by retrying). Not
too convenient to write usage of automatic object whose constructor
may throw survivable exceptions?
 
M

Michael Doubez

Note ... quite often the constructors may throw. Your algorithm may
have ways to survive some of exceptions (for example by retrying). Not
too convenient to write usage of automatic object whose constructor
may throw survivable exceptions?

It depends. If they are swapable, I may prefer to keep them on the
stack.

There are also strategies using in-place new (in the spirit of Barton-
Nackman's fallible) that can handle it quite easily.
 
Ö

Öö Tiib

It depends. If they are swapable, I may prefer to keep them on the
stack.

There are also strategies using in-place new (in the spirit of Barton-
Nackman's fallible) that can handle it quite easily.

Perhaps i lost track how. Can you paste some sample elegant example? I
may be wrong but it feels like by using some sort of two-phase
construction with default constructor producing useless/invalid
object? There are cases when missing value is important, useful and
valid state of object (scientific computing). On other cases i usually
prefer a pointer that is NULL (or exception) to any ways to produce
invalid object with missing value. The placement news, swaps and
invalid state (when not natural) cause confusion to average
programmer, slow him down and i do not have time to maintain
everything myself.
 
M

Michael Doubez

Perhaps i lost track how. Can you paste some sample elegant example? I
may be wrong but it feels like by using some sort of two-phase
construction with default constructor producing useless/invalid
object? There are cases when missing value is important, useful and
valid state of object (scientific computing). On other cases i usually
prefer a pointer that is NULL (or exception) to any ways to produce
invalid object with missing value. The placement news, swaps and
invalid state (when not natural) cause confusion to average
programmer, slow him down and i do not have time to maintain
everything myself.

Something along the lines of:

template< class T >
class DelayedBuilder
{
public:
DelayedBuilder():_valid(false){}
// ensure RAII
~DelayedBuilder(){
if(_isvalis)destroy();
}

// reinterpret union as T
T& value(){ assert(_isvalid); return *((T*)_data.object);}

// destroy object
void destroy(){ assert(_isvalid); value().~T(); }

// build object
void build()
{ assert(!_isvalid); new (_data.object) (); _isvalid = true; }
template<class P1> void build(P1 const & p1)
{ assert(!_isvalid); return new (_data.object) (p1); _isvalid =
true; }
template<class P1,class P2> void build(P1 const & p1, P2 const &
p2)
{ assert(!_isvalid); return new (_data.object) (p1,2); _isvalid =
true; }
// and so one for build functions

private:
union{ stricter_alignement ; unsigned char object[ sizeof(T) ] ;}
_data;
bool _isvalid;
};


And the usage:

// SocketStream has only constructor with (address,port)
DelayedBuilder<SocketStream> socket;

// try to build socket 3 times
for( int i=0; i<3 ; ++i )
{
try {
socket.build("127.0.0.23",4242);
} catch(SocketConnectError const & e){
continue;
}
...
}

if( !socket.isValid() )
{// failure
return -1;
}

// ... use socket

I don't say I would use it all over the place but it might have its
use.
 
J

James Kanze

If you don't know the actual type of the object at compile
time in the current scope, but nevertheless want to use
scoped_ptr to handle it, it would mean that there has to be
some function which you call from the current scope and which
returns a raw pointer to the object it allocated (leaving the
responsibility of deleting the object up to the calling code).

Why? The function declaring the scoped_ptr can decide itself
which class it wants to new. Don't forget that scoped_ptr
supports reset, so you can do something like:

boost::scoped_ptr p;
if (someCondition)
p.reset(new Type1);
else (somethingElse)
p.reset(new Type2);
// and if neither is true, I have a null pointer...
 
Ö

Öö Tiib

Perhaps i lost track how. Can you paste some sample elegant example? I
may be wrong but it feels like by using some sort of two-phase
construction with default constructor producing useless/invalid
object? There are cases when missing value is important, useful and
valid state of object (scientific computing). On other cases i usually
prefer a pointer that is NULL (or exception) to any ways to produce
invalid object with missing value. The placement news, swaps and
invalid state (when not natural) cause confusion to average
programmer, slow him down and i do not have time to maintain
everything myself.

Something along the lines of:

template< class T >
class DelayedBuilder
{
   public:
     DelayedBuilder():_valid(false){}
     // ensure RAII
     ~DelayedBuilder(){
         if(_isvalis)destroy();
     }

     // reinterpret union as T
     T& value(){ assert(_isvalid); return *((T*)_data.object);}

     // destroy object
     void destroy(){ assert(_isvalid); value().~T(); }

     // build object
     void build()
     { assert(!_isvalid); new (_data.object) (); _isvalid = true; }
     template<class P1> void build(P1 const & p1)
     { assert(!_isvalid); return new (_data.object) (p1); _isvalid =
true;  }
     template<class P1,class P2> void build(P1 const & p1, P2 const &
p2)
     { assert(!_isvalid); return new (_data.object) (p1,2); _isvalid =
true;  }
     // and so one for build functions

   private:
     union{ stricter_alignement ; unsigned char object[ sizeof(T) ] ;}
_data;
     bool _isvalid;

};

And the usage:

// SocketStream has only constructor with (address,port)
DelayedBuilder<SocketStream> socket;

// try to build socket 3 times
for( int i=0; i<3 ; ++i )
{
  try {
  socket.build("127.0.0.23",4242);
  } catch(SocketConnectError const & e){
    continue;
  }
  ...

}

if( !socket.isValid() )
{// failure
 return -1;

}

// ... use socket

I don't say I would use it all over the place but it might have its
use.

Oh, you meant in a template that adds valid state externally. Sure,
there may be reasons when such template may be better performing than
boost::scoped_ptr<>. It is perhaps somewhat like boost:eek:ptional<>?
Performance optimizations are usually in later list of things to do
and average team mate does not usually apply to maintain templates
anyway. I usually take such things from boost since it is usually well-
tested and so does rarely need maintenance.

As for performance ... all know when dynamics really start to hurt
performance. Worst case is when the object state is small (lets say
one double in it) but we have large containers of such. Vector of
scoped_ptr's may be lost case there. However DelayedBuilder's are also
questionable since these will effectively double up the needed storage
size compared to just vector of such objects. Therefore using NaN
value of that double state for indicating if the object is invalid
might be lot better performance optimization.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,228
Members
46,818
Latest member
SapanaCarpetStudio

Latest Threads

Top