To use or not to use smart pointers?

R

Roland Pibinger

What I meant is that pointers are
the core language feature that _creates the possibility_ of explicit
life-time management.

Pointers create the possibility to reference objects (stack-, heap-,
global objects). There is no intrinsic necessity to extend pointers to
object-lifetime managers.
 
D

Dennis Jones

Roland Pibinger said:
Pointers create the possibility to reference objects (stack-, heap-,
global objects). There is no intrinsic necessity to extend pointers to
object-lifetime managers.

Okay, there seems to be a lot of argument over whether or not pointers are
needed for memory management. But I have not yet heard anyone talk about
HOW one avoids using pointers to manage dynamically allocated memory.
Therefore, I have to ask the (apparently naive) question, if you don't use
pointers for memory management, what do you use? Surely you cannot write
every program without dynamically allocating some memory, and therefore not
ever need to manage that memory?

Is there some (Zen-like?) level of software development that somehow manages
to avoid memory allocation, and therefore the need to deallocate it? What
am I missing here?

- Dennis
 
K

Kai-Uwe Bux

Roland said:
Pointers create the possibility to reference objects (stack-, heap-,
global objects).

That too. But they also create the conceptual split of the pointer and the
pointee which is necessary to decouple life-time from scope. Only values
can leave a scope, since all local objects are destroyed. Since a pointer
value can leave the scope, the possibility for pointee-life-time management
arises.

There seems to be a little disagreement about the data type T*. You seem to
claim that for this type the derefencing operation * and the address
operator & are the only primitive operations. I would maintain that new T
and delete() are also primitive operations on objects of type T*.
There is no intrinsic necessity to extend pointers to object-lifetime
managers.

I will concede that you can write a program that uses pointers but does not
contain any new() or delete() statements. I will also grant you that for
those cases, replacing pointers by smart pointers would be pointless (if at
all possible). However, as soon as you have a new() or a delete() statement
in your program, you _are_ managing life-times whether you like it or not.

However, my claim that you quoted is that pointers are necessary to do
explicit life-time management (which is not to say that each and every use
of pointers amounts to life-time management). What other mechanisms for
explicit life-time management do you find in the core language?


Best

Kai-Uwe Bux
 
R

Roland Pibinger

However, my claim that you quoted is that pointers are necessary to do
explicit life-time management (which is not to say that each and every use
of pointers amounts to life-time management). What other mechanisms for
explicit life-time management do you find in the core language?

It's beyond question that you need real pointers to access dynamically
created objects, e.g. to manage their lifetime. There's also no doubt
(to me) that destructors in combination with automatic objects, a.k.a.
RAII, are the best thing ever invented in C++. But that doesn't mean
that objects that mimic real pointers but do something 'smart' under
the hood (e.g. lifetime management) are a good idea too.
 
K

Kai-Uwe Bux

Roland said:
It's beyond question that you need real pointers to access dynamically
created objects, e.g. to manage their lifetime. There's also no doubt
(to me) that destructors in combination with automatic objects, a.k.a.
RAII, are the best thing ever invented in C++. But that doesn't mean
that objects that mimic real pointers but do something 'smart' under
the hood (e.g. lifetime management) are a good idea too.

Good, we finally reached some common ground: that RAII is good does not mean
that smart pointers are good too. That just is an independent question.

Upthread, I think, I did not make an unqualified general statement that
using smart pointers is a good idea per se. Instead, I observed that they
_can_ play a role in reducing the trickness of programs involving
dynamically allocated objects. However, I am not really interested in
general remarks about smart pointers being smart or dumb because I don't
consider unspecific generalities particularly helpful in making design
decisions; and therefore, I gave specific examples where I found smart
pointer useful / convenient. Maybe a more specific discussion about
particular use cases would be more interesting.


Best

Kai-Uwe Bux
 
K

kwikius

It's beyond question that you need real pointers to access dynamically
created objects, e.g. to manage their lifetime. There's also no doubt
(to me) that destructors in combination with automatic objects, a.k.a.
RAII, are the best thing ever invented in C++. But that doesn't mean
that objects that mimic real pointers but do something 'smart' under
the hood (e.g. lifetime management) are a good idea too.

IMO this whole debate would be much more interesting with some example
high level specification (allowing for any differences in useage
between smart pointers and raw pointers), and then the anti raw
pointer enthusiasts could implement it using raw pointers and the
pro's could implement it using smart pointers. I have no doubt that if
measured in terms of raw speed the raw pointers would win hands down,
however the other part to this contest, that is admittedly harder to
judge and that would carry twice the weight, is how easy is this
thing to write and maintain?


As a warm up. Here is a somewhat vague spec of one library I found
useful. Anyone that has used a ducktyping language may see something
familiar.

One library I have found surprisingly useful is a C++ directory tree.
The thing was thrown together basically for fun but since writing it I
have found a huge variety of uses.
The tree has one interesting characteristic, which is that you can
throw any type of data into it
The tree is comprised of various types of nodes, each which carries a
unique id in its directory level. So far I have implemented the
following:

abstract_node : The ABC node, but one that can be instantiated. very
politically incorrect but useful for debugging..

leaf_node<T> : a terminal node that can holds a 'T'. N.B: where one
tree can hold infinite different and unrelated types of T.

branch_node<Container> : A branch that uses Container for its child
elements. So far I have only implemented vector and list, but ideally
it should be able to use a map.

container_with_leaf<Container> : I found it useful to have a branch
with some element accessable directly from its id. This type of node
can look like either a branch or a leaf.

The thing is implemented using boost::shared_ptr and weak_ptr and
internally uses a large amount of dynamic casting. Originally getting
a child returned a weak_ptr, but I found this extremely tedious, so I
restricted use of weak_ptr to getting a nodes parent I have thought of
reimplementing it with raw pointers to see what difference it would
make, both in terms of ease of writing the code and in terms of
performance. I suspect that the performance under shared_ptr is
extremely poor, but I also suspect that it would be more difficult to
code and maintain using raw pointers. I emphasise that I don't know
though !

At the end some code where the tree is set up as a parser for some
simplified html source. N.B This is just one specific use. I have also
used it for other things.. (I hope to publish the source for the tree
one day as part of my super-quan distro...)


As part of the above challenge, It is expected that whatever crap you
throw at the dom that you will be informed that you have done
something wrong by some sort of std::exception derived exception...

The two sides can then try to find ways to screw the others versions
up ;-)

example use... (The following only shows a small sample of its
functionality).

anything::ptr is some sort of shared_ptr in my impl. In this setup The
node ids are looked up as encountered directly in the source text.

N.B Not all function ptrs in following have the same sig ;-)

void quanta::html::doc::setup_dom_functions()
{
using quanta::dom::add_branch;
using quanta::dom::list_branch;
using quanta::dom::add_element;

list_branch::ptr html
= add_branch<std::list>(html_root,"html",&parse_html_tag);

list_branch::ptr head
= add_branch<std::list>(html,"head",&parse_head_tag);

add_element(head,"text-css",&parse_text_css);
list_branch::ptr body
= add_branch<std::list>(html,"body",&parse_body_tag);

add_element(body,"text",&parse_text);
add_element(body,"comment", &parse_comment);

//necessary to look up the correct function for the following cases
typedef void (*pf)( std::istream & ,
quanta::html::body_content_list::ptr ,
quanta::html::tag_identifier::ptr
);

add_element(body,"a",&parse_a_tag);

add_element(body,"p",static_cast<pf>(&parse_list_tag<quanta::html::p_tag>));

add_element(body,"div",static_cast<pf>(&parse_list_tag<quanta::html::div_tag>));

add_element(body,"pre",static_cast<pf>(&parse_list_tag<quanta::html::pre_tag>));

add_element(body,"span",static_cast<pf>(&parse_list_tag<quanta::html::span_tag>));


add_element(body said:

add_element(body said:

add_element(body said:

add_element(body said:

add_element(body said:

add_element(body said:

list_branch::ptr table = add_branch<std::list>(body,"table",
&parse_table_tag);

list_branch::ptr tr =
add_branch<std::list>(table,"tr",&parse_table_tr_tag);

add_element(tr,"td",&parse_table_tr_td_tag);
}

regards
Andy Little
 
J

James Kanze

James said:
[...]
(and also should carry the burden of proving the correctness
for the code base resulting from a design decision in favor of
raw pointers).
That is, of course, the key part. Systematic use of shared_ptr,
where not appropriate, makes it very difficult to prove that the
code is correct.
Pointers in C++ are heavily overloaded. They offer two main
features at once, namely (a) decoupling life-time from scope
and (b) support for polymorphism.

I'm not sure that "overloaded" is quite the right word, but
there's more than a little truth in what you say. Pointers are
an implementation technique (like most language features), and
can be used to implement a number of different concepts. Three
important concepts are navigation, arbitrary lifetime and
polymorphism. And that smart pointers (at least the classical
smart pointers) are neither necessary, or even helpful, for the
first two. Before using a smart pointer, you should understand
why you're using a pointer to begin with.

The one case where I do (or did, before I started using the
Boehm collector) religiously use some form of reference counted
pointer was when I allocated dynamically only for reasons of
polymorphism; when without the constraint of polymorphism, I
would have used a value object, directly on the stack (and
copied when necessary). Such objects are far from the majority
in my code, however.
If you want the later, you are inviting upon you the perils
of the former.

Agreed. Technically, the problem isn't the pointer, but dynamic
allocation and the fact that you cannot copy without slicing.
But the result is that you end up using pointers, and needing to
manage lifetime when you don't need to conceptually.
That pointer and pointee have different life-times introduces
a global state into the program and arguing correctness becomes
much more of a hassle: every new() has to match with one and
only one delete() along each path of execution.

That's only a small part of the problem in general. If you're
allocating in order to achieve application specific object
lifetimes (which accounts for almost all of the dynamic
allocations in my code), then the problem is that the delete
must occur at the right moment (i.e. when the application logic
says that the lifetime of the object must end), and that no
pointers to the object are used after that (which is generally
best handled by some variation of the observer pattern). Using
a pointer which prevents the immediate destruction of the object
breaks the application logic, and usually doesn't solve the
dangling pointer problem satisfactorily.
Exceptions can divert the execution path at any moment to an
unknown location, and client supplied types in templates make
it unpredictable what will throw and what will be thrown. That
makes life very hard.

That's why I argued against exceptions for a long time:).

Seriously, that's another use of smart pointers; to hold onto
the object until we can turn it over to the application logic.
You can't use boost::shared_ptr for this, since there's no way
of regaining control, but I do use auto_ptr rather often here;
in many cases, boost::scoped_ptr would also be an option.
A smart pointer like shared_ptr<> also requires global
knowledge in at least two ways: (a) we need to ensure that
no cycles occur and (b) when one relies on side-effects of
a destructor, sometimes one needs to argue that the last
pointer owning a given pointee is going out of scope.

The first is almost impossible if you use shared_ptr
religiously. On the other hand, short lived polymorphic objects
tend not to have pointers to other short lived polymorphic
objects (although pointers to entity objects are frequent), so
shared_ptr works well for them.
For me, the lesson is that one needs a set of idioms and tools
that allow us to reduce code complexity and restore the ability
to argue correctness of code locally (by looking at, say, 20
_consecutive_ lines of code at a time).

Exactly. There is no silver bullet.
Thus, I tend to wrap pointers within classes.

Most pointers will be members of a class somewhere, since the
largest single use of pointers is to navigate or express
relationships between objects. Pointers as local variables are
exceedingly rare, other than as copies of a pointer obtained
from a long lived object.
Smart pointers are just readily packaged
idioms that come up often in this context. For these reasons, smart
pointers form an intermediate layer in my library. They are used
to build other components but rarely used directly. Let me
illustrate this with a few examples.
Example 1 [statefull function objects]
=========

Interesting example. Somewhat justified, because of arguably
poor design in the standard library. (If functional objects
were passed by reference, for example, you wouldn't use this
nearly as much.) Most of the time, however, I find it
preferable to use a raw pointer to a local variable in the
funtional object, rather than allocating the actual object
dynamically; the one exception is if the actual object must also
be polymorphic.

[...]
Similar trickery can be applied to reduce the cost of
copying for large objects, in which case reference countng
becomes an invisible performance hack.

And will usually be implemented directly in the class you're
optimizing:).
Note that shared_ptr<> is actually overkill in this context.
It's support for polymorphism, incomplete types, and a custom
deleter is not needed. A simple intrusive reference counting
smart pointer (not working with incomplete types) fits this
bill neatly.

Which is what leads to my previous comment:).
Example 2 [deep copy pointers]
=========
C++ has value semantics. However, variables are not polymorphic.
You have to use a pointer to support polymorphism. Deep copy
pointers come as close as possible to polymorphic variables
as you can get in C++ (if only we could overload the dod-operator).
A particular example is tr1::function<>, which under the hood
is a deep copy pointer that forwards the function interface to
the pointee.

The letter envelope idiom, in sum.
Example 3 [ownership models]
=========
Smart pointers do not manage resources magically for you, but
sometimes their semantics makes enforcing an ownership model
easier.

If the shoe fits, of course... If exceptionally you do need
exactly the semantics of shared_ptr, by all means use it.

I've rarely had a case where this was the case for object
lifetime. I have had it several times for other resources, like
locks. In such cases, explicitly passing a deletor to share_ptr
fits the bill nicely; I probably have more shared_ptr with a
deletor that unlocks a mutex than I do shared_ptr with the
default deletor.

[...]
On the other hand, there are sometimes reasons to use a
smart pointer directly, although I always feel a little
uneasy. Example:
#include <memory>
class UserCommand {
public:

// ...

virtual
~UserCommand ( void ) {}
};
// magic source of commands.
// in real life, this is polymorphic.
UserCommand * getUserInput ( void ) {
return new UserCommand;
}
class MessageHandler {
public:

// ...

virtual
void operator() ( UserCommand const & cmd,
bool & do_terminate );
// UserCommand is polymorphic

virtual
~MessageHandler ( void ) {}
};
struct MyException {};
void event_loop ( MessageHandler & the_handler ) {
// MessageHandler is polymorphic
try {
bool last_run = false;
do {
// neeed for pointer because of polymorphism:
UserCommand * the_command = getUserInput();
the_handler( *the_command, last_run );
} while ( last_run );
}
catch ( MyException const & e ) {
if ( false ) {
// placeholder for things that can be
// handled here
} else {
throw( e );
}
}
}
The event_loop as written is buggy. If the line

the_handler( *the_command, last_run );
throws, we leak memory.

Even if it doesn't. This is an avatar of your polymorphic
object problem mentionned at the start; if UserCommand wasn't
polymorphic, you'd use value semantics, and be done with it.
One can use std::auto_ptr<> to rectify things:

Which is exactly what I do in this case. std::auto_ptr is
exactly what the doctor ordered here.
 
K

Kai-Uwe Bux

James said:
On Jul 21, 4:04 am, Kai-Uwe Bux <[email protected]> wrote:

[massive agreement snipped]
That's only a small part of the problem in general. If you're
allocating in order to achieve application specific object
lifetimes (which accounts for almost all of the dynamic
allocations in my code), then the problem is that the delete
must occur at the right moment (i.e. when the application logic
says that the lifetime of the object must end), and that no
pointers to the object are used after that (which is generally
best handled by some variation of the observer pattern). Using
a pointer which prevents the immediate destruction of the object
breaks the application logic, and usually doesn't solve the
dangling pointer problem satisfactorily.

This particular scenario reminds me of a smart pointer that has somewhat
unusual semantics (and one that is fun to implement, too). If you free the
pointee through any of the pointers holding it, all pointers revert to null
automatically; thus, the information that the pointee has died spreads
globally. Dereferencing a null pointer then throws a particular exception
or triggers an assert(). If the last pointer pointing to the pointee before
the pointee dies, an exception with a pointer to the pointee is thrown.

[more agreement snipped]


Best

Kai-Uwe Bux
 
W

werasm

Boris said:
Their main concern is a
possible performance impact. I've been explaining the advantages of smart
pointers endlessly (which are currently used in all our C++ software; we
use the Boost smart pointers) as I'm seriously concerned that there is a
shift to raw pointers.

In our projects we mainly use boost::scoped_ptr for heap-based
members. We often use auto_ptr as constructor parameters which we
then
release for ownership by scoped_ptrs. The performance overhead for
scoped_ptrs are insignificant.

We rarely use shared_ptrs. I'm
not comfortable with the constraints that they have, especially
the constraints that you can't read/write simultaneously when
accessed via multiple threads. Normal pointers don't have that
constraint. We (or I) use shared pointers mostly for objects that
require custom deletion (like file/pipe handles, for instance) or
objects that dictate how they get destroyed. In this case access
to these objects happen through normal pointers anyway, but the
destruction happens via the (only) owner of a shared_ptr.

I've never had the need to use shared_ptr in a container. Getting
around
the copy symantics burden has never been a problem. I'm looking at
giving boost::ptr_container a try, but have not had the need yet.


Regads,

Werner
 
M

Michael DOUBEZ

Boris a écrit :
They don't want to use them at all. Their argument is that performance
is priority #1 ignoring other goals like stability and maintainability
or resource constraints like project time and budget.

That's a concern easy to address. Is performance an issue ? Is there any
benchmark available on the ressources already used ?
[...]
I've been explaining the advantages of smart pointers
endlessly (which are currently used in all our C++ software;
we use the Boost smart pointers) as I'm seriously concerned
that there is a shift to raw pointers.

That's interesting. I've always found that trying to use them
systematically (rather than just in specific cases) caused a lot
of extra effort for no gain.

I admit that I've been using them rather systematically. The less time I
have to spend looking around for memory leaks the more time I can
concentrate on other things or the earlier I can finish a project.

I am not sure memory leaks are that common in the main case and usually
easy to find. Problems arise when there are complex object life time.
Given
that the projects I'm involved in typically suffer under heavy resource
constraints I find it a luxury to evaluate the pointer type every time I
need to use a pointer.
As I'm also not always happy with the code quality of the projects

Do you have any defect management report, tool or metric that show so or
is it a purely subjective perception ?
I'm responsible for I assume that making
everyone use smart pointers contributes more to the code quality than
giving them a choice (if you don't trust someone to make a correct
choice you don't trust him either to be able to write correct code with
raw pointers).

Make a programmer use something he is not convinced of is a sure way to
dispirit your team. Showing them you don't trust them to do their job
correctly is a proven bad idea.

An approch that sometimes work (it depends on your relationship) is to
expose the problem (with strong backing/report ...) to them and *ASK*
them what you (you and them as a team) can do. Eventually, they may be
more perceptive about using smart pointers if it is really a good idea
or to find an alternative solution.

Nothing to do with C++ here.

Michael
 
J

James Kanze

They don't want to use them at all. Their argument is that
performance is priority #1 ignoring other goals like
stability and maintainability or resource constraints like
project time and budget.

That's just stupid. There may be places where they cause a
performance problem (though IMHO it's unlikely); if so, the
profiler will reveal them, and you can try to work out something
faster. They're not a silver bullet, but they are convenient in
specific cases, and it would be silly to refuse to use them in
such cases, just because they might be slower.

Of course, if the other programmers on the team consider
their speculations about unmeasured performance more important
than project time and budget, it really doesn't matter what you
do. You're screwed from the start.
[...]
I've been explaining the advantages of smart pointers
endlessly (which are currently used in all our C++ software;
we use the Boost smart pointers) as I'm seriously concerned
that there is a shift to raw pointers.
That's interesting. I've always found that trying to use them
systematically (rather than just in specific cases) caused a lot
of extra effort for no gain.
I admit that I've been using them rather systematically. The
less time I have to spend looking around for memory leaks the
more time I can concentrate on other things or the earlier I
can finish a project.

That's why I use the Boehm collector:). But even without it,
most of the cases involving dynamic memory (at least in my
applications) also involve explicit, application controlled
lifetime. The real problem isn't so much memory leaks, since we
know when to delete the object, but dangling pointers, since
it's easy to forget to notify someone that we are being deleted.

Note that these objects often contain pointers to other such
objects, for navigation, and these pointers often create cycles.
You can't just replace them all with boost::shared_ptr, and
expect to get rid of memory leaks.

That's most of the objects, of course, and without the Boehm
collector, there is a distinct subset of cases where I do use
something like shared_ptr. It doesn't represent the majority of
my objects, but it's not an empty set, either.
Given that the projects I'm involved in typically suffer
under heavy resource constraints I find it a luxury to
evaluate the pointer type every time I need to use a pointer.

Which is what you have to do with smart pointers.
As I'm also not always happy with the code quality of the
projects I'm responsible for I assume that making everyone use
smart pointers contributes more to the code quality than
giving them a choice (if you don't trust someone to make a
correct choice you don't trust him either to be able to write
correct code with raw pointers).

If you don't trust someone to make a correct choice, you don't
trust him to be able to write correct code. The type of
pointers involved don't change anything.
Thus I wonder if the problems smart pointers might cause
(like a possible performance impact) are not actually
negligibile compared to the problems they solve as developers
have more time to concentrate on other things (the ones who
write the code and the others who have to test and maintain
the code later).

I'm not sure I follow. Smart pointers are a low level
implementation technique. They don't free you from having to
consider object lifetime at the design level. When the design
says that a specific smart pointer can do the job, of course,
it's that much less new code you have to write. But my
experience has been that in practice, such cases aren't all that
common (and most of the time, are better handled with garbage
collection, which is even less work).
Absolutely. But I wonder how this should work in practice as
in the projects I've been involved in there are typically more
problems to solve and details to take care of than resources
available.

Sure. Any time you can factor out commonality is good, and if
there is a commonality in the lifetime management of certain
classes of objects, it's good if you can factor it out into a
common smart pointer. In practice, however, I find that this
just isn't the case that often.
And I don't think my situation is exceptional.
Your situation might be different (looking at to your sig I
wonder if you are a consultant :).

I'm not sure what you mean by "consultant". I'm certainly a
consultant in Germany; programmers have to pay an additional
local tax, which consultants are exempt from. But I mostly
write code.
That said my experience so far is that in practice smart
pointers are a tool you automatically use all the time if you
aim for high code quality under heavy resource constraints.

And my experience is that automatically using just about any
tool results in misuse, and poor code quality. Most pointers in
my code are raw pointers, simply because most pointers are used
for navigation, in one way or another. Most "objects" are local
variables, with lifetime automatically handled by the compiler;
the second largest group requires explicit lifetime management
according to application specific requirements, and can't be
automized by some generic smart pointer.
 
B

Boris

[...]Make a programmer use something he is not convinced of is a sure
way to dispirit your team. Showing them you don't trust them to do their
job correctly is a proven bad idea.

Letting them go ahead and do whatever they like to do is even worse as you
can shut down the company soon afterwards.
An approch that sometimes work (it depends on your relationship) is to
expose the problem (with strong backing/report ...) to them and *ASK*
them what you (you and them as a team) can do. Eventually, they may be
more perceptive about using smart pointers if it is really a good idea
or to find an alternative solution.

It's months ago that I tried this.

Boris
 
B

Boris

[...]Of course, if the other programmers on the team consider
their speculations about unmeasured performance more important
than project time and budget, it really doesn't matter what you
do. You're screwed from the start.

I'm afraid I have to agree. :-/
[...]If you don't trust someone to make a correct choice, you don't
trust him to be able to write correct code. The type of
pointers involved don't change anything.

I'm trying to minimize the risks. You'll never have a team with 100%
perfect people around you. But you'll never let anyone do whatever they
like to do either (it wouldn't deserve to be called team anymore).
[...]I'm not sure I follow. Smart pointers are a low level
implementation technique. They don't free you from having to
consider object lifetime at the design level. When the design
says that a specific smart pointer can do the job, of course,
it's that much less new code you have to write. But my
experience has been that in practice, such cases aren't all that
common (and most of the time, are better handled with garbage
collection, which is even less work).

To make the discussion more technical again: What I like about smart
pointers for example that you know who is actually responsible for the
memory they refer to. If you call a function with a raw pointer or you get
a raw pointer from a function you have to lookup the function definition
or the documentation to know if you (as a caller) can or actually should
free memory and if ownership is passed or not (a problem COM developers
for example face all the time).
[...]Sure. Any time you can factor out commonality is good, and if
there is a commonality in the lifetime management of certain
classes of objects, it's good if you can factor it out into a
common smart pointer. In practice, however, I find that this
just isn't the case that often.

I never used the Boehm collector before. Seeing how easy memory management
is in Java or .NET I can imagine that it might be another helpful tool
indeed.
[...]And my experience is that automatically using just about any
tool results in misuse, and poor code quality. Most pointers in
my code are raw pointers, simply because most pointers are used
for navigation, in one way or another. Most "objects" are local

How do you make sure though that your pointers are not in fact dangling
pointers? Is it something the Boehm collector takes care of?

Boris
 
M

Michael DOUBEZ

Boris a écrit :
[...]Make a programmer use something he is not convinced of is a sure
way to dispirit your team. Showing them you don't trust them to do
their job correctly is a proven bad idea.

Letting them go ahead and do whatever they like to do is even worse as
you can shut down the company soon afterwards.

Keeping people in a project when they resent it has the same result.
The bear or the wolf: choose who will eat you.
It's months ago that I tried this.

I didn't say it was a silver bullet. I have had, in the past, this kind
of situation; it is hard work.
I don't think negociation or more technical point will get you anywhere
since you already got a "no".
Have you tried to speak to the leader of the group and ask him to
perform some benchmark (diverting 2 days off the project's budget) ?
If you can sway him and your technical points are valid, you may get the
lever you need.

Michael

NOTE: this is OT here. I tried to answer by mail but your address is
unknown.
 
R

Roland Pibinger

One library I have found surprisingly useful is a C++ directory tree.
The thing was thrown together basically for fun but since writing it I
have found a huge variety of uses.
The tree has one interesting characteristic, which is that you can
throw any type of data into it
The tree is comprised of various types of nodes, each which carries a
unique id in its directory level.

Seems to be a tree structure (like e.g. a DOM tree). I'd implement it
in such a way that each node creates and ownes its child nodes. When
the root nodes goes out of scope all nodes are deleted in classic RAII
manner. Since ownership is never returned or transferred 'smart
pointers' need not be taken into consideration.
 
J

James Kanze

[...]I'm not sure I follow. Smart pointers are a low level
implementation technique. They don't free you from having to
consider object lifetime at the design level. When the design
says that a specific smart pointer can do the job, of course,
it's that much less new code you have to write. But my
experience has been that in practice, such cases aren't all that
common (and most of the time, are better handled with garbage
collection, which is even less work).
To make the discussion more technical again: What I like about smart
pointers for example that you know who is actually responsible for the
memory they refer to.

What I don't like about using smart pointers systematically (as
opposed to using them as a solution to a specific problem in
specific cases) is that they force an object lifetime model on
you, which in most cases is not the right one. As I said
earlier, most pointers are for navigation. You do NOT,
normally, return a pointer, or pass a pointer, and expect
whoever receives it to delete it. Except for specific cases,
like factory functions. And then, the (exceptional) fact that
you have to delete it is so intimately tied up with the
fundamental semantics of the function, that it's inconceivable
that the caller is unaware of this fact. (But factory functions
often should return an std::auto_ptr, to ensure exception
safety until the pointer is in its final destination, and under
control of whatever should finally control its lifetime.)
If you call a function with a raw pointer or you get
a raw pointer from a function you have to lookup the function definition
or the documentation to know if you (as a caller) can or actually should
free memory and if ownership is passed or not (a problem COM developers
for example face all the time).

I don't know how COM handles this, but the normal convention is
that if you receive a raw pointer, you don't have to worry about
lifetime. Too much. If you have a pointer to an entity object
which manages its own lifetime, you do have to make arrangements
to be informed if the object ceases to exist.
[...]Sure. Any time you can factor out commonality is good, and if
there is a commonality in the lifetime management of certain
classes of objects, it's good if you can factor it out into a
common smart pointer. In practice, however, I find that this
just isn't the case that often.
I never used the Boehm collector before. Seeing how easy
memory management is in Java or .NET I can imagine that it
might be another helpful tool indeed.

Very. There's a proposal to make something similar part of C++
in the next edition of the standard.
[...]And my experience is that automatically using just about any
tool results in misuse, and poor code quality. Most pointers in
my code are raw pointers, simply because most pointers are used
for navigation, in one way or another. Most "objects" are local
How do you make sure though that your pointers are not in fact
dangling pointers? Is it something the Boehm collector takes
care of?

Not directly. It does ensures that the memory won't be reused
for anything else as long as you have a pointer to it, so it's
possible to add a flag to the memory, which you assert each time
you use the pointer. But in general, you still need to use the
observer pattern, and notify all clients of the object anytime
the object ceases to exist.
 
K

kwikius

Seems to be a tree structure (like e.g. a DOM tree). I'd implement it
in such a way that each node creates and ownes its child nodes. When
the root nodes goes out of scope all nodes are deleted in classic RAII
manner. Since ownership is never returned or transferred 'smart
pointers' need not be taken into consideration.

Well, I did a raw pointer port. Its attractive since shared_ptr is my
last dependency on the boost library. The actual number of deletes I
had to write was one, which was in a loop in the container(template)
node dtor.

That only leaves one problem which is the root node. You can either
declare it on the stack or new it on the heap, though then you have to
remember to delete. This does amount to a problem, since exceptions
are quite a frequent occurence, due to say trying to access nodes that
don't exist so you now have to think about management.

Declaring the root on the stack means you have to use a different
syntax , e.g &root rather than root, and it is slightly less flexible
as it is more difficult to modify the code, if say you want to make
the old root a sub node of some new super-root. This is quite possible
e.g in a GUI menu-tree.

I suspect that porting is easier than starting from scratch. IIRC I
did get one assert form the original when trying to access an empty
ptr.

For this application the differences are relatively trivial, but you
can see them starting to mount up in a more complex application and
mistakes happening. IOW I think there is an advantage in the smart
pointer from the viewpoint of simplicity, but for performance and
memory use raw pointers would definitely be far superior, though I
havent benchmarked it.

regards
Andy Little
 
R

Roland Pibinger

That only leaves one problem which is the root node. You can either
declare it on the stack or new it on the heap, though then you have to
remember to delete. This does amount to a problem, since exceptions
are quite a frequent occurence, due to say trying to access nodes that
don't exist so you now have to think about management.

This is not your problem. You provide an interface that can be used
without dynamic allocation. When a few users 'new' the root node (for
whatever reasons) it's their task to provide proper cleanup code.
Declaring the root on the stack means you have to use a different
syntax , e.g &root rather than root, and it is slightly less flexible
as it is more difficult to modify the code, if say you want to make
the old root a sub node of some new super-root. This is quite possible
e.g in a GUI menu-tree.

Your interface currently is a little Boost-like. Try to refactor it to
a more user-friendly form.
 
I

Ian Collins

Roland said:
Seems to be a tree structure (like e.g. a DOM tree). I'd implement it
in such a way that each node creates and ownes its child nodes. When
the root nodes goes out of scope all nodes are deleted in classic RAII
manner. Since ownership is never returned or transferred 'smart
pointers' need not be taken into consideration.
Ownership may not be transfered (except when importing nodes form a
document fragment) when manipulating a DOM, but the are often multiple
references to the same node (node lists and named node maps) in
existence, a case where a reference counted pointer type is a natural fit.
 
K

kwikius

This is not your problem. You provide an interface that can be used
without dynamic allocation. When a few users 'new' the root node (for
whatever reasons) it's their task to provide proper cleanup code.

Its interesting. If I use a smart pointer for the root node, then the
whole tree is despite the term pointer, in fact an object that cleans
itself up. Its not my or the users problem

If OTOH for raw pointers I use a newed pointer for the root rather
than putting on the stack, then the simplicity is gone.

And I don't like declaring on the stack because this makes the root
semantics different to the rest.

Overall I'm erring back in favour of the smart pointer approach,
beacause with the raw pointer version I'm starting to have to think
about this and that, where it was all pretty simple previously.
Your interface currently is a little Boost-like. Try to refactor it to
a more user-friendly form.

I'm not sure if you are referring to the use of free functions?

I'm not claiming its pretty anyway or high performance etc. Its just a
hack really at the moment. As usual though.. hack in haste... repent
at leisure :)

regards
Andy Little
 

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,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top