printing in C++

Ö

Öö Tiib

Also, a raw pointer is only needed if it can be NULL or must support
reseating, otherwise one should use references to reduce the complexity
of the code. For example, navigation could often be done via references
only. Not quite sure if Tiib or James included references in the notion
of naked pointers or not.

Reference is not pointer because it is not so loose. Reference is great
as function parameter or for naming and extending life-span of temporary
in code. As data member it is useful if there is need to store a reference
to a composite in component. The only dangerous usage of reference is as
return value and that is fault of compilers. All cases of returning
reference to a local or temporary must be very apparent to compilers,
so compiling such things silently feels criminal. ;)
 
R

Rui Maciel

Öö Tiib said:
Why? For navigation there are usually iterators.

Iterators aren't automatically generated in custom classes/containers. In
those cases, pointers are either used directly (no need to waste time adding
and debugging custom iterators/generators) or indirectly (shoved under the
hood).

Plus, pointers are a fundamental type. Increasing the complexity of a piece
of software is only justified if something really good can come out of it.
When complexity is mindlessly added for no reason whatsoever and with
dubious returns then we are wasting our time (and other people's time as
well) with counterproductive practices.

People involved in software development should always bear in mind that
ultimately the job of a software developer is to manage complexity.
Mindlessly advocating alternatives which are needlessly complex over the
simplest alternatives may well be the exact opposite of what is expected
from a competetent software developer.


Rui Maciel
 
Ö

Öö Tiib

Iterators aren't automatically generated in custom classes/containers. In
those cases, pointers are either used directly (no need to waste time adding
and debugging custom iterators/generators) or indirectly (shoved under the
hood).

Iterator on common case is not "shoving pointer under hood" but "erasing all the
broken interface" of pointer and "replacing it with useful interface". It is well
worth the time and the tests are trivial. Not doing it is like not erasing copy
constructor and assignment operator from a class that is not meant to be
copyable.
Plus, pointers are a fundamental type. Increasing the complexity of a piece
of software is only justified if something really good can come out of it..
When complexity is mindlessly added for no reason whatsoever and with
dubious returns then we are wasting our time (and other people's time as
well) with counterproductive practices.

Complexity can not be added by erasing complexity. Complexity can be added
by not following rule of three. Complexity can be added by not following RAII.
Complexity can be added by not caring about thread or exception safety.
Complexity can be added by exposing naked pointers.
People involved in software development should always bear in mind that
ultimately the job of a software developer is to manage complexity.

Exactly. Easiest way to manage it is to erase most things that mean undefined
behavior from the interface of a class or a module. Every pointer has majority
of its interface either very confusing or undefined behavior.
Mindlessly advocating alternatives which are needlessly complex over the
simplest alternatives may well be the exact opposite of what is expected
from a competetent software developer.

Exactly. Mindlessly advocating exposure of needlessly complex and error prone
constructs is unexpected from "competetent" software developer. Pointer is the
thing in C++ that is on most cases the very reason behind software defects.
 
R

Rui Maciel

Öö Tiib said:
Iterator on common case is not "shoving pointer under hood" but "erasing
all the broken interface" of pointer

This is obviously false, because even standard C++ iterators were defined
with the express purpose of mimicking C++ pointers.
and "replacing it with useful
interface". It is well worth the time and the tests are trivial. Not doing
it is like not erasing copy constructor and assignment operator from a
class that is not meant to be copyable.

Its cruft that needlessly needs to be added, and an extra abstraction layer
that needs to be tested in validated. Worse, it's one which is designed to
mimic the pointer syntax. So, in essence, it requires time and effort to
end up precisely where you were already. That's pretty much the definition
of an anti-pattern.

In addition, C++11 introduced standard language features which already
support fundamental data types, such as pointers and references, as
iterators.

Complexity can not be added by erasing complexity.

You don't erase complexity by shoving cruft to a module by needlessly
implementing interfaces which are redundant and useless.

Exactly. Easiest way to manage it is to erase most things that mean
undefined behavior from the interface of a class or a module. Every
pointer has majority of its interface either very confusing or undefined
behavior.

Pointers are one of C++'s fundamental types. They are one of the first
things that are taught to C++ programmers in any programming101 course,
right after introducing hello world. References are more complex and are
more nuanced than pointers, and nevertheless newbies tend to grasp the
basics with little to no effort whatsoever.

If someone who claims to be a C++ programmer states that they find pointers
to be "very confusing" then that person's competency is rather questionable.

Exactly. Mindlessly advocating exposure of needlessly complex and error
prone constructs is unexpected from "competetent" software developer.
Pointer is the thing in C++ that is on most cases the very reason behind
software defects.

There are bugs of all sorts. If someone managed to find a bug which
involved iterators, that would say nothing about about iterators as a
feature.

You may find pointers to be "very confusing", and thus prefer to run away
from them like they were the plague instead of simply doing what every C++
did at one point in his life and learn about them. Yet, that doesn't mean
that somehow they are the bane of every living C++ programmer. Far from it.
In some cases there might be alternatives which are more appropriate, but in
other cases using a pointer is perfectly fine, and it may even be the best
possible way to go.

Knowing what we're doing is always a better option than basing a set of
beliefs in a set of arguments which are entire irrational and baseless.


Rui Maciel
 
Ö

Öö Tiib

This is obviously false, because even standard C++ iterators were defined
with the express purpose of mimicking C++ pointers.

Are you joking? Why to make more pointers?
Its cruft that needlessly needs to be added, and an extra abstraction layer
that needs to be tested in validated.

Erasing constructor and assignment operator from non-copyable is cruft or
adding iterators to containers is cruft?
You don't erase complexity by shoving cruft to a module by needlessly
implementing interfaces which are redundant and useless.

Go propose then to erase that "cruft" from standard library.
If someone who claims to be a C++ programmer states that they find pointers
to be "very confusing" then that person's competency is rather questionable.

I did not say that pointers are confusing. Read again.

Every pointer is used in one of several roles. Pointer that is used for storing
address of first element of array, pointer that is used for storing addressof
optional object, pointer that is used to store address to polymorphic interface
of object, etc. I said that if pointer is in one of such roles then most ofits
*interface* (meant for other roles) is very clearly *undefined* *behavior* or
its usage is very confusing when it is not undefined behavior.

For example if 'p' is pointer to polymorphic base sub-object then yes you can
convert it to reference to that sub-object by using 'p[0]' but that is very
confusing to anyone reading your code.
Knowing what we're doing is always a better option than basing a set of
beliefs in a set of arguments which are entire irrational and baseless.

It is you who push here emotional nonsense about how non-mindless
competent, rational, adequate and what not you are without iterators and
how pointless, mindless, irrational and baseless cruft are iterators and
anyone suggesting those.

Take a breath, look at things clearly for a moment. Iterators are fine.
Iterators do not mimic pointers. Iterators are for navigation in containers.. I
do not know where iterators did hurt you but ... your iteratorphobia is
quite ... uncommon.
 
J

James Kanze

One of the most common constructs in my code is containers of smart
pointers, so I don't see from where you derive that generalisation.

What's a legitimate use case for containers of smart pointers?
I can think of one, off hand, but it's a very special case, only
relevant when implementing Excel plug-ins. There are a few
others. But by far the most frequent use of containers of
pointers is for navigation, in which case, raw pointers are more
appropriate.

In general, use of smart pointers is an advanced technique.
std::shared_ptr is particularly tricky to use correctly; I tend
to avoid it except in special circumstances.
 
J

James Kanze

Why? For navigation there are usually iterators. While standard allows
raw pointers as iterators all modern implementations use special
classes that are as efficient and lot safer.

How can you navigate using iterators. The target objects aren't
necessarily in any particular sequence.
Pointer has several usages: container (array), iterator of container,
optional object and for polymorphic object. It is best alternative
for none of the tasks.

Pointers are mainly used for navigation (or not used at all).
There are exceptions: polymorphic objects, and iterating over
C style arrays, and optional arguments (or unsuccessful return
values) are good examples. In the case of temporary polymorphic
objects, smart pointers often are appropriate. But certainly
*not* in the other two cases. (And short lived polymorphic
objects aren't that common.)
 
I

Ian Collins

James said:
What's a legitimate use case for containers of smart pointers?
I can think of one, off hand, but it's a very special case, only
relevant when implementing Excel plug-ins. There are a few
others. But by far the most frequent use of containers of
pointers is for navigation, in which case, raw pointers are more
appropriate.

In my case, XML DOM and JSON objects.

I also do a lot of parsing and processing with LDAP data. In the this
case, entries are shared between containers,
In general, use of smart pointers is an advanced technique.
std::shared_ptr is particularly tricky to use correctly; I tend
to avoid it except in special circumstances.

It is the most convenient tool to implement DOM, where nodes can appear
in multiple lists.
 
Ö

Öö Tiib

How can you navigate using iterators. The target objects aren't
necessarily in any particular sequence.

Some thoughts of mine ...
* Most numerous objects are not just freely floating around but
are in containers.
* There must be some way to sequence the objects of application.
Otherwise it may be difficult to serialize, unserialize, dump,
roll back, undo, redo and the like.
* Classes that navigate all over the place (like Visitor) usually use
references. like 'visit(CocncreteObject&)' to avoid pointless
nullptr navigations.
* The objects whose life-time must end exactly at certain
moment are better in intrusive containers. The containers of raw
pointers are less suitable since the object has to have knowledge
of being in those for to exit those. With intrusive containers it
has such knowledge more naturally.
Pointers are mainly used for navigation (or not used at all).
There are exceptions: polymorphic objects, and iterating over
C style arrays, and optional arguments (or unsuccessful return
values) are good examples. In the case of temporary polymorphic
objects, smart pointers often are appropriate. But certainly
*not* in the other two cases. (And short lived polymorphic
objects aren't that common.)

I see no problems with polymorphic objects and smart pointers.
There must be some special issue that I haven't met.
C style array is not needed with std::array<>. Need for optional
arguments can be first reduced with overloads that do not have
the argument. The smart pointers are as optional as naked
pointers. There are various "optional" and "fallible" templates
too for pass-by-value cases. Very rarely happening failures
can be cheaper to signal using exceptions.
 
J

James Kanze

On 3/18/2013 6:20 PM, James Kanze wrote:
When you want a polymorphic collection.

That is a good reason, although most of the time, I suspect that
Boost's pointer collections would be better. (They sound
better---I've not actually been in a situation to try them and
see.)
 
J

James Kanze

James Kanze wrote:
In my case, XML DOM and JSON objects.

I'm not familiar with DOM or JSON, but I don't really seen much
use for them in XML (but then, the XML itself is handled by
Xerces---I don't know what it uses internally).
I also do a lot of parsing and processing with LDAP data. In the this
case, entries are shared between containers,

I'm not sure I understand. The LDAP model is a tree, not a DAG.
Are you saying that you are mapping it to a DAG.
It is the most convenient tool to implement DOM, where nodes can appear
in multiple lists.

Does DOM allow cycles, or only DAG's?
 
J

James Kanze

Some thoughts of mine ...
* Most numerous objects are not just freely floating around but
are in containers.

I'm not sure what you mean by "just freely floating around". If
the object isn't "freely floating around" in some sense of the
word, there's no need to allocate it dynamically (usually).

There are often containers with pointers to the object, to
implement 1->n relationships. But these would contain raw
pointers.
* There must be some way to sequence the objects of application.
Otherwise it may be difficult to serialize, unserialize, dump,
roll back, undo, redo and the like.

You create temporary collections of objects on an as needed
basis. You have to be able to navigate to the objects. But
this is best done, once again, using raw pointers.
* Classes that navigate all over the place (like Visitor) usually use
references. like 'visit(CocncreteObject&)' to avoid pointless
nullptr navigations.

I'm not talking about the sort of navigation vistors do. These
are short lived objects, and I don't think it would occur to
anyone to use smart pointers here. In the unlikely case where
you might want to assign vistors, then you might use raw
pointers, so that you could support assignment, but generally,
vistors are polymorphic and noncopyable, so references are just
fine.
* The objects whose life-time must end exactly at certain
moment are better in intrusive containers. The containers of raw
pointers are less suitable since the object has to have knowledge
of being in those for to exit those. With intrusive containers it
has such knowledge more naturally.

The object obviously has to know about all of the objects which
reference it, in order to inform them of its demise. This is
the observer pattern. I've yet to find any smart pointer which
handles this correctly. Espcially as the objects which
reference it will usually have other actions to take when it
disappears (e.g. switch to a backup).
I see no problems with polymorphic objects and smart pointers.

That's what I said. For short lived object (e.g. temporary
agents), reference counted pointers are fine; this is where
I first used them (about 20 years ago), and this is one of the
few places where I still use them systematically. In many ways,
logically, it would make more sense to copy the objects, but
copy and polymorphism don't work well together.
There must be some special issue that I haven't met.
C style array is not needed with std::array<>.

String literals are *not* std::array<> (nor std::string). And
std::array<> doesn't allow the compiler to count the
initializers, and determine the length without me counting them.
In my own code, C style arrays will still be more frequent than
std::array. (But this probably is application dependent.)
 
B

Balog Pal

That is a good reason, although most of the time, I suspect that
Boost's pointer collections would be better. (They sound
better---I've not actually been in a situation to try them and
see.)

I normally use auto_ptrDC, my own variant of std::auto_ptr with the same
interface but with deep-copy semantics; having policy for deleter and
cloner. My polymorphic types support clone() or equivalent so the
resulting thing works almost like a usual collection.

Without that I could probably use shared_ptr for the cases, where copy
is not really used, otherwise would look around in boost too first.
 
Ö

Öö Tiib

I'm not sure what you mean by "just freely floating around". If
the object isn't "freely floating around" in some sense of the
word, there's no need to allocate it dynamically (usually).

I mean that if the objects are not polymorphic and are numerous then they
are usually directly in container (like 'std::vector<T>' ). If the objects
are polymorphic and numerous then they can be in intrusive containers or
in pointer containers. Pointer container can be special (like boost::ptr_vector<T>') or standard (like 'std::vector<std::unique_ptr<T>>').
The life-time of object is bound to such containers but most numerous
objects usually have their lifetime bound to something else.
There are often containers with pointers to the object, to
implement 1->n relationships. But these would contain raw
pointers.

Depends on type of relation. Components can be in container of
std::unique_ptr or boost::ptr_somethings and contain reference
to composite. Other 1->N associations can be made using
containers of std::shared_ptr, std::weak_ptr or boost::intrusive
containers.
You create temporary collections of objects on an as needed
basis. You have to be able to navigate to the objects. But
this is best done, once again, using raw pointers.

What sort of navigation you mean? I usually use visitor or memento
pattern and those do not use pointers but either references or
templates. For observers I use slots and signals, current favorite
is boost::signals2. In interface they use references and function
pointers (I do not mind function pointers).
I'm not talking about the sort of navigation visitors do.

I am still unsure what sort of navigation you mean. Visitors navigate
fine with references. Containers navigate fine with iterators.
Polymorphic objects are fine with smart pointers. Observers work fine
with references and functions.
The object obviously has to know about all of the objects which
reference it, in order to inform them of its demise. This is
the observer pattern. I've yet to find any smart pointer which
handles this correctly. Espcially as the objects which
reference it will usually have other actions to take when it
disappears (e.g. switch to a backup).

std::vector can not be made observer of objects that it contains. So there
must be some sort of helper object that manages it. Now lets look
at boost::intrusive::auto_unlink_hook. The element will be removed from
a container when element is destroyed and no characters need to be typed. If
it is in several containers then it has to have several hooks. Very tidy.
Note that the intrusive containers can be polymorphic without pointers
needed and lifetime of object is not bound to lifetime of container in any
way.
That's what I said. For short lived object (e.g. temporary
agents), reference counted pointers are fine; this is where
I first used them (about 20 years ago), and this is one of the
few places where I still use them systematically. In many ways,
logically, it would make more sense to copy the objects, but
copy and polymorphism don't work well together.

Polymorphic objects are usually not copy-able but often clone-able
and are fine with smart pointers.

One usage (may-be worth mentioning) are opaque pointers (or more often
called "handles"). The smart pointers of C++11 produce such things also
very well (unlike std::auto_ptr).
String literals are *not* std::array<> (nor std::string).

std::string has iterators. The string literals can be used enwrapped, that
was already discussed this thread. I can not publish mine (ownership
issues) but these two ... string_literal from proposed boost log ...:
http://boost-log.svn.sourceforge.ne...y/string_literal.hpp?revision=848&view=markup

.... and StringRef from LLVM...:
http://www.llvm.org/docs/ProgrammersManual.html#llvm-adt-stringref-h
http://www.llvm.org/doxygen/classllvm_1_1StringRef.html

.... look more or less like it.
And std::array<> doesn't allow the compiler to count the
initializers, and determine the length without me counting them.

Ok, that is downside. Feels that C++ initialization is in half-made
shape at the moment. Too major leap in C++11. It will be fixed I
hope.
In my own code, C style arrays will still be more frequent than
std::array. (But this probably is application dependent.)

I have to admit that I also have several C style arrays that are
entirely immutable. It is because they work and I have other things to
worry about.

I am not saying that things can not be made with naked pointers. I know
very lot of developers who use C as their choice of language. They just
make smaller applications.

Efficiency of the C++ alternatives to naked pointers is fine and safety
is better. Only shared_ptr and weak_ptr have overhead, other things are
as good as naked pointers. Safety is important because better developers
are very heavily head-hunted. Less experienced guys are more productive
and make less mistakes without having to deal with naked pointers.
 
I

Ian Collins

James said:
I'm not familiar with DOM or JSON, but I don't really seen much
use for them in XML (but then, the XML itself is handled by
Xerces---I don't know what it uses internally).

I have been using my own parser since the latter part of the last
century! All of the DOM elements are extensions of my even older shared
pointer implementation. DOM specifies that modifications to nodes
extracted from the document modify the document. So shared pointer
objects are the best fit for a C++ implementation. You could use
pointers, but I preferred to pass and return nodes by value, as they are
in PHP and JavaScript.
I'm not sure I understand. The LDAP model is a tree, not a DAG.
Are you saying that you are mapping it to a DAG.

Dag had a different meaning in this part of the wold!

The source is a tree, but I have to generate several outputs tress from
one input.
Does DOM allow cycles, or only DAG's?

Off hand I can't think of a way to introduce cycles into a DOM.
 
J

James Kanze

I mean that if the objects are not polymorphic and are numerous then they
are usually directly in container (like 'std::vector<T>' ). If the objects
are polymorphic and numerous then they can be in intrusive containers or
in pointer containers. Pointer container can be special (like boost::ptr_vector<T>') or standard (like 'std::vector<std::unique_ptr<T>>').
The life-time of object is bound to such containers but most numerous
objects usually have their lifetime bound to something else.

There are objects and there are objects. Value objects, which
can be copied, are not a problem, since you don't normally have
pointers (smart or raw) to them, anywhere. Entity objects do
have identity, and you can (usually) navigate between them.
They normally have their own lifetime, defined by the
application logic, however, and aren't in containers, per se.
(Pointers to them may be in containers, for navigation purposes,
of course.)
Depends on type of relation. Components can be in container of
std::unique_ptr or boost::ptr_somethings and contain reference
to composite. Other 1->N associations can be made using
containers of std::shared_ptr, std::weak_ptr or boost::intrusive
containers.

None of these smart pointers implement relationships correctly.
A raw pointer does, sort of. (You still need an observer,
because there's a good chance that an object which has a pointer
to you will want to know when you are destructed.)
What sort of navigation you mean? I usually use visitor or memento
pattern and those do not use pointers but either references or
templates. For observers I use slots and signals, current favorite
is boost::signals2. In interface they use references and function
pointers (I do not mind function pointers).

Navigation, like going from one object to another. Implementing
the application specific relationships between objects. In
a GUI, for example, a container will know about its
sub-components, the sub-components will know about the
container. You can navigate in either direction (and in some
cases, accross).
I am still unsure what sort of navigation you mean. Visitors navigate
fine with references. Containers navigate fine with iterators.
Polymorphic objects are fine with smart pointers. Observers work fine
with references and functions.

Observers are a perfect example. Observers can't use
references, since they have a dynamic lifetime. In a typical
implementation, the Observee will have a vector of raw pointers
to the Observers. How else could you implement it. You can't
use references (a vector can't contain references). You can't
use unique_ptr (because the observer objects exist outside of
the observee). You can't use shared_ptr (because the observer
objects manage their own lifetime, and will auto-destruct). You
can't use weak_ptr, because there cannot be any shared_ptr to
the object. You can't use iterators, because the object itself
isn't in any container.

A long time ago, I worked with a firm which had designed
a managed_ptr; a pointer which was, itself, an observer, and
reset itself to null when the pointed to object was deleted.
This was designed to solve a very special case, and worked well
there, but experimentation soon revealed that it wasn't
a general solution. Whoever had a pointer to the object usually
needed to know when the object was destructed anyway, so it
could reconfigure itself to function without the object. And
once you have the observer, any additional baggage just to
manage the pointer is just added complexity.
std::vector can not be made observer of objects that it contains.

Of course not. std::vector is a low level implementation
detail. It only exists to be used by higher level objects.
So there must be some sort of helper object that manages it.

Obviously. An application isn't defined in terms of
std::vector.
Now lets look
at boost::intrusive::auto_unlink_hook. The element will be removed from
a container when element is destroyed and no characters need to be typed.If
it is in several containers then it has to have several hooks. Very tidy.
Note that the intrusive containers can be polymorphic without pointers
needed and lifetime of object is not bound to lifetime of container in any
way.

It might help in a few cases, but it still doesn't solve the
larger problem. Removing the pointer from a vector or a map is
one thing; telling the object which contained the vector or map
that something is happening that it needs to know about is
another. And once you've solved this second problem, you don't
need a solution specialized in removing the pointer from
a container.

And intrusive containers suppose that the object knows about all
of the containers it might be inserted into. Which is
fundamentally impossible, because it is an open set.

[...]
Polymorphic objects are usually not copy-able but often clone-able
and are fine with smart pointers.

Some polymorphic objects, yes. Larger, entity objects will
contain pointers to other objects, which in turn contain
pointers back to the original object, and have their own
specific lifetimes.
One usage (may-be worth mentioning) are opaque pointers (or more often
called "handles"). The smart pointers of C++11 produce such things also
very well (unlike std::auto_ptr).

The smart pointers of C++ are *not* in any way opaque. In my
external interfaces, I make extensive use of opaque pointers:
void*. That's the only opaque pointer in C++. (But of course,
I don't want opaque pointers internally. They're only used for
information hiding at the boundary level.)

[...]
I have to admit that I also have several C style arrays that are
entirely immutable. It is because they work and I have other things to
worry about.

Most of the time (in my applications, at least), if I'm not
using std::vector, it's because I have a static array with
static initializers, and I want the compiler to count the
initializers for me. Replacing them with std::array won't work.
(And yes, they're always const.)

In other applications... The cost of using an std::vector with
three elements) for the coordinates of a Point3D, when you have
thousands of them, and are constantly copying them, could be
prohibitive. In this case, std::array could be an option
(although I'm not sure what it really buys you over a C style
array with 3 elements).
I am not saying that things can not be made with naked pointers. I know
very lot of developers who use C as their choice of language. They just
make smaller applications.

The applications I'm talking about where you need mostly raw
pointers tend to be the larger ones.
Efficiency of the C++ alternatives to naked pointers is fine and safety
is better.

It depends on what you're doing. shared_ptr is extremely
dangerous if used inappropriately. And naked pointers are
perfectly safe when used for navigation.
Only shared_ptr and weak_ptr have overhead, other things are
as good as naked pointers. Safety is important because better developers
are very heavily head-hunted. Less experienced guys are more productive
and make less mistakes without having to deal with naked pointers.

The problem is that smart pointers are in many cases *more*
dangerous than naked pointers. (Especially with auto. Consider
what happens with:

auto objPtr = somePtrVector;

If somePtrVector contains unique_ptr, you're in for a surprise.)
 
Ö

Öö Tiib

There are objects and there are objects. Value objects, which
can be copied, are not a problem, since you don't normally have
pointers (smart or raw) to them, anywhere. Entity objects do
have identity, and you can (usually) navigate between them.
They normally have their own lifetime, defined by the
application logic, however, and aren't in containers, per se.
(Pointers to them may be in containers, for navigation purposes,
of course.)

So major objects that have complex state machine have usually lot
of other things to worry about. I was more about the typical things
that have very narrow set of responsibilities. Those are the most
numerous ones. Not the "device" but its "parameters", not an "account"
but its "transactions", not the "dialog" but its "controls".
Polymorphic, many to one, with their life-time bound to the owner.
None of these smart pointers implement relationships correctly.
A raw pointer does, sort of. (You still need an observer,
because there's a good chance that an object which has a pointer
to you will want to know when you are destructed.)

Observer pattern is not needed always. Typically it is needed in
places where wills of several active parts meet. Observer pattern
helps to orchestrate events in such places greatly. Most of code
still deals with far more mundane issues. I understand that we
agree that on these mundane cases smart pointers save lot of time.
Navigation, like going from one object to another. Implementing
the application specific relationships between objects. In
a GUI, for example, a container will know about its
sub-components, the sub-components will know about the
container. You can navigate in either direction (and in some
cases, accross).

You mean sub-components in GUI ... like shapes on diagram? There
is strict ownership relation, shapes can *not* continue being when
the diagram is closed. So diagram could have shared_ptr and all
the rest parts who are interested in particular shape a weak_ptr.

Anyway ... I see your point, weak_ptr has inconvenient interface
and carries a slight inefficiency burden with it so if pointing object
anyway tracks pointed at object's life-time by other, not-ref-counted
means then here is waste.

You made me inspired to write a non-owning 'track_ptr' that is
better integrated to the logic of such "other, not-ref-counted
means".

I already have 'robust_ptr' that is like 'unique_ptr' used for
polymorphic base object but in all situations when 'unique_ptr'
is nullptr it instead points at static (internally immutable)
"Missing" object that implements the polymorphic interface. As
result ... it may be always dereferenced.

I will couple the 'track_ptr' as optional side-kick of my
'robust_ptr' because the effect is great so far. Then diagram could
have 'robust_ptr' to shape and all the rest could have 'track_ptr'.

'unique_ptr' seems is currently also lacking such optional non-owning
side-kick. I have used reference or std::reference_wrapper to object
so far.
Observers are a perfect example. Observers can't use
references, since they have a dynamic lifetime. In a typical
implementation, the Observee will have a vector of raw pointers
to the Observers. How else could you implement it.

Observee has set of signals. Usually a fixed set signaling about
interesting events like state transitions.
Each signal may have some slots (function objects related to Observer)
connected. Connections may be optionally made to track Observer using
'weak_ptr'. If to add 'track_ptr' then it becomes "either with 'weak_ptr'
or with 'track_ptr'".
You can't
use references (a vector can't contain references). You can't
use unique_ptr (because the observer objects exist outside of
the observee). You can't use shared_ptr (because the observer
objects manage their own lifetime, and will auto-destruct). You
can't use weak_ptr, because there cannot be any shared_ptr to
the object. You can't use iterators, because the object itself
isn't in any container.

Ok, in the building blocks the raw pointers are always needed, be it
iterator, container or smart pointer. Such building blocks are written by
the brightest guys and those use various concurrency and exception
safety testing frameworks for testing and profile very well, so ... I am
not worried that things like shared_ptr somehow fail me. I am trying to
move the raw pointers into such template libraries, away from abstraction
layer of ordinary C++ Joe.
A long time ago, I worked with a firm which had designed
a managed_ptr; a pointer which was, itself, an observer, and
reset itself to null when the pointed to object was deleted.
This was designed to solve a very special case, and worked well
there, but experimentation soon revealed that it wasn't
a general solution. Whoever had a pointer to the object usually
needed to know when the object was destructed anyway, so it
could reconfigure itself to function without the object. And
once you have the observer, any additional baggage just to
manage the pointer is just added complexity.

When the relation is non-owning and lifetime is unrelated then non
-owning pointer like 'weak_ptr' is best. I see your point about
missing smart pointer now very well. 'shared_ptr' is for cases
when ownership is shared. When there is single owner then it is
(usually insignificant) waste. Unresolved Problem is that weak_ptr
is awkward to use and so its usage adds to complexity.
Of course not. std::vector is a low level implementation
detail. It only exists to be used by higher level objects.

Ok, but I am still big fan of intrusive containers. It works well on
cases when there are no observation needed.
Obviously. An application isn't defined in terms of
std::vector.


It might help in a few cases, but it still doesn't solve the
larger problem. Removing the pointer from a vector or a map is
one thing; telling the object which contained the vector or map
that something is happening that it needs to know about is
another. And once you've solved this second problem, you don't
need a solution specialized in removing the pointer from
a container.

Yes, it does not solve the cases where you actually need signals
for some other reason. It just is very efficient even when there
are signals for some other reason. Element of intrusive container
knows exactly where it is (if it is), so erasing itself is O(1).
And intrusive containers suppose that the object knows about all
of the containers it might be inserted into. Which is
fundamentally impossible, because it is an open set.

That is untrue in C++. We have always finite number of classes and
therefore we have finite number of 1->n relations. It is by design
of language, we can't add classes (and so relations) between
classes dynamically, run time.

When there is B n<->m C relation then that clearly indicates
under-engineering. Class X that carries the relation between B and C
is missing. While this may be actually case in real code (who has not
seen broken designs?) I would certainly deal with it by adding missing X,
not by cleverly using raw pointers.
[...]
Polymorphic objects are usually not copy-able but often clone-able
and are fine with smart pointers.

Some polymorphic objects, yes. Larger, entity objects will
contain pointers to other objects, which in turn contain
pointers back to the original object, and have their own
specific lifetimes.

Ok, I see that.
The smart pointers of C++ are *not* in any way opaque. In my
external interfaces, I make extensive use of opaque pointers:
void*. That's the only opaque pointer in C++. (But of course,
I don't want opaque pointers internally. They're only used for
information hiding at the boundary level.)

Yes, and robustness of boundary between modules is very important
topic. If modules are developed by different teams then robustness
of interface helps to dispatch on what side of boundary the problem
lies. That saves lot of time.

'unique_ptr' may point at opaque type where it is constructed but
not where it is destroyed. 'shared_ptr' may point at opaque type
where it is destroyed but not where it is constructed. Non-owning
pointers (like weak_ptr) may be opaque all the time.
[...]
I have to admit that I also have several C style arrays that are
entirely immutable. It is because they work and I have other things to
worry about.

Most of the time (in my applications, at least), if I'm not
using std::vector, it's because I have a static array with
static initializers, and I want the compiler to count the
initializers for me. Replacing them with std::array won't work.
(And yes, they're always const.)

Ok, that is not issue with me. I do same and see no problem. Maybe
some day when I have nothing to do I deal with it just for sake
of purity. The time is usually better spent dealing with real issues
not issues raised from following pure concepts too closely.
In other applications... The cost of using an std::vector with
three elements) for the coordinates of a Point3D, when you have
thousands of them, and are constantly copying them, could be
prohibitive. In this case, std::array could be an option
(although I'm not sure what it really buys you over a C style
array with 3 elements).

On such cases there is an option of having Point class with members
x, y, z and static std::array of pointer-to-member. The x,y,z are
just the coordinates of a Point in room. Like the red, green, blue,
alpha or hue, saturation, lightness, alpha are components that mutually
just describe the Color of something.
The applications I'm talking about where you need mostly raw
pointers tend to be the larger ones.

From small applications that are well integrated with each other one can
make rather complex software. We sometimes forget it when using C++.
It depends on what you're doing. shared_ptr is extremely
dangerous if used inappropriately. And naked pointers are
perfectly safe when used for navigation.

Everything can be misused. Nothing is 100% fool proof. I merely meant safety
of detecting more possible misuses compile-time. That saves time.
The problem is that smart pointers are in many cases *more*
dangerous than naked pointers. (Especially with auto. Consider
what happens with:

auto objPtr = somePtrVector;

If somePtrVector contains unique_ptr, you're in for a surprise.)


What surprise? I expect diagnostic here. same with:

auto objPtr = somePtrVector.front(); // expect diagnostic

§ 20.7.1.2:
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;

Maybe conforming compiler may do something surprising here but how?
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top