Discussion:
Designing with smart pointers
(too old to reply)
Marty Rabens
2007-10-20 15:50:59 UTC
Permalink
Hello all,



First post here. I'm using smart pointers for the first time in a project
(yeah, I know - shame on me, I should have started using them years ago).
Boost smart pointers, to be specific. It's clear to me how they work, but
I'm still trying to get a handle on how to best make use of them. After a
couple decades of coding with raw pointers, it's hard to think in terms of
smart pointers.



Here's the design I'm implementing. I'm using component-based entities. An
entity object is essentially a container of components (e.g., a render
component, a physics component, etc.). Each type of component is managed by
a subsystem for that component type, which keeps track of all components of
that type. For example, the render subsystem knows about all render
components, and when it's time to render, it iterates through all of them.



An entity is created by a factory. The factory is handed an entity
definition with all the info about which components it needs and their
parameters. The factory has the subsystems create the components and
attaches them to the entity.



There is no monolithic container of entities, since everything is handled by
the subsystems.



All components derive from a common interface, and all subsystems derive
from a common interface.



What I'm having trouble with is a clean, elegant design for how everything
keeps references to everything else (using smart pointers), especially when
it comes to destroying an entity.



An entity must have:

- a reference to each of the components attached to it.



A component must have:

- a reference to the entity it's attached to.

- zero or more references to other components attached to the same entity
(I've decided to allow this for performance reasons).



A subsystem must have:

- references to all components of the type it manages.



Other things may also be holding a reference to a component. For example,
if one entity is following another, a component in the follower will have a
reference to a component in the followee. Nothing will ever be holding a
reference to an entity, though (except the components attached to it).



When an entity is destroyed, all attached components need to be destroyed
(eventually), and each component's subsystem needs to remove the component
from its list.



So, can anyone recommend which types of references (shared_ptr, weak_ptr,
etc.) should be used and where, and the process through which an entity and
its components are destroyed? Any tips greatly appreciated.



Thanks,

--Marty
Phlip
2007-10-20 16:31:11 UTC
Permalink
Post by Marty Rabens
So, can anyone recommend which types of references (shared_ptr, weak_ptr,
etc.) should be used and where, and the process through which an entity and
its components are destroyed? Any tips greatly appreciated.
How are your unit tests?

I once added 'heapwalk' to test cases, to walk the heap and examine each
block. The count of in-use blocks before and after dynamic memory operations
had to match.

(This hearkens back to the original AmigaDOS, where you could run a program
and check memory usage from a command line tool to see if it matched before
and after. AmigaDOS had zero garbage collection!)

If you use lots of unit tests, you can change your pointers as your design
matures. Your post gives the impression you can't change such details
easily, so you have only one chance to get them right!
--
Phlip
http://www.oreilly.com/catalog/9780596510657/
"Test Driven Ajax (on Rails)"
assert_xpath, assert_javascript, & assert_ajax
Ola Olsson
2007-10-20 18:34:29 UTC
Permalink
Hi,

Unless I'm missing something it is fairly straightforward, the entity
should keep a strong reference to all components, everything else should
have a weak one.

In the end since the components are parts of your entities, they should
be owned by the entities, thus if you let an entity go it will clean up
its components. (In the same vein you might have the level/sector hold
references on all the entities such that letting that go drops all the
entities).

And on that note it might be useful to let the components de-register
themselves with the systems, in which case you have the opportunity to
use straight pointers inside systems for extra speed benefits, should
the need arise.

I'd make very sure to typedef all references such that you can switch
from pointer to boost::whatever_pointer to you inhouse pointer.

Remember that for reference counting to work you have to have a tree (or
at least acyclic graph) so you have to be very careful to design this.
If you start shoveling reference counted pointers in every orifice you
will have one big nasty memory leak before long.

After all reference counting is about the worst way to do grabage
collection there is, I'm not just aware of any useful alternatives for C++.

from the heights of my head
cheers
.ola
Post by Marty Rabens
Hello all,
First post here. I'm using smart pointers for the first time in a
project (yeah, I know - shame on me, I should have started using them
years ago). Boost smart pointers, to be specific. It's clear to me how
they work, but I'm still trying to get a handle on how to best make use
of them. After a couple decades of coding with raw pointers, it's hard
to think in terms of smart pointers.
Here's the design I'm implementing. I'm using component-based
entities. An entity object is essentially a container of components
(e.g., a render component, a physics component, etc.). Each type of
component is managed by a subsystem for that component type, which keeps
track of all components of that type. For example, the render subsystem
knows about all render components, and when it's time to render, it
iterates through all of them.
An entity is created by a factory. The factory is handed an entity
definition with all the info about which components it needs and their
parameters. The factory has the subsystems create the components and
attaches them to the entity.
There is no monolithic container of entities, since everything is
handled by the subsystems.
All components derive from a common interface, and all subsystems derive
from a common interface.
What I'm having trouble with is a clean, elegant design for how
everything keeps references to everything else (using smart pointers),
especially when it comes to destroying an entity.
- a reference to each of the components attached to it.
- a reference to the entity it's attached to.
- zero or more references to other components attached to the same
entity (I've decided to allow this for performance reasons).
- references to all components of the type it manages.
Other things may also be holding a reference to a component. For
example, if one entity is following another, a component in the follower
will have a reference to a component in the followee. Nothing will ever
be holding a reference to an entity, though (except the components
attached to it).
When an entity is destroyed, all attached components need to be
destroyed (eventually), and each component's subsystem needs to remove
the component from its list.
So, can anyone recommend which types of references (shared_ptr,
weak_ptr, etc.) should be used and where, and the process through which
an entity and its components are destroyed? Any tips greatly appreciated.
Thanks,
--Marty
------------------------------------------------------------------------
_______________________________________________
Sweng-Gamedev mailing list
http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-midnightryder.com
Noel Llopis
2007-10-20 19:19:18 UTC
Permalink
Post by Marty Rabens
First post here. I'm using smart pointers for the first time in a project
(yeah, I know - shame on me, I should have started using them years ago).
No, shame on you: You should stay away from them completely! :-)

Seriously, I know it's not what you're asking, but I'd just like to
pitch in my general dislike for smart pointers, especially for
consoles. I suppose if you're making a PC game and you don't really
care when/how memory is deallocated, then go for it. Although at that
point you might as well consider C#/Java/whatever.

I find that on the runtime in a console environment, I always know
when memory needs to be deallocated (at the end of the level, when I
page out a certain resource, etc). Usually when a level or a section
of the game is deallocated, you can just wipe a whole memory block. So
I gain nothing with smart pointers and I need to fight them along the
way.
Post by Marty Rabens
What I'm having trouble with is a clean, elegant design for how everything
keeps references to everything else (using smart pointers), especially when
it comes to destroying an entity.
What do you hope to gain by using smart pointers in this situation? It
seems that the entity is the ultimate owner of those components, so it
decides when to kill/destroy them. That's it. There's no sharing of
components across entities, so raw pointers should work just fine.


--Noel
Will Vale
2007-10-21 11:04:14 UTC
Permalink
Post by Noel Llopis
No, shame on you: You should stay away from them completely! :-)
Sounds like good advice to me!

It's also worth distinguishing between smart pointers and the concepts
they model. The smart pointer bit provides the pointer-like syntax and
such, but the concept can be used on its own.

Generally you get more automation with smart pointers (eliminates
repetitive code, can prevent mistakes) but you lose transparency as
there's lots of stuff which now happens invisibly as opposed to
explicitly. If you find yourself writing "// Smart pointers clean up
here" often, you might as well write "delete p;" or "p->DecRef();" instead.

As concepts, reference counting and the holding of weak references are
useful in some places. Reference counting can tell you if an asset is
safe to unload in a dynamic (e.g. streaming) environment, and weak
references can allow entities to target other entities safely.
Notification of observers (cf. weak pointer) is one way of handling
this, checking IDs in a database is another.

In the original post I'm a bit scared of the need for a component to
refer to another entity's component, since that makes more types of
object potential targets for (expensive) weak references. If component
lifetime is bound to entity lifetime it might be easier to hold a weak
reference to the containing entity, unless you *really* need the
optimisation.

If you do decide to go with Boost smart pointers, do yourself a favour
and look through the code in some detail - the need for them to work for
all possible cases tends to make them a lot heavier than the kind of
simple intrusive technique you might create for yourself.

Cheers,

Will
Darren Grant
2007-10-21 00:25:33 UTC
Permalink
Post by Marty Rabens
Hello all,
First post here. I'm using smart pointers for the first time in a
project (yeah, I know - shame on me, I should have started using
them years ago). Boost smart pointers, to be specific. It's clear
to me how they work, but I'm still trying to get a handle on how to
best make use of them. After a couple decades of coding with raw
pointers, it's hard to think in terms of smart pointers.
Here's the design I'm implementing. I'm using component-based
entities. An entity object is essentially a container of components
(e.g., a render component, a physics component, etc.). Each type of
component is managed by a subsystem for that component type, which
keeps track of all components of that type. For example, the render
subsystem knows about all render components, and when it's time to
render, it iterates through all of them.
An entity is created by a factory. The factory is handed an entity
definition with all the info about which components it needs and
their parameters. The factory has the subsystems create the
components and attaches them to the entity.
There is no monolithic container of entities, since everything is
handled by the subsystems.
All components derive from a common interface, and all subsystems
derive from a common interface.
What I'm having trouble with is a clean, elegant design for how
everything keeps references to everything else (using smart
pointers), especially when it comes to destroying an entity.
- a reference to each of the components attached to it.
- a reference to the entity it's attached to.
- zero or more references to other components attached to the same
entity (I've decided to allow this for performance reasons).
- references to all components of the type it manages.
Other things may also be holding a reference to a component. For
example, if one entity is following another, a component in the
follower will have a reference to a component in the
followee. Nothing will ever be holding a reference to an entity,
though (except the components attached to it).
When an entity is destroyed, all attached components need to be
destroyed (eventually), and each component's subsystem needs to
remove the component from its list.
So, can anyone recommend which types of references (shared_ptr,
weak_ptr, etc.) should be used and where, and the process through
which an entity and its components are destroyed? Any tips greatly
appreciated.
Thanks,
--Marty
Beware of using the wrong tool for the job. The reason I say this
is: by the description of your problem what you really want are
specialized event responses.. "Hey objects, I'm going away forever,
and these are the reasons for my departure from this world." Call
out with delegates or functors or straight up callback functions and
that's pretty much it.

In addition to this you can use any implementation of a weak
reference to prevent undefined behaviour, which is definitely not a
bad idea during development. If someone forgets to remove a
reference to an object that died, then a weak reference (perhaps as
implemented by weak_ptr, but be aware of its special limitations)
will at least guarantee a consistent 'object no longer exists' result
instead of just pointing into the ether.

Refer to a language with first-class garbage collection for a good
understanding of reference strengths and why they exist. Java goes
nuts by including strong, soft, weak and phantom references
(geeeez).. but that makes it good for review.

Regards,


----
Darren Grant
Lead Programmer
http://www.kerberos-productions.com/
http://www.swordofthestars.com/
Nathan Martz
2007-10-21 01:04:39 UTC
Permalink
If this problem is entity/component specific, I definitely lean in the
direction that Ola, Noel, and Darren suggest, i.e. a leaner, more
customized approach that formalizes the entity/component access patterns
and lifetime management issues unique to a component/entity model.

As a data point, our entity/component system works as follows: Entities
own their components, as such they can safely store raw pointers to
them. If you want to add, remove, or get at a component, you have to go
through the entity. Anything that wants to refer to another entity does
so through a weak reference (because you don't "own" other entities).
Holding onto component pointers by anyone except for the entity which
contains them is forbidden. That means more calls to GetComponent() than
if you held onto component pointers directly, but, given a properly
optimized implementation of GetComponent() (as discussed in some earlier
threads here), our experience is that it's a performance non-issue.

-Nathan

-----Original Message-----
From: sweng-gamedev-***@lists.midnightryder.com
[mailto:sweng-gamedev-***@lists.midnightryder.com] On Behalf Of
Darren Grant
Sent: Saturday, October 20, 2007 5:26 PM
To: sweng-***@midnightryder.com; sweng-***@midnightryder.com
Subject: Re: [Sweng-Gamedev] Designing with smart pointers

At 08:50 AM 10/20/2007, Marty Rabens wrote:


Hello all,

First post here. I'm using smart pointers for the first time in a
project (yeah, I know - shame on me, I should have started using them
years ago). Boost smart pointers, to be specific. It's clear to me how
they work, but I'm still trying to get a handle on how to best make use
of them. After a couple decades of coding with raw pointers, it's hard
to think in terms of smart pointers.

Here's the design I'm implementing. I'm using component-based entities.
An entity object is essentially a container of components (e.g., a
render component, a physics component, etc.). Each type of component is
managed by a subsystem for that component type, which keeps track of all
components of that type. For example, the render subsystem knows about
all render components, and when it's time to render, it iterates through
all of them.

An entity is created by a factory. The factory is handed an entity
definition with all the info about which components it needs and their
parameters. The factory has the subsystems create the components and
attaches them to the entity.

There is no monolithic container of entities, since everything is
handled by the subsystems.

All components derive from a common interface, and all subsystems derive
from a common interface.

What I'm having trouble with is a clean, elegant design for how
everything keeps references to everything else (using smart pointers),
especially when it comes to destroying an entity.

An entity must have:
- a reference to each of the components attached to it.

A component must have:
- a reference to the entity it's attached to.
- zero or more references to other components attached to the same
entity (I've decided to allow this for performance reasons).

A subsystem must have:
- references to all components of the type it manages.

Other things may also be holding a reference to a component. For
example, if one entity is following another, a component in the follower
will have a reference to a component in the followee. Nothing will ever
be holding a reference to an entity, though (except the components
attached to it).

When an entity is destroyed, all attached components need to be
destroyed (eventually), and each component's subsystem needs to remove
the component from its list.

So, can anyone recommend which types of references (shared_ptr,
weak_ptr, etc.) should be used and where, and the process through which
an entity and its components are destroyed? Any tips greatly
appreciated.

Thanks,
--Marty


Beware of using the wrong tool for the job. The reason I say this is:
by the description of your problem what you really want are specialized
event responses.. "Hey objects, I'm going away forever, and these are
the reasons for my departure from this world." Call out with delegates
or functors or straight up callback functions and that's pretty much it.


In addition to this you can use any implementation of a weak reference
to prevent undefined behaviour, which is definitely not a bad idea
during development. If someone forgets to remove a reference to an
object that died, then a weak reference (perhaps as implemented by
weak_ptr, but be aware of its special limitations) will at least
guarantee a consistent 'object no longer exists' result instead of just
pointing into the ether.

Refer to a language with first-class garbage collection for a good
understanding of reference strengths and why they exist. Java goes nuts
by including strong, soft, weak and phantom references (geeeez).. but
that makes it good for review.

Regards,



----
Darren Grant
Lead Programmer
http://www.kerberos-productions.com/
http://www.swordofthestars.com/
Ola Olsson
2007-10-21 13:22:16 UTC
Permalink
I can see no practical difference in going through a weak pointer to
an entity to get hold of a component, as oppsed to keeping a weak
pointer to the component. It would appear to implement the exact same
functionality?

E.g.

if (StrongComponentPtr p(m_weakComponentPtr))
{
...do stuff to component using p...
}
or
if (StrongComponentPtr p(getComponentFromEntity(m_entity, m_componentType))
{
...do stuff to component using p...
}

Indeed, one could quite easily implement the weak component pointer by
wrapping one weak reference to the entity and a component type id. Nor
can I see that there is any speed/storage advantage to either, except on
a subatomic level.

I don't see a problem with components having references to other
components (assuming one of the mechanisms in 1 below). In fact I can't
think of a compelling reason why a component should need to know of the
entity at all!

Anyhow as others have noted there are several issues at play here:

1. Keeping tabs on other things that might dissapear.
This requires some handle/weak pointer/reporting-of-death mechanism.
Does not require reference counting, and either can be implemented to
look like a weak pointer (in that it always has to be converted into a
strong/real pointer before use). This part is important if we want to be
sure that the object is not deleted by some other thread or so while we
think we are using it, so we need some lock/unlock semantics, which is
what weak->strong reference conversion provides. Still as I said doesn't
require reference counting.

2. Automating cleanup of objects that are not shared.
Explicit deallocation works fine for this, as does simple a simple
auto_ptr (or similar). Reference counting is not needed, except perhaps
to discover errors, in which case it can be compiled out in release
builds. Though if this is the aim a more sophisticated debug mechanism
which actually tells you _what_ is keeping the pointer would be nice.

3. Keeping track of use of shared objects.
These does require reference counting or some other way of being
collected when no longer needed. Reference counting being the simplest
form of garbage collector and it being fairly simple to keep the
references un-cyclic for this type of objects means it works fine.

4. Performance.
Reference counting has fairly high overhead, especially when used
indiscriminately on multithreaded machines (the InterlockedXX type
operations are not exactly free). Therefore reference counting should
probably be avoided for non-shared resources, at least if you can forsee
a large number of them being passed around the systems. The problem is
that the relatively expensive operations have to be used whenever a
smart pointer is copied, which _can_ become a problem fairly quickly
_if_ they are copied often.

5. Interesting other questions when chosing implementation:
* Do we want to be able to get the smart pointer from a raw pointer
(typically this).
* Is there any resource we wish to use this for that should not have
delete called on it when references goes to 0? Does the smart pointer
allow us to get around this?
* Can we elect to pass around straight pointers in places where it
matters (e.g. we do inside the rendering system, and it really matters!).
* Will the safe pointer implementaion generate blowout of object
files, slowing down the linker and so on.

Personally I would not advice anyone to not use some form of smart
pointer. Managing memory allocations is hard and it just takes one
mistakenly inserted return statement to start a trickle that might bring
down the game at some point. I do agree that one should consider
carefully what type, where and why they are used however! (The obvious
corollary is to use a good leak detector, and introduce it early in
development).

While thinking about this I was wondering if anyone ever tried some
other garbage collection scheme for shared resources? Seing as a lot of
the time the resources cannot be deleted straight away anyway. For
example rendering resources might need to hang around a few extra
frames. So we might as well run a collector say at the end of each frame
anyway.

cheers
.ola
Post by Nathan Martz
If this problem is entity/component specific, I definitely lean in the
direction that Ola, Noel, and Darren suggest, i.e. a leaner, more
customized approach that formalizes the entity/component access patterns
and lifetime management issues unique to a component/entity model.
As a data point, our entity/component system works as follows: Entities
own their components, as such they can safely store raw pointers to
them. If you want to add, remove, or get at a component, you have to go
through the entity. Anything that wants to refer to another entity does
so through a weak reference (because you don’t “own” other entities).
Holding onto component pointers by anyone except for the entity which
contains them is forbidden. That means more calls to GetComponent() than
if you held onto component pointers directly, but, given a properly
optimized implementation of GetComponent() (as discussed in some earlier
threads here), our experience is that it’s a performance non-issue.
-Nathan
-----Original Message-----
*Darren Grant
*Sent:* Saturday, October 20, 2007 5:26 PM
*Subject:* Re: [Sweng-Gamedev] Designing with smart pointers
Hello all,
First post here. I'm using smart pointers for the first time in a
project (yeah, I know - shame on me, I should have started using them
years ago). Boost smart pointers, to be specific. It's clear to me how
they work, but I'm still trying to get a handle on how to best make use
of them. After a couple decades of coding with raw pointers, it's hard
to think in terms of smart pointers.
Here's the design I'm implementing. I'm using component-based
entities. An entity object is essentially a container of components
(e.g., a render component, a physics component, etc.). Each type of
component is managed by a subsystem for that component type, which keeps
track of all components of that type. For example, the render subsystem
knows about all render components, and when it's time to render, it
iterates through all of them.
An entity is created by a factory. The factory is handed an entity
definition with all the info about which components it needs and their
parameters. The factory has the subsystems create the components and
attaches them to the entity.
There is no monolithic container of entities, since everything is
handled by the subsystems.
All components derive from a common interface, and all subsystems derive
Post by Marty Rabens
from a common interface.
What I'm having trouble with is a clean, elegant design for how
everything keeps references to everything else (using smart pointers),
especially when it comes to destroying an entity.
- a reference to each of the components attached to it.
- a reference to the entity it's attached to.
- zero or more references to other components attached to the same
entity (I've decided to allow this for performance reasons).
- references to all components of the type it manages.
Other things may also be holding a reference to a component. For
example, if one entity is following another, a component in the follower
will have a reference to a component in the followee. Nothing will ever
be holding a reference to an entity, though (except the components
attached to it).
When an entity is destroyed, all attached components need to be
destroyed (eventually), and each component's subsystem needs to remove
the component from its list.
So, can anyone recommend which types of references (shared_ptr,
weak_ptr, etc.) should be used and where, and the process through which
an entity and its components are destroyed? Any tips greatly appreciated.
Thanks,
--Marty
by the description of your problem what you really want are specialized
event responses.. "Hey objects, I'm going away forever, and these are
the reasons for my departure from this world." Call out with delegates
or functors or straight up callback functions and that's pretty much it.
In addition to this you can use any implementation of a weak reference
to prevent undefined behaviour, which is definitely not a bad idea
during development. If someone forgets to remove a reference to an
object that died, then a weak reference (perhaps as implemented by
weak_ptr, but be aware of its special limitations) will at least
guarantee a consistent 'object no longer exists' result instead of just
pointing into the ether.
Refer to a language with first-class garbage collection for a good
understanding of reference strengths and why they exist. Java goes nuts
by including strong, soft, weak and phantom references (geeeez).. but
that makes it good for review.
Regards,
----
Darren Grant
Lead Programmer
http://www.kerberos-productions.com/
http://www.swordofthestars.com/
------------------------------------------------------------------------
_______________________________________________
Sweng-Gamedev mailing list
http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-midnightryder.com
Marty Rabens
2007-10-21 15:07:37 UTC
Permalink
Wow. I was hoping for a response with a couple tips, but I didn't expect
this much insightful edification from this many people! I really appreciate
it (and I have to say, I'm impressed with the quality of people on this
list).

I just wanted to let everyone know that I am reading all of the responses
and discussion. It'll take me a bit to digest it all, so I don't have any
kind of meaningful reply just yet.

I will say a couple things to clarify where I'm coming from.

First, the primary problem I'm ultimately trying to solve is things having
stale pointers to destroyed entities. I'm also using component-based
entities for the first time (I've always hated the mess that results from an
entity class hierarchy, and I want the flexibility of data-driven entity
definition).

Admittedly, I'd read good things about smart pointers, so I had this shiny
new hammer and was seeing an awful lot of nails around. They seemed like a
reasonable solution to stale pointers, and the things I've read seemed to
indicate using them consistently, so I was trying to use them throughout my
design. I now see the error of my ways. I'm certainly not dead-set on
using them.

The project I'm working on is one of those personal experimental things in
my free time (my day job is as a programmer at a small game studio). It's
Windows platform, and I'm sure will never see a console. Performance and
efficiency is definitely a factor, though, since the game design involves
large swarms of hundreds (or thousands) of entities.

Again, thanks to everyone for all of the useful info.

--Marty
Robert Blum
2007-10-21 16:11:07 UTC
Permalink
Post by Marty Rabens
First, the primary problem I'm ultimately trying to solve is things having
stale pointers to destroyed entities. I'm also using component-based
entities for the first time (I've always hated the mess that
results from an
entity class hierarchy, and I want the flexibility of data-driven entity
definition).
I hate to say it, but it's merely a different kind of mess ;) I
believe there have been a few threads on this topic on sweng already
if you're interested.
Post by Marty Rabens
Admittedly, I'd read good things about smart pointers, so I had this shiny
new hammer and was seeing an awful lot of nails around. They
seemed like a
reasonable solution to stale pointers,
They are a reasonable solution to stale pointers. I'm working on an
AAA title using them right now, and they don't *seem* to be a
problem. (I'll withhold judgement till it's shipped, though ;)

They are not the only solution, and in some areas they're not even a
good solution. It's nice if you do have a solution that gracefully
converts between smart and naked pointers - that way, you'll use the
naked pointers in the innermost loops (render, etc.) while the smart
pointers take care of global lifetime management.
Post by Marty Rabens
and the things I've read seemed to
indicate using them consistently, so I was trying to use them
throughout my
design. I now see the error of my ways. I'm certainly not dead-
set on
using them.
Using them involves a tradeoff. You get easier lifetime management.
You pay a price in terms of performance and memory.(and - with naive
implementations - memory fragmentation).

Give them at least a try before you discard them.

I do believe that memory management is losing importance in many
areas. No, it probably won't go away any time soon, but all but
extremely large titles have a chance to do away with most of it by
now. So it pays to look at alternative mechanisms to be prepared when
we can actually use them. (And I'd give GC a look, too. There are
decent realtime collectors by now).
Post by Marty Rabens
The project I'm working on is one of those personal experimental things in
my free time (my day job is as a programmer at a small game
studio). It's
Windows platform, and I'm sure will never see a console.
Performance and
efficiency is definitely a factor, though, since the game design involves
large swarms of hundreds (or thousands) of entities.
I don't think they'll be a major pain. They haven't been in my
experience. And since it's a spare-time project, I'd think it's the
perfect area to try new ideas.

Either way, they are just one tool of many in the toolbox. They don't
solve all problems, but they aren't universally evil either.

- Robert
Kyle Wilson
2007-10-21 16:54:29 UTC
Permalink
Unlike most of the people who've responded, my experience with smart pointers has been tremendously positive. In Despair (the engine Day 1 is using in Fracture and another, as-yet-unannounced, shooter), we have about ten different smart pointer classes. I've had very smart, experienced engineers from publishers roll their eyes and say, "One smart pointer type is too many!" when I tell them that, but in actual practice they've worked so well for us that I couldn't imagine working again on an engine that didn't use smart pointers.

It's important that the programmers working with smart pointers realize:
a.. Smart pointers are not the same as raw pointers. Smart pointers are a shorthand for managing AddRefs and Releases, but you can't use them effectively without realizing when they're going to allocate memory or change reference counts.
b.. Smart pointers are not the same as garbage collection. It's imperative that you avoid cyclic strong references, or you may suddenly go from not leaking any memory to leaking every object you allocate. This happens to us about once a year, and it's several hours of annoying work to track down the cause.
We've mitigated the costs of smart pointers by regular reiteration of tips like "Pass smart pointers by reference, not by value" and "Establish strong references at load time and use them at runtime." We've also created different smart pointer types for different situations. The vast majority of objects in our engine are internally-reference-counted and will never have AddRef/Release called on them from multiple threads at the same time. We don't have to allocate memory for an external reference count and we don't have to do InterlockedIncrement/InterlockedDecrement, which makes smart pointers to these objects much more efficient than boost::shared_ptr. (Using our own smart pointers instead of boost::shared_ptr also lets us change some points of style. Our shared_ptrs are implicitly constructible from NULL, so engineers can continue to use the more natural "if (myPtr != NULL)" syntax.)

Among our smart pointer gallimaufry are:
a.. scoped_ptr - our version of std::auto_ptr, without the unnatural assignment operator
b.. shared_ptr - a non-threadsafe externally ref-counted strong pointer
c.. ref_ptr - an internally ref-counted strong pointer (thread safe or not depending on how AddRef and Release are implemented)
d.. ComPtr - the same, but for COM objects
e.. HavokRefPtr - the same, but for Havok objects
f.. res_ptr - a pointer to resources being loaded from our streaming thread... it becomes non-NULL when the resource load completes, and afterward behaves like a ref_ptr
g.. weak_ptr - an intrusive weak pointer
We could significantly reduce our number of smart pointer types if C++ supported template typedefs, and we could just typedef one smart pointer class with different policies. We still use boost::shared_ptr in places, but I keep meaning to replace it with a threadsafe atomic_shared_ptr of our own, since the minor usage differences are annoying.

So far I've talked a lot about mitigating the costs of shared pointers without saying anything about the benefits, which I'm afraid may give the wrong impression. In practice, it's obvious what pointer is best for a given situation, and most of the smart pointers used in the game are typedefed in core libraries so that gameplay engineers can use them without even having to think about their details. The daily cost of our use of smart pointers is negligible. The daily benefit is that the Despair engine doesn't leak memory. Somebody introduces a memory leak maybe once every two or three months, and when it happens it's invariably a junior engineer who for some reason has decided not to use the common idioms of our software development process. I credit our use of smart pointers, our rules for ownership, and our use of the RAII idiom for giving us an engine that's more stable than any I've ever worked on before, even as our team has grown from ten engineers to thirty.

To get back to your particular circumstance, the game object system you describe is actually a lot like ours. You just need to define your ownership hierarchy and figure out how to make weak references for all your other connections. The way we do this actually doesn't involve very many smart pointers, since most of the inter-object references are weak references that aren't exactly lifetime dependent:
a.. An entity owns a std::vector of components. You could use raw pointers for this and delete them explicitly when they're removed or when the object goes away. We use smart pointers because we have inter-object inheritance, so components are sometimes shared in prototype objects. That means our components have to be reference-counted, and once an object is reference-counted, you're safer using a ref_ptr to hold onto it. For actual simulation objects, though, the rule is that when an object goes away, all its components go away.
b.. All entities and components have an explicit OnAddToScene call. Components register with their managers (like your subsystems) here. They also look up other components that they need to know about. The work here is all raw pointers, except when one component references a component in another object. Those indirect through a weak pointer type that goes NULL when the other component is removed from the scene.
c.. All entities and components have an explicit OnRemoveFromScene call. Components unregister with their managers here.
The OnAddToScene / OnRemoveFromScene calls exist because an object's existence doesn't necessarily mean that it should be part of the simulation. We stream objects in a separate thread, and we don't want them simulating until their whole world has finished loading, has been returned to the main game thread, and can be added to the scene as a single unit.

I hope this is helpful. Good luck with your engine.

Regards,
Kyle


----- Original Message -----
From: "Marty Rabens" <***@rabens.com>
To: <sweng-***@midnightryder.com>
Sent: Sunday, October 21, 2007 11:07 AM
Subject: Re: [Sweng-Gamedev] Designing with smart pointers
Post by Marty Rabens
Wow. I was hoping for a response with a couple tips, but I didn't expect
this much insightful edification from this many people! I really appreciate
it (and I have to say, I'm impressed with the quality of people on this
list).
I just wanted to let everyone know that I am reading all of the responses
and discussion. It'll take me a bit to digest it all, so I don't have any
kind of meaningful reply just yet.
I will say a couple things to clarify where I'm coming from.
First, the primary problem I'm ultimately trying to solve is things having
stale pointers to destroyed entities. I'm also using component-based
entities for the first time (I've always hated the mess that results from an
entity class hierarchy, and I want the flexibility of data-driven entity
definition).
Admittedly, I'd read good things about smart pointers, so I had this shiny
new hammer and was seeing an awful lot of nails around. They seemed like a
reasonable solution to stale pointers, and the things I've read seemed to
indicate using them consistently, so I was trying to use them throughout my
design. I now see the error of my ways. I'm certainly not dead-set on
using them.
The project I'm working on is one of those personal experimental things in
my free time (my day job is as a programmer at a small game studio). It's
Windows platform, and I'm sure will never see a console. Performance and
efficiency is definitely a factor, though, since the game design involves
large swarms of hundreds (or thousands) of entities.
Again, thanks to everyone for all of the useful info.
--Marty
Dan Chang
2007-10-21 17:30:07 UTC
Permalink
Smart pointers are not the same as garbage collection. It's imperative
that you avoid cyclic strong references, or you may suddenly go from not
leaking any memory to leaking every object you allocate. This happens to us
about once a year, and it's several hours of annoying work to track down the
cause.

I've seen this happen on several games that I've worked on. I don't think it
can be avoided--someone will (eventually) make the error. And it is a real
pain to track them down.

I addressed the problem by extending the smart pointer to track who was
holding a reference to the object. I then used Graphviz to show which
objects kept hold of references to the object. I couldn't graph too many
objects, as it would quickly get way too hairy. Perhaps this was a function
of how objects worked in our engine, or perhaps this is something that
always happens.

This experience gave me a pretty dim view of (how we were using) smart
pointers. I think of the smart pointer trade-off as:

* Not use smart pointers: track down who forgot to deallocate a bit of
memory.
* Using smart pointers: track down which object is still holding a
reference to the object.

We have lots of ways to deal with the first problem, including heap walking
code and other techniques. I'm still learning about how to deal with the
second problem.

Best regards,
Dan
Unlike most of the people who've responded, my experience with smart
pointers has been tremendously positive. In Despair (the engine Day 1 is
using in *Fracture* and another, as-yet-unannounced, shooter), we have
about ten different smart pointer classes. I've had very smart,
experienced engineers from publishers roll their eyes and say, "One smart
pointer type is too many!" when I tell them that, but in actual practice
they've worked so well for us that I couldn't imagine working again on an
engine that didn't use smart pointers.
- Smart pointers are not the same as raw pointers. Smart pointers
are a shorthand for managing AddRefs and Releases, but you can't use them
effectively without realizing when they're going to allocate memory or
change reference counts.
- Smart pointers are not the same as garbage collection. It's
imperative that you avoid cyclic strong references, or you may suddenly go
from not leaking any memory to leaking every object you allocate. This
happens to us about once a year, and it's several hours of annoying work to
track down the cause.
We've mitigated the costs of smart pointers by regular reiteration of tips
like "Pass smart pointers by reference, not by value" and "Establish strong
references at load time and use them at runtime." We've also created
different smart pointer types for different situations. The vast majority
of objects in our engine are internally-reference-counted and will never
have AddRef/Release called on them from multiple threads at the same time.
We don't have to allocate memory for an external reference count and we
don't have to do InterlockedIncrement/InterlockedDecrement, which makes
smart pointers to these objects much more efficient than boost::shared_ptr.
(Using our own smart pointers instead of boost::shared_ptr also lets us
change some points of style. Our shared_ptrs are implicitly constructible
from NULL, so engineers can continue to use the more natural "if (myPtr !=
NULL)" syntax.)
- scoped_ptr - our version of std::auto_ptr, without the unnatural
assignment operator
- shared_ptr - a non-threadsafe externally ref-counted strong
pointer
- ref_ptr - an internally ref-counted strong pointer (thread safe or
not depending on how AddRef and Release are implemented)
- ComPtr - the same, but for COM objects
- HavokRefPtr - the same, but for Havok objects
- res_ptr - a pointer to resources being loaded from our streaming
thread... it becomes non-NULL when the resource load completes, and
afterward behaves like a ref_ptr
- weak_ptr - an intrusive weak pointer
We could significantly reduce our number of smart pointer types if C++
supported template typedefs, and we could just typedef one smart pointer
class with different policies. We still use boost::shared_ptr in places,
but I keep meaning to replace it with a threadsafe atomic_shared_ptr of our
own, since the minor usage differences are annoying.
So far I've talked a lot about mitigating the costs of shared pointers
without saying anything about the benefits, which I'm afraid may give the
wrong impression. In practice, it's obvious what pointer is best for a
given situation, and most of the smart pointers used in the game are
typedefed in core libraries so that gameplay engineers can use them without
even having to think about their details. The daily cost of our use of
smart pointers is negligible. The daily benefit is that the Despair engine
doesn't leak memory. Somebody introduces a memory leak maybe once every two
or three months, and when it happens it's invariably a junior engineer who
for some reason has decided not to use the common idioms of our software
development process. I credit our use of smart pointers, our rules for
ownership, and our use of the RAII idiom for giving us an engine that's more
stable than any I've ever worked on before, even as our team has grown from
ten engineers to thirty.
To get back to your particular circumstance, the game object system you
describe is actually a lot like ours. You just need to define your
ownership hierarchy and figure out how to make weak references for all your
other connections. The way we do this actually doesn't involve very many
smart pointers, since most of the inter-object references are weak
- An entity owns a std::vector of components. You could use raw
pointers for this and delete them explicitly when they're removed or when
the object goes away. We use smart pointers because we have inter-object
inheritance, so components are sometimes shared in prototype objects. That
means our components have to be reference-counted, and once an object is
reference-counted, you're safer using a ref_ptr to hold onto it. For actual
simulation objects, though, the rule is that when an object goes away, all
its components go away.
- All entities and components have an explicit OnAddToScene call.
Components register with their managers (like your subsystems) here. They
also look up other components that they need to know about. The work here
is all raw pointers, except when one component references a component in
another object. Those indirect through a weak pointer type that goes NULL
when the other component is removed from the scene.
- All entities and components have an explicit OnRemoveFromScene
call. Components unregister with their managers here.
The OnAddToScene / OnRemoveFromScene calls exist because an object's
existence doesn't necessarily mean that it should be part of the
simulation. We stream objects in a separate thread, and we don't want them
simulating until their whole world has finished loading, has been returned
to the main game thread, and can be added to the scene as a single unit.
I hope this is helpful. Good luck with your engine.
Regards,
Kyle
Sent: Sunday, October 21, 2007 11:07 AM
Subject: Re: [Sweng-Gamedev] Designing with smart pointers
Post by Marty Rabens
Wow. I was hoping for a response with a couple tips, but I didn't
expect
Post by Marty Rabens
this much insightful edification from this many people! I really
appreciate
Post by Marty Rabens
it (and I have to say, I'm impressed with the quality of people on this
list).
I just wanted to let everyone know that I am reading all of the
responses
Post by Marty Rabens
and discussion. It'll take me a bit to digest it all, so I don't have
any
Post by Marty Rabens
kind of meaningful reply just yet.
I will say a couple things to clarify where I'm coming from.
First, the primary problem I'm ultimately trying to solve is things
having
Post by Marty Rabens
stale pointers to destroyed entities. I'm also using component-based
entities for the first time (I've always hated the mess that results
from an
Post by Marty Rabens
entity class hierarchy, and I want the flexibility of data-driven entity
definition).
Admittedly, I'd read good things about smart pointers, so I had this
shiny
Post by Marty Rabens
new hammer and was seeing an awful lot of nails around. They seemed
like a
Post by Marty Rabens
reasonable solution to stale pointers, and the things I've read seemed
to
Post by Marty Rabens
indicate using them consistently, so I was trying to use them throughout
my
Post by Marty Rabens
design. I now see the error of my ways. I'm certainly not dead-set on
using them.
The project I'm working on is one of those personal experimental things
in
Post by Marty Rabens
my free time (my day job is as a programmer at a small game studio).
It's
Post by Marty Rabens
Windows platform, and I'm sure will never see a console. Performance
and
Post by Marty Rabens
efficiency is definitely a factor, though, since the game design
involves
Post by Marty Rabens
large swarms of hundreds (or thousands) of entities.
Again, thanks to everyone for all of the useful info.
--Marty
_______________________________________________
Sweng-Gamedev mailing list
http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-midnightryder.com
Jon Watte
2007-10-22 17:41:19 UTC
Permalink
Post by Dan Chang
This experience gave me a pretty dim view of (how we were using) smart
* Not use smart pointers: track down who forgot to deallocate a bit
of memory.
* Using smart pointers: track down which object is still holding a
reference to the object.
In debug mode, each pointed-at object can have a list of who's pointing
at it.

Or you can register all smart pointers in a registry, and call a
function to find the pointer(s) with the value given.

Cheers,

/ h+
--
-- Revenge is the most pointless and damaging of human desires.
Tom Seddon
2007-10-22 20:21:01 UTC
Permalink
Post by Jon Watte
Post by Dan Chang
This experience gave me a pretty dim view of (how we were using) smart
* Not use smart pointers: track down who forgot to deallocate a bit
of memory.
* Using smart pointers: track down which object is still holding a
reference to the object.
In debug mode, each pointed-at object can have a list of who's pointing
at it.
Or you can register all smart pointers in a registry, and call a
function to find the pointer(s) with the value given.
I did this. I also made it record the program counter of the last place
the pointer was set. Then I added some dbghelp+OutputDebugString code to
allow one to use this stuff from the VC++ immediate window, and get a
nice display of all the pointers to a given object, and the locations in
code where each was set.

This was fairly useful, but tracking things down was still often a pain.
It wasn't always obvious what the root cause of any reference chain was:
quite often, having found a dead object, and the places pointers were
set to point at it, you'd end up looking at nothing but pieces of code
that had set pointers that were themselves part of refcounted objects,
with no further information to continue the search. In effect, you'd be
back at square one.

"I'm Eeyore, and I disapprove of this message"? -- not so, for I have a
concrete suggestion. As you and Dan suggest, allow pointers to
optionally record which *refcounted* object owns them (the pointer would
be NULL if no such). Refcounted objects would initialize these in the
constructor/OnLoad function/etc.

My thinking is that a stuck refcounted object is ultimately due to
pointers that aren't themselves part of refcounted objects. So to find
what's causing the zombie object at X, search for all smart pointers
pointing to X, then for all smart pointers pointing to the objects
holding those pointers, and so on, until you have a list of all the
smart pointers that aren't parts of refcounted objects.

Then go through these, one at a time, looking at the bit of code that
set each, until you work out what's up.

(Naturally, you'd detect cycles as part of your graph traversal and
print them out too, because they can put a bit of a spanner in the works ;)

Disclaimer: this only occurred to me as a result of this discussion, and
I haven't tried it out (yet!), so the usual caveats apply.

--Tom
Tobias Ford
2007-10-25 07:54:33 UTC
Permalink
This is a fun thread since I've helped implement smart pointer
solutions in previous projects from scratch and dealt a lot with the
std::shared_ptr in our current game. I personally love smart
pointers but I feel that they should never be mistaken as a garbage
collection solution. I believe that the developer should always be
aware of an object's scope and they should also know when to actually
use hard or weak references. I've also ran into many loves and hates
in regard to intrusive and non-intrusive implementations.

I'm currently using shared_ptr, which I'm fairly happy with. This
post touches on my _biggest_ complaint with that solution, though. I
really want the ability to set some sort of call back whenever the
reference is incremented or decremented in std::shared_ptr. I don't
think that something like this exists except in the case of the ref
decrementing to 0.

But even so, leaked pointers are a problem that comes up vary rarely
for us. But when it does happen, something like this could save a
couple of hours when tracking it down.
Post by Jon Watte
Post by Dan Chang
This experience gave me a pretty dim view of (how we were using) smart
* Not use smart pointers: track down who forgot to deallocate a bit
of memory.
* Using smart pointers: track down which object is still holding a
reference to the object.
In debug mode, each pointed-at object can have a list of who's
pointing
at it.
Or you can register all smart pointers in a registry, and call a
function to find the pointer(s) with the value given.
Cheers,
/ h+
--
-- Revenge is the most pointless and damaging of human desires.
_______________________________________________
Sweng-Gamedev mailing list
http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-
midnightryder.com
-------------------
Tobias Ford...
tobias1482 'at' mac.com
tford 'at' spacetimestudios.com
-------------------
Sr Programmer @ space time studios for a generic space game mmo
Conte, Matt
2007-10-21 21:45:56 UTC
Permalink
I'd also like to make a couple of positive squeaks for the smart pointer
team. I work in a large codebase that makes heavy use of smart pointers,
and has shipped many console titles in its 8 year lifespan. We have just
one type of smart pointer, an intrusive ref-counted variant that is tied
into the central object model.

The memory management and reference counting benefits of smart pointers
is great, but the the single most important facet of smart pointers, in
my opinion, is explicit ownership. This is completely lacking with dumb
pointers, for instance:

Object* object = new Object;
object = new Object; // leak #1
object = 0; // leak #2

SmartPtr<Object> objectRef = new Object;
objectRef = new Object; // no leak
objectRef = 0; // no leak

There are performance implications with smart pointers, as others have
noticed. As always, this varies with implementation and usage. Inner
loop stuff isn't really an issue (typically the dereference operator is
implemented on the smart pointer class to just return the unlderlying
pointer); typically in performance critical code, we'll avoid passing
and returning smart pointers to and from methods, instead preferring
naked pointers, to avoid the useless ref/release.

--Matt

--
matthew conte
vv/activision


________________________________

From: sweng-gamedev-***@lists.midnightryder.com
[mailto:sweng-gamedev-***@lists.midnightryder.com] On Behalf Of Kyle
Wilson
Sent: Sunday, October 21, 2007 9:54 AM
To: sweng-***@midnightryder.com
Subject: Re: [Sweng-Gamedev] Designing with smart pointers


Unlike most of the people who've responded, my experience with
smart pointers has been tremendously positive. In Despair (the engine
Day 1 is using in Fracture and another, as-yet-unannounced, shooter), we
have about ten different smart pointer classes. I've had very smart,
experienced engineers from publishers roll their eyes and say, "One
smart pointer type is too many!" when I tell them that, but in actual
practice they've worked so well for us that I couldn't imagine working
again on an engine that didn't use smart pointers.

It's important that the programmers working with smart pointers
realize:

* Smart pointers are not the same as raw pointers. Smart
pointers are a shorthand for managing AddRefs and Releases, but you
can't use them effectively without realizing when they're going to
allocate memory or change reference counts.
* Smart pointers are not the same as garbage collection.
It's imperative that you avoid cyclic strong references, or you may
suddenly go from not leaking any memory to leaking every object you
allocate. This happens to us about once a year, and it's several hours
of annoying work to track down the cause.

We've mitigated the costs of smart pointers by regular
reiteration of tips like "Pass smart pointers by reference, not by
value" and "Establish strong references at load time and use them at
runtime." We've also created different smart pointer types for
different situations. The vast majority of objects in our engine are
internally-reference-counted and will never have AddRef/Release called
on them from multiple threads at the same time. We don't have to
allocate memory for an external reference count and we don't have to do
InterlockedIncrement/InterlockedDecrement, which makes smart pointers to
these objects much more efficient than boost::shared_ptr. (Using our
own smart pointers instead of boost::shared_ptr also lets us change some
points of style. Our shared_ptrs are implicitly constructible from
NULL, so engineers can continue to use the more natural "if (myPtr !=
NULL)" syntax.)

Among our smart pointer gallimaufry are:

* scoped_ptr - our version of std::auto_ptr, without the
unnatural assignment operator
* shared_ptr - a non-threadsafe externally ref-counted
strong pointer
* ref_ptr - an internally ref-counted strong pointer
(thread safe or not depending on how AddRef and Release are implemented)

* ComPtr - the same, but for COM objects
* HavokRefPtr - the same, but for Havok objects
* res_ptr - a pointer to resources being loaded from our
streaming thread... it becomes non-NULL when the resource load
completes, and afterward behaves like a ref_ptr
* weak_ptr - an intrusive weak pointer

We could significantly reduce our number of smart pointer types
if C++ supported template typedefs, and we could just typedef one smart
pointer class with different policies. We still use boost::shared_ptr
in places, but I keep meaning to replace it with a threadsafe
atomic_shared_ptr of our own, since the minor usage differences are
annoying.

So far I've talked a lot about mitigating the costs of shared
pointers without saying anything about the benefits, which I'm afraid
may give the wrong impression. In practice, it's obvious what pointer
is best for a given situation, and most of the smart pointers used in
the game are typedefed in core libraries so that gameplay engineers can
use them without even having to think about their details. The daily
cost of our use of smart pointers is negligible. The daily benefit is
that the Despair engine doesn't leak memory. Somebody introduces a
memory leak maybe once every two or three months, and when it happens
it's invariably a junior engineer who for some reason has decided not to
use the common idioms of our software development process. I credit our
use of smart pointers, our rules for ownership, and our use of the RAII
idiom for giving us an engine that's more stable than any I've ever
worked on before, even as our team has grown from ten engineers to
thirty.

To get back to your particular circumstance, the game object
system you describe is actually a lot like ours. You just need to
define your ownership hierarchy and figure out how to make weak
references for all your other connections. The way we do this actually
doesn't involve very many smart pointers, since most of the inter-object
references are weak references that aren't exactly lifetime dependent:

* An entity owns a std::vector of components. You could
use raw pointers for this and delete them explicitly when they're
removed or when the object goes away. We use smart pointers because we
have inter-object inheritance, so components are sometimes shared in
prototype objects. That means our components have to be
reference-counted, and once an object is reference-counted, you're safer
using a ref_ptr to hold onto it. For actual simulation objects, though,
the rule is that when an object goes away, all its components go away.
* All entities and components have an explicit
OnAddToScene call. Components register with their managers (like your
subsystems) here. They also look up other components that they need to
know about. The work here is all raw pointers, except when one
component references a component in another object. Those indirect
through a weak pointer type that goes NULL when the other component is
removed from the scene.
* All entities and components have an explicit
OnRemoveFromScene call. Components unregister with their managers here.

The OnAddToScene / OnRemoveFromScene calls exist because an
object's existence doesn't necessarily mean that it should be part of
the simulation. We stream objects in a separate thread, and we don't
want them simulating until their whole world has finished loading, has
been returned to the main game thread, and can be added to the scene as
a single unit.

I hope this is helpful. Good luck with your engine.

Regards,
Kyle


----- Original Message -----
From: "Marty Rabens" <***@rabens.com <mailto:***@rabens.com>
To: <sweng-***@midnightryder.com
<mailto:sweng-***@midnightryder.com> >
Sent: Sunday, October 21, 2007 11:07 AM
Subject: Re: [Sweng-Gamedev] Designing with smart pointers
Post by Marty Rabens
Wow. I was hoping for a response with a couple tips, but I
didn't expect
Post by Marty Rabens
this much insightful edification from this many people! I
really appreciate
Post by Marty Rabens
it (and I have to say, I'm impressed with the quality of
people on this
Post by Marty Rabens
list).
I just wanted to let everyone know that I am reading all of
the responses
Post by Marty Rabens
and discussion. It'll take me a bit to digest it all, so I
don't have any
Post by Marty Rabens
kind of meaningful reply just yet.
I will say a couple things to clarify where I'm coming from.
First, the primary problem I'm ultimately trying to solve is
things having
Post by Marty Rabens
stale pointers to destroyed entities. I'm also using
component-based
Post by Marty Rabens
entities for the first time (I've always hated the mess that
results from an
Post by Marty Rabens
entity class hierarchy, and I want the flexibility of
data-driven entity
Post by Marty Rabens
definition).
Admittedly, I'd read good things about smart pointers, so I
had this shiny
Post by Marty Rabens
new hammer and was seeing an awful lot of nails around. They
seemed like a
Post by Marty Rabens
reasonable solution to stale pointers, and the things I've
read seemed to
Post by Marty Rabens
indicate using them consistently, so I was trying to use them
throughout my
Post by Marty Rabens
design. I now see the error of my ways. I'm certainly not
dead-set on
Post by Marty Rabens
using them.
The project I'm working on is one of those personal
experimental things in
Post by Marty Rabens
my free time (my day job is as a programmer at a small game
studio). It's
Post by Marty Rabens
Windows platform, and I'm sure will never see a console.
Performance and
Post by Marty Rabens
efficiency is definitely a factor, though, since the game
design involves
Post by Marty Rabens
large swarms of hundreds (or thousands) of entities.
Again, thanks to everyone for all of the useful info.
--Marty
Leigh McRae
2007-10-21 23:39:38 UTC
Permalink
Nathan, I would say that you have summed it up really well. A couple of comments.

Accessing Entities through a weak reference is a very important point. As Nathan points out, you probably don't want Entities owning each other. I use a handle based system for references to Entities. AI code is usually littered with weak references that you wouldn't want to prevent an Entity from being deleted. Chances are that when you are deleting an Entity, you really want to deleted it. Say you have an AI unit that is way off in the distance and only gets updated rarely. This unit could be holding onto references that cause Entites not to be able to unload.

Smart pointers are really good for when you own a resource and I don't know why you wouldn't want to use them. I use simple smart pointers to handle the reference count of shared resources such as textures, audio, models and anims. Saying that there is a performance cost to smart pointers is really harsh. If you write code with smart pointers as if they were raw points, then you could have some problems. I don't think I even have a single function that takes a smart pointer. I only use a smart pointer to lock a reference between updates. It's should be standard practise to cache intermediate values within a function. If a smart pointer is being used more than once in a function, then de-reference it and cache the raw pointer.

Leigh
----- Original Message -----
From: Nathan Martz
To: sweng-***@midnightryder.com
Sent: Saturday, October 20, 2007 9:04 PM
Subject: Re: [Sweng-Gamedev] Designing with smart pointers


If this problem is entity/component specific, I definitely lean in the direction that Ola, Noel, and Darren suggest, i.e. a leaner, more customized approach that formalizes the entity/component access patterns and lifetime management issues unique to a component/entity model.



As a data point, our entity/component system works as follows: Entities own their components, as such they can safely store raw pointers to them. If you want to add, remove, or get at a component, you have to go through the entity. Anything that wants to refer to another entity does so through a weak reference (because you don't "own" other entities). Holding onto component pointers by anyone except for the entity which contains them is forbidden. That means more calls to GetComponent() than if you held onto component pointers directly, but, given a properly optimized implementation of GetComponent() (as discussed in some earlier threads here), our experience is that it's a performance non-issue.



-Nathan



-----Original Message-----
From: sweng-gamedev-***@lists.midnightryder.com [mailto:sweng-gamedev-***@lists.midnightryder.com] On Behalf Of Darren Grant
Sent: Saturday, October 20, 2007 5:26 PM
To: sweng-***@midnightryder.com; sweng-***@midnightryder.com
Subject: Re: [Sweng-Gamedev] Designing with smart pointers



At 08:50 AM 10/20/2007, Marty Rabens wrote:



Hello all,

First post here. I'm using smart pointers for the first time in a project (yeah, I know - shame on me, I should have started using them years ago). Boost smart pointers, to be specific. It's clear to me how they work, but I'm still trying to get a handle on how to best make use of them. After a couple decades of coding with raw pointers, it's hard to think in terms of smart pointers.

Here's the design I'm implementing. I'm using component-based entities. An entity object is essentially a container of components (e.g., a render component, a physics component, etc.). Each type of component is managed by a subsystem for that component type, which keeps track of all components of that type. For example, the render subsystem knows about all render components, and when it's time to render, it iterates through all of them.

An entity is created by a factory. The factory is handed an entity definition with all the info about which components it needs and their parameters. The factory has the subsystems create the components and attaches them to the entity.

There is no monolithic container of entities, since everything is handled by the subsystems.

All components derive from a common interface, and all subsystems derive from a common interface.

What I'm having trouble with is a clean, elegant design for how everything keeps references to everything else (using smart pointers), especially when it comes to destroying an entity.

An entity must have:
- a reference to each of the components attached to it.

A component must have:
- a reference to the entity it's attached to.
- zero or more references to other components attached to the same entity (I've decided to allow this for performance reasons).

A subsystem must have:
- references to all components of the type it manages.

Other things may also be holding a reference to a component. For example, if one entity is following another, a component in the follower will have a reference to a component in the followee. Nothing will ever be holding a reference to an entity, though (except the components attached to it).

When an entity is destroyed, all attached components need to be destroyed (eventually), and each component's subsystem needs to remove the component from its list.

So, can anyone recommend which types of references (shared_ptr, weak_ptr, etc.) should be used and where, and the process through which an entity and its components are destroyed? Any tips greatly appreciated.

Thanks,
--Marty



Beware of using the wrong tool for the job. The reason I say this is: by the description of your problem what you really want are specialized event responses.. "Hey objects, I'm going away forever, and these are the reasons for my departure from this world." Call out with delegates or functors or straight up callback functions and that's pretty much it.

In addition to this you can use any implementation of a weak reference to prevent undefined behaviour, which is definitely not a bad idea during development. If someone forgets to remove a reference to an object that died, then a weak reference (perhaps as implemented by weak_ptr, but be aware of its special limitations) will at least guarantee a consistent 'object no longer exists' result instead of just pointing into the ether.

Refer to a language with first-class garbage collection for a good understanding of reference strengths and why they exist. Java goes nuts by including strong, soft, weak and phantom references (geeeez).. but that makes it good for review.

Regards,




----
Darren Grant
Lead Programmer
http://www.kerberos-productions.com/
http://www.swordofthestars.com/
Jesus de Santos Garcia
2007-10-22 14:58:23 UTC
Permalink
Hi,

I think that we have here 3 different topics intermixed that I would like to
tackle separately:

- Memory Garbage Collection

I have been using successfully smart pointers + reference counting for this
purpose. They have worked so well that I cannot imagine going back to an
implementation without them. With a proper implementation plus a good
guideline manual you can achieve an implementation almost so efficient as
raw pointers:


- As a general rule, we do not allow using raw pointers. If you want
to simulate passing pointers (avoiding the AddRef()/Release()
overhead) you
must use references. In very specific scenarios (mostly internal
scenarios)
you can work with the internal pointer.
- Although, we have been tempted to typedef the smart pointer to
each engine class (as suggested above) we finally opted to using explicit
smart pointers (it is a very short name: Ptr<BaseObject> :) ).
We think it
is not a good idea to hide this detail.
- We use an intrusive implementation for reference counting. All
our engine class derive from a base class implementing the counter. This
way, we don't need an extra block of memory for the counter and
we are more
cache friendly
- Only the necessary classes implement a thread safe
AddRef()/Release(). The rest of the classes (that are not thread
safe) have
en efficient implementation.
- Cycles are a problem here yes, but we detect then immediately
because we have an intrusive leak detector (it cannot be disabled and a
message box is shown each time a leak is detected). So we usually detect
them the first time they are created and fix them: converting one of the
references to a weak pointer (using a reference smart pointer or
a true weak
pointer).

There are alternatives to Reference Counting, for example
http://www.hpl.hp.com/personal/Hans_Boehm/gc/ but didn't have time to try
it. Sounds interesting, but probably is not worth the effort as usually the
next layer in the engine (scripting) usually have this problem solved.

At higher levels, you can implement better solutions. For example, I
remember unreal engine using the serialization system to implement a garbage
collector algorithm for UObjects. We have a reflection system, so we could
use it to implement this at the level of EngineObjects (avoiding the
reference counter). Have anyone tried this approach?


- RAII

We use this pattern everywhere. This applies not only to memory, but every
resource (files, for example). This way, we ensure all our engine avoid
leaking at all levels. We use exceptions for debug versions of the engine
and for tools, so RAII is a must for our implementation. Anyway without
exceptions we would too use RAII because it creates clearer and safer code.

I recommend reading http://www.ddj.com/cpp/184403758 about ScopeGuard and
RAII.


- Entity / Component System

I think we use an implementation very similar to the one described by Kyle
Wilson. Each entity have a vector of components (smart pointers). Each
manager have a list of components (so for example, when you have to render,
you don't iterate through the entities). Entity references use weak pointers
to other entities.

We have a generic notification system (observer pattern) for all the system
that is used too at the entity level.

In conclusion, I do not recommend a generic smart pointer solution but a
specific one that suits your architecture optimally.

---
Jesus
http://entland.homelinux.com/blog/
Tom Plunket
2007-10-21 01:07:34 UTC
Permalink
Post by Marty Rabens
- a reference to each of the components attached to it.
- a reference to the entity it's attached to.
- zero or more references to other components attached to the same
entity (I've decided to allow this for performance reasons).
- references to all components of the type it manages.
Component factory returns pointers to components out of its own
internal pool. Same factory is returned those pointers/objects when
the entity is destroyed, thereby releasing the individual
components. Inter-component references are not interesting as long
as you decide to disallow inter-component communication during
entity teardown (and you can enforce this by nulling out the
references as a first step in the teardown process).
Post by Marty Rabens
So, can anyone recommend which types of references (shared_ptr,
weak_ptr, etc.) should be used and where, and the process through
which an entity and its components are destroyed? Any tips greatly
appreciated.
I'm all for experimenting with this stuff and figuring out how it
all works, but they're pretty heavyweight solutions for an Actual
Game. They require additional dynamic memory allocation, and That's
Bad; my proposal above requires no dynamic memory allocation
(although you're free to new/delete your components if you want to,
that'll break the nice locality benefits of having all components
for a system right next to one another in memory).

-tom!
Emmanuel Deloget
2007-10-21 09:26:01 UTC
Permalink
Hello,

This design looks rather suspicious to me. It seems that there is a sharing of
ownership that leads to a situation where you no longer know

* the lifetime of each object
* who really owns each object

As a consequence, I doubt that you'll find a clean and elegant design to do what
you want.

Your system have too many circular references. Circular references makes more
difficult to defines who, ultimately owns an object. In your design, an entity
can reference multiple subsystems and a subsystem can reference multiple
entities. Who owns the entity? Who decides when the entity is no longer valid
and must be destroyed? What happens when a subsystem decide to destroy the
entity and another one decides that the entity is to be used to perform another
task?

With the notion of ownership comes the notion of responsibility: difficulties to
define ownership also means that you'll have a hard time defining the class
responsibility (and to obey the SRP).

In this system, throwing smart pointers won't help much. I don't see smart
pointers as objects that allows you to forget about the notion of ownership -
and in fact, it should be used only to stress out this notion (with the help of
weak pointers).

I would suggest you to correctly define these notions in your design before you
choose a technology.

And of course, this is only an advice, based on what I read in your post - I
might be totally wrong (do not hesitate to correct me :))

Best regards,
--
.Emmanuel Deloget
.Website: http://www.emmanueldeloget.com
Post by Marty Rabens
Hello all,
First post here. I'm using smart pointers for the first time in a project
(yeah, I know - shame on me, I should have started using them years ago).
Boost smart pointers, to be specific. It's clear to me how they work, but
I'm still trying to get a handle on how to best make use of them. After a
couple decades of coding with raw pointers, it's hard to think in terms of
smart pointers.
Here's the design I'm implementing. I'm using component-based entities. An
entity object is essentially a container of components (e.g., a render
component, a physics component, etc.). Each type of component is managed by
a subsystem for that component type, which keeps track of all components of
that type. For example, the render subsystem knows about all render
components, and when it's time to render, it iterates through all of them.
An entity is created by a factory. The factory is handed an entity
definition with all the info about which components it needs and their
parameters. The factory has the subsystems create the components and
attaches them to the entity.
There is no monolithic container of entities, since everything is handled by
the subsystems.
All components derive from a common interface, and all subsystems derive
from a common interface.
What I'm having trouble with is a clean, elegant design for how everything
keeps references to everything else (using smart pointers), especially when
it comes to destroying an entity.
- a reference to each of the components attached to it.
- a reference to the entity it's attached to.
- zero or more references to other components attached to the same entity
(I've decided to allow this for performance reasons).
- references to all components of the type it manages.
Other things may also be holding a reference to a component. For example,
if one entity is following another, a component in the follower will have a
reference to a component in the followee. Nothing will ever be holding a
reference to an entity, though (except the components attached to it).
When an entity is destroyed, all attached components need to be destroyed
(eventually), and each component's subsystem needs to remove the component
from its list.
So, can anyone recommend which types of references (shared_ptr, weak_ptr,
etc.) should be used and where, and the process through which an entity and
its components are destroyed? Any tips greatly appreciated.
Thanks,
--Marty
Hernan Saez
2007-10-23 15:33:31 UTC
Permalink
Hi all,

I've been following this thread very closely since it is a very
interesting subject and I have to say that I totally agree with
Emmanuel. Ownership and responsibilities are two important concepts that
seems to became irrelevant when using smart pointers. During the last
decade I have the opportunity to work on different platforms/languages
and in cases like C or C++ where there is no garbage collection it is
imperative to have some "elegant" way to get rid of the memory problems
and in most cases that functionality is part of the model architecture.
On the other hand, in those languages that have some sort of memory
management system (like Python, Java or C#), the software architects
tend to forget what is happening with their instances. They just assume
that they are going to be deleted somehow by the almighty garbage collector.

We all know the benefits and consequences of using smart pointers. But
the point is if we should use them because they actually add some value
to our design or just because our model is too complex to manage the
different instances by design.

For example, I'm working on my own 3D engine during my free time (yes, I
still have some free time once in a while). For that purpose I'm using a
tree-like structure to handle scene entities where the parent node is
responsible for the life time of its children, and that means to delete
any child node whenever the parent is destroyed. Therefore, child nodes
are considered components of their parent (each node can have at most
one parent). I'm not using smart pointers here because it makes no
sense. A child node cannot exists without a parent (even the root node
has a "parent"). So there is no need for smart pointers there.

But what happens with those entities that are shared between several
nodes? For example, I can have state information, textures, vertexes,
etc. How can I manage them since they are not actually owned by any
single node but by all of them instead? I can use smart pointers and
reference counting there (In fact, that's what I'm using right now) but
I believe that a better approach should use managers to handle ownership
and cleanup methods for each of the shared resources.

Best,
Hernan
Post by Emmanuel Deloget
Hello,
This design looks rather suspicious to me. It seems that there is a sharing of
ownership that leads to a situation where you no longer know
* the lifetime of each object
* who really owns each object
As a consequence, I doubt that you'll find a clean and elegant design to do what
you want.
Your system have too many circular references. Circular references makes more
difficult to defines who, ultimately owns an object. In your design, an entity
can reference multiple subsystems and a subsystem can reference multiple
entities. Who owns the entity? Who decides when the entity is no longer valid
and must be destroyed? What happens when a subsystem decide to destroy the
entity and another one decides that the entity is to be used to perform another
task?
With the notion of ownership comes the notion of responsibility: difficulties to
define ownership also means that you'll have a hard time defining the class
responsibility (and to obey the SRP).
In this system, throwing smart pointers won't help much. I don't see smart
pointers as objects that allows you to forget about the notion of ownership -
and in fact, it should be used only to stress out this notion (with the help of
weak pointers).
I would suggest you to correctly define these notions in your design before you
choose a technology.
And of course, this is only an advice, based on what I read in your post - I
might be totally wrong (do not hesitate to correct me :))
Best regards,
Jon Watte
2007-10-24 02:25:46 UTC
Permalink
Post by Hernan Saez
sense. A child node cannot exists without a parent (even the root node
has a "parent"). So there is no need for smart pointers there.
Smart pointers don't have to do reference counting, they can do many
other things. The parent could have auto-delete smart pointers to its
children, meaning you don't have to iterate through your child
collection to delete the children manually.
Post by Hernan Saez
I believe that a better approach should use managers to handle ownership
and cleanup methods for each of the shared resources.
If you want weak references, those can be implemented with smart
pointers. Get a weak pointer to a texture, and the manager will zap that
pointer to NULL when the texture gets unloaded by whomever owns it.

Smart pointers are an implementation detail, kind-of like a string
class. You can use them in many ways. Trying to do reference counting
without smart pointers is folly, but using smart pointers without
reference counting is totally plausible. I think your beef seems to be
with reference counting, not smart pointers?

Cheers,

/ h+
--
-- Revenge is the most pointless and damaging of human desires.
Kyle Wilson
2007-10-25 15:16:59 UTC
Permalink
Post by Jon Watte
Smart pointers don't have to do reference counting, they can do many
other things. The parent could have auto-delete smart pointers to its
children, meaning you don't have to iterate through your child
collection to delete the children manually.
[...]
Post by Jon Watte
Smart pointers are an implementation detail, kind-of like a string
class. You can use them in many ways.
That's true, and bears repeating. If you ever have to explicitly call
delete, you're doing something wrong. It's just too easy, with early error
code returns, exceptions, and multiple developers working in the same code,
for someone not to clean up properly. No matter how you want object
lifetime to be managed--whether ref-counted or scoped, thread-safe or
not--there's an RAII container that can do so safely.

I'm particularly bitter about this at the moment since I've just spent three
days tracking down a memory leak in a piece of code that does *not* use RAII
and that has vague and shifting rules for ownership.

Regards,
Kyle
Tom Plunket
2007-10-25 18:43:13 UTC
Permalink
Post by Kyle Wilson
Post by Jon Watte
Smart pointers don't have to do reference counting, they can do many
other things. The parent could have auto-delete smart pointers to its
children, meaning you don't have to iterate through your child
collection to delete the children manually.
[...]
Post by Jon Watte
Smart pointers are an implementation detail, kind-of like a string
class. You can use them in many ways.
That's true, and bears repeating. If you ever have to explicitly call
delete, you're doing something wrong. It's just too easy, with early error
code returns, exceptions, and multiple developers working in the same code,
for someone not to clean up properly. No matter how you want object
lifetime to be managed--whether ref-counted or scoped, thread-safe or
not--there's an RAII container that can do so safely.
I'm particularly bitter about this at the moment since I've just spent three
days tracking down a memory leak in a piece of code that does *not* use RAII
and that has vague and shifting rules for ownership.
Regards,
Kyle
_______________________________________________
Sweng-Gamedev mailing list
http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-midnightryder.com
-tom!
Tom Plunket
2007-10-25 18:55:54 UTC
Permalink
That was a fun previous post. I'd written something there, I
promise...

<sigh>
No matter how you want object lifetime to be managed--whether
ref-counted or scoped, thread-safe or not--there's an RAII
container that can do so safely.
Resource Acquisition Is Initialization changed recently? I mean, I
see that RAII is now considered to be about implicit memory
management and finalization, but I thought for sure it used to be,
well, what its name said it was, that when you acquired an object
you were guaranteed that it would be initialized... And nothing
more.
I'm particularly bitter about this at the moment since I've just
spent three days tracking down a memory leak in a piece of code
that does *not* use RAII and that has vague and shifting rules for
ownership.
In some senses I think it's inevitable; game programmers really love
their 'new's, and tend to only think about their 'delete's when they
get a bug report. But yeah, I'm with you. Implicit lifetime
control for the win. Static pools and function-scoped objects are
all you need.

-tom!
Kyle Wilson
2007-10-26 04:23:15 UTC
Permalink
Post by Tom Plunket
Resource Acquisition Is Initialization changed recently? I mean, I
see that RAII is now considered to be about implicit memory
management and finalization, but I thought for sure it used to be,
well, what its name said it was, that when you acquired an object
you were guaranteed that it would be initialized... And nothing
more.
RAII has always been a lousy acronym, because Resource Acquisition Is
Initialization has never just meant that "a resource is acquired when an
object is initialized". That aspect is much less significant than the
corollary, which is that "a resource is released when an object is
destroyed". Hence, a mutex objects acquires a lock in its constructor and
releases it in its destructor. A Boost shared_ptr adds a reference in its
constructor and releases it in its destructor. A standard auto_ptr take
ownership of a dynamically-allocated object in its constructor and deletes
that object in its destructor.

"Scoped ownership" probably would have been a better name for the idiom, but
RAII caught on and seems to be here to stay.

Kyle
Thatcher Ulrich
2007-10-27 23:02:59 UTC
Permalink
Post by Jon Watte
Post by Hernan Saez
sense. A child node cannot exists without a parent (even the root node
has a "parent"). So there is no need for smart pointers there.
Smart pointers don't have to do reference counting, they can do many
other things. The parent could have auto-delete smart pointers to its
children, meaning you don't have to iterate through your child
collection to delete the children manually.
Post by Hernan Saez
I believe that a better approach should use managers to handle ownership
and cleanup methods for each of the shared resources.
If you want weak references, those can be implemented with smart
pointers. Get a weak pointer to a texture, and the manager will zap that
pointer to NULL when the texture gets unloaded by whomever owns it.
Smart pointers are an implementation detail, kind-of like a string
class. You can use them in many ways. Trying to do reference counting
without smart pointers is folly, but using smart pointers without
reference counting is totally plausible. I think your beef seems to be
with reference counting, not smart pointers?
Slight spin on this, I agree that smart pointers != reference
counting, but also that the implementation details are very important.
Based on my recent experience with scoped_ptr, and less recent
experience with reference-counted smart pointers:

* I don't like reference-counting smart pointers (a la shared_ptr).
Admittedly they are appropriate when the objects form an acyclic
directed ownership graph, like many scene graphs. However, I consider
an ADG scene graph somewhat specialized, and ref-counting is way
overused in other situations.

* I do like scoped_ptr. scoped_ptr is appropriate when you have a
tree-like ownership graph. This does not apply to many
bag-of-autonomous-entities game situations but it does apply to many
other cases where ref counting is often used. scoped_ptr is much
simpler, it makes ownership totally explicit, and it's much harder to
foul up than ref-counting. When you make a mistake it is generally
much easier to debug. Also more efficient (it's basically free).

When you find yourself reaching for shared_ptr, consider whether you
can use scoped_ptr instead.

-T
Jon Watte
2007-10-28 18:12:44 UTC
Permalink
Post by Thatcher Ulrich
Based on my recent experience with scoped_ptr, and less recent
scoped_ptr (basically, auto-delete) is great.

I would still use smart pointers for reference counted items, but I
mostly reference count things like COM interfaces, and there we already
have a decent smart pointer: CComPtr<>.

We have this problem of:
- materials X and Y use texture Z
- stuff with those materials is added to the draw queue
- materials X and Y are removed before draw queue is emptied
- when draw queue comes to the added stuff, texture Z still needs to be
around

I used to have a method where a "deleted" rendering resource would be
deferred until drawqueue was empty, and then actually deleted, but
someone else felt that was too specialized and changed it to pure
reference counting. Pure ref counting works, too, but I can't help but
to mentally count those add/subtract memory cycles in the back of my mind...

Cheers,

/ h+
--
-- Revenge is the most pointless and damaging of human desires.
Darren Grant
2007-10-24 05:14:39 UTC
Permalink
Post by Hernan Saez
But what happens with those entities that are shared between several
nodes? For example, I can have state information, textures, vertexes,
etc. How can I manage them since they are not actually owned by any
single node but by all of them instead? I can use smart pointers and
reference counting there (In fact, that's what I'm using right now) but
I believe that a better approach should use managers to handle ownership
and cleanup methods for each of the shared resources.
Yes, grouping resources into relevant units, like levels, sectors or
stages, is a common trick of the trade. It is always an effective
sharing strategy in a controlled system (and games are usually pretty
tightly controlled) because it is very simple and direct. On the
other side of the spectrum, if you really don't know what will be
coming and going from moment to moment, then that is a good hint to
seek out an advanced algorithm if not a better language to protect
yourself from the management details.

I have heard of many developers who have also had success by placing
resource units in an ownership hierarchy for more dynamic scenarios,
which is still pretty simple.

Regards,


----
Darren Grant
Lead Programmer
http://www.kerberos-productions.com/
http://www.swordofthestars.com/
Conor Stokes
2007-10-22 15:44:58 UTC
Permalink
This sort of style has been positive in my experience as well. The other thing is that if you use exception handling (some do, some think it's the devil) then RAII in general and smart pointers in particular start to make a lot more sense.

In C++ RAII/Smart pointers is the only way to avoid leaks in some exceptional circumstances.

Cheers,
Conor

----- Original Message ----
From: Kyle Wilson <***@gamearchitect.net>
To: sweng-***@midnightryder.com
Sent: Monday, October 22, 2007 12:54:29 AM
Subject: Re: [Sweng-Gamedev] Designing with smart pointers








Unlike most of the people who've responded, my
experience with smart pointers has been tremendously positive. In Despair
(the engine Day 1 is using in Fracture and another, as-yet-unannounced,
shooter), we have about ten different smart pointer classes. I've had
very smart, experienced engineers from publishers roll their eyes and say,
"One smart pointer type is too many!" when I tell them that, but in actual
practice they've worked so well for us that I couldn't imagine working again on
an engine that didn't use smart pointers.



It's important that the programmers working with
smart pointers realize:


Smart pointers are not the same as raw
pointers. Smart pointers are a shorthand for managing AddRefs and
Releases, but you can't use them effectively without realizing when they're
going to allocate memory or change reference counts.
Smart pointers are not the same as garbage
collection. It's imperative that you avoid cyclic strong references, or
you may suddenly go from not leaking any memory to leaking every object you
allocate. This happens to us about once a year, and it's several hours
of annoying work to track down the cause.
We've mitigated the costs of smart pointers by
regular reiteration of tips like "Pass smart pointers by reference, not by
value" and "Establish strong references at load time and use them at
runtime." We've also created different smart pointer types for different
situations. The vast majority of objects in our engine are
internally-reference-counted and will never have AddRef/Release called on them
from multiple threads at the same time. We don't have to allocate memory
for an external reference count and we don't have to do
InterlockedIncrement/InterlockedDecrement, which makes smart pointers to these
objects much more efficient than boost::shared_ptr. (Using our own smart
pointers instead of boost::shared_ptr also lets us change some points of
style. Our shared_ptrs are implicitly constructible from NULL, so
engineers can continue to use the more natural "if (myPtr != NULL)"
syntax.)



Among our smart pointer gallimaufry
are:


scoped_ptr - our version of std::auto_ptr, without
the unnatural assignment operator
shared_ptr - a non-threadsafe externally
ref-counted strong pointer
ref_ptr - an internally ref-counted strong pointer
(thread safe or not depending on how AddRef and Release are
implemented)
ComPtr - the same, but for COM objects
HavokRefPtr - the same, but for Havok
objects
res_ptr - a pointer to resources being loaded from
our streaming thread... it becomes non-NULL when the resource load completes,
and afterward behaves like a ref_ptr
weak_ptr - an intrusive weak
pointer
We could significantly reduce our number of smart
pointer types if C++ supported template typedefs, and we could just typedef one
smart pointer class with different policies. We still use
boost::shared_ptr in places, but I keep meaning to replace it with a
threadsafe atomic_shared_ptr of our own, since the minor usage differences
are annoying.



So far I've talked a lot about mitigating the costs
of shared pointers without saying anything about the benefits, which I'm afraid
may give the wrong impression. In practice, it's obvious what pointer is
best for a given situation, and most of the smart pointers used in the game are
typedefed in core libraries so that gameplay engineers can use them without even
having to think about their details. The daily cost of our use of smart
pointers is negligible. The daily benefit is that the Despair engine
doesn't leak memory. Somebody introduces a memory leak maybe once every
two or three months, and when it happens it's invariably a junior engineer who
for some reason has decided not to use the common idioms of our software
development process. I credit our use of smart pointers, our rules for
ownership, and our use of the RAII idiom for giving us an engine that's more
stable than any I've ever worked on before, even as our team has grown from ten
engineers to thirty.



To get back to your particular circumstance, the
game object system you describe is actually a lot like ours. You just need
to define your ownership hierarchy and figure out how to make weak references
for all your other connections. The way we do this actually doesn't
involve very many smart pointers, since most of the inter-object references are
weak references that aren't exactly lifetime dependent:


An entity owns a std::vector of components.
You could use raw pointers for this and delete them explicitly when they're
removed or when the object goes away. We use smart pointers because we
have inter-object inheritance, so components are sometimes shared in prototype
objects. That means our components have to be reference-counted, and
once an object is reference-counted, you're safer using a ref_ptr to hold
onto it. For actual simulation objects, though, the rule is that when an
object goes away, all its components go away.
All entities and components have an explicit
OnAddToScene call. Components register with their managers (like your
subsystems) here. They also look up other components that they need to
know about. The work here is all raw pointers, except when one component
references a component in another object. Those indirect through a weak
pointer type that goes NULL when the other component is removed from the
scene.
All entities and components have an
explicit OnRemoveFromScene call. Components unregister with their
managers here.
The OnAddToScene / OnRemoveFromScene calls exist
because an object's existence doesn't necessarily mean that it should be part of
the simulation. We stream objects in a separate thread, and we don't want
them simulating until their whole world has finished loading, has been returned
to the main game thread, and can be added to the scene as a single
unit.



I hope this is helpful. Good luck with your
engine.



Regards,

Kyle





----- Original Message -----
From: "Marty Rabens" <***@rabens.com>

To: <sweng-***@midnightryder.com>

Sent: Sunday, October 21, 2007 11:07
AM

Subject: Re: [Sweng-Gamedev] Designing with smart
pointers
Post by Marty Rabens
Wow. I was hoping for a response with a couple tips, but I
didn't expect
Post by Marty Rabens
this much insightful edification from this many
people! I really appreciate
Post by Marty Rabens
it (and I have to say, I'm impressed
with the quality of people on this
Post by Marty Rabens
list).
I just wanted
to let everyone know that I am reading all of the responses
Post by Marty Rabens
and
discussion. It'll take me a bit to digest it all, so I don't have
any
Post by Marty Rabens
kind of meaningful reply just yet.
I will say a
couple things to clarify where I'm coming from.
Post by Marty Rabens
First, the
primary problem I'm ultimately trying to solve is things having
Post by Marty Rabens
stale
pointers to destroyed entities. I'm also using component-based
entities for the first time (I've always hated the mess that results from
an
Post by Marty Rabens
entity class hierarchy, and I want the flexibility of data-driven
entity
Post by Marty Rabens
definition).
Admittedly, I'd read good things
about smart pointers, so I had this shiny
Post by Marty Rabens
new hammer and was seeing an
awful lot of nails around. They seemed like a
Post by Marty Rabens
reasonable solution
to stale pointers, and the things I've read seemed to
Post by Marty Rabens
indicate using
them consistently, so I was trying to use them throughout my
design. I now see the error of my ways. I'm certainly not dead-set
on
Post by Marty Rabens
using them.
The project I'm working on is one of
those personal experimental things in
Post by Marty Rabens
my free time (my day job is as a
programmer at a small game studio). It's
Post by Marty Rabens
Windows platform, and I'm
sure will never see a console. Performance and
Post by Marty Rabens
efficiency is
definitely a factor, though, since the game design involves
Post by Marty Rabens
large swarms
of hundreds (or thousands) of entities.
Post by Marty Rabens
Again, thanks to
everyone for all of the useful info.
--Marty





__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com
speedy
2007-10-22 15:57:44 UTC
Permalink
Hello Sweng-gamdedev crew,

on the exception handling tangent, even if one does not use them,
they have to be enabled for the purpouse of catching Kernel and
C Run Time errors and acting upon them. (crash dumping, backtrace
printing...)

Is there any way to do it without them enabled? O:)

Monday, October 22, 2007, 5:44:58 PM, you wrote:

CS> This sort of style has been positive in my experience as well.
CS> The other thing is that if you use exception handling (some do,
CS> some think it's the devil) then RAII in general and smart pointers
CS> in particular start to make a lot more sense.
--
Best regards,
speedy mailto:***@3d-io.com
r***@realtimeworlds.com
2007-10-22 16:04:56 UTC
Permalink
There is a big difference between Win32 exceptions and C++ exceptions,
they are handled completely differently and have differing semantics. So
it is possible to switch off C++ exceptions and still handle Win32
exceptions successfully (and with a lot less code).

<cat amongst pigeons>
Still you could use C# and managed code and avoid a lot of these issues
that you are discussing...

Seriously we are moving most of our development over to Managed code and
are not looking back (we are using Mono on non-Windows platforms) with
C++ only being used for very time critical sections, we have a big boost
in productivity with C# and we have not seen any unacceptable
performance loss.

Something to consider.
</cat amongst pigeons>

Russell

-----Original Message-----
From: sweng-gamedev-***@lists.midnightryder.com
[mailto:sweng-gamedev-***@lists.midnightryder.com] On Behalf Of
speedy
Sent: 22 October 2007 16:58
To: Conor Stokes
Cc: sweng-***@midnightryder.com
Subject: [Sweng-Gamedev] VC++/win32 exception handling [Was:
Re[2]:Designing with smart pointers]

Hello Sweng-gamdedev crew,

on the exception handling tangent, even if one does not use them,
they have to be enabled for the purpouse of catching Kernel and
C Run Time errors and acting upon them. (crash dumping, backtrace
printing...)

Is there any way to do it without them enabled? O:)

Monday, October 22, 2007, 5:44:58 PM, you wrote:

CS> This sort of style has been positive in my experience as well.
CS> The other thing is that if you use exception handling (some do,
CS> some think it's the devil) then RAII in general and smart pointers
CS> in particular start to make a lot more sense.
--
Best regards,
speedy mailto:***@3d-io.com

_______________________________________________
Sweng-Gamedev mailing list
Sweng-***@lists.midnightryder.com
http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-midnightryder.
com

____________________________________________________________________
This email has been scanned by the MessageLabs Email Security System

DISCLAIMER
This message and any attachments contain privileged and confidential information intended for the use of the addressee named above. If you are not the intended recipient of this message, you are hereby notified that any use, dissemination, distribution or reproduction of this message is prohibited. Please note that we cannot guarantee that this message or any attachment is virus free or that it has not been intercepted and amended. The views of the author may not necessarily reflect those of Real Time Worlds Ltd.
____________________________________________________________________
This email has been scanned by the MessageLabs Email Security System
speedy
2007-10-22 17:17:37 UTC
Permalink
Hello Russell,

Monday, October 22, 2007, 6:04:56 PM, you wrote:

rkrc> There is a big difference between Win32 exceptions and C++ exceptions,
rkrc> they are handled completely differently and have differing semantics. So
rkrc> it is possible to switch off C++ exceptions and still handle Win32
rkrc> exceptions successfully (and with a lot less code).

Hmm - they are the same in that MS CRT uses the same mechanism of
delivering them - namely - C++ exception handlers? Can you provide a
bit more detail to your claim?

rkrc> <cat amongst pigeons>
rkrc> Still you could use C# and managed code and avoid a lot of these issues
rkrc> that you are discussing...

rkrc> Seriously we are moving most of our development over to Managed code and
rkrc> are not looking back (we are using Mono on non-Windows platforms) with
rkrc> C++ only being used for very time critical sections, we have a big boost
rkrc> in productivity with C# and we have not seen any unacceptable
rkrc> performance loss.

rkrc> Something to consider.
rkrc> </cat amongst pigeons>

C++ & Lua combo here. Lua - free, quick, tiny, portable, robust,
proven in large productions, has JIT, and backed with full source code.

rkrc> Russell

Cheers!
--
Best regards,
speedy mailto:***@3d-io.com
Jesus de Santos Garcia
2007-10-22 19:08:31 UTC
Permalink
The C++ exception model is broken is this sense.

C++ exception do not mix very well with Operating System Exception (In
windows, SEH, Structured Exception Handled).

You can have crash dumping of Operating System Exceptions without enabling
C++ exceptions in your project: SetUnhandledExceptionFilter if your ally
here (for Windows). :)
Post by speedy
Hello Sweng-gamdedev crew,
on the exception handling tangent, even if one does not use them,
they have to be enabled for the purpouse of catching Kernel and
C Run Time errors and acting upon them. (crash dumping, backtrace
printing...)
Is there any way to do it without them enabled? O:)
CS> This sort of style has been positive in my experience as well.
CS> The other thing is that if you use exception handling (some do,
CS> some think it's the devil) then RAII in general and smart pointers
CS> in particular start to make a lot more sense.
--
Best regards,
_______________________________________________
Sweng-Gamedev mailing list
http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-midnightryder.com
speedy
2007-11-15 01:11:33 UTC
Permalink
Hello Jesus, Russell,

It turned out that C++ exception stack unwinding can occasionaly
mess up the call stack - which got fixed by disabling C++ exceptions
compile option.

SetUnhandledExceptionFilter() works even without C++ exceptions
enabled - I'm really glad to be rid of that essentially unused
option. ;)

As a side note, perhaps useful to someone, ATI OpenGL drivers
also mess up the stack, but one can recover quite precise and
useful info from the minidumps using WinDbg's .ecxr and then
dps esp+offset.

Thanks for the great advice guys! :]

Monday, October 22, 2007, 8:08:31 PM, you wrote:

JdSG> The C++ exception model is broken is this sense.

JdSG> C++ exception do not mix very well with Operating System
JdSG> Exception (In windows, SEH, Structured Exception Handled).

JdSG> You can have crash dumping of Operating System Exceptions
JdSG> without enabling C++ exceptions in your project:
JdSG> SetUnhandledExceptionFilter if your ally here (for Windows). :)

JdSG> On 10/22/07, speedy <***@3d-io.com> wrote:
JdSG> Hello Sweng-gamdedev crew,

JdSG>       on the exception handling tangent, even if one does not use them,
JdSG>       they have to be enabled for the purpouse of catching Kernel and
JdSG>       C Run Time errors and acting upon them. (crash dumping, backtrace
JdSG>       printing...)

JdSG>       Is there any way to do it without them enabled? O:)

JdSG> Monday, October 22, 2007, 5:44:58 PM, you wrote:

CS>> This sort of style has been positive in my experience as well.
CS>> The other thing is that if you use exception handling (some do,
CS>> some think it's the devil) then RAII in general and smart pointers
CS>> in particular start to make a lot more sense.

JdSG> --
JdSG> Best regards,
JdSG> speedy                            mailto:***@3d-io.com

JdSG> _______________________________________________
JdSG> Sweng-Gamedev mailing list
JdSG> Sweng-***@lists.midnightryder.com
JdSG> http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-midnightryder.com

JdSG>

JdSG>
--
Best regards,
speedy mailto:***@3d-io.com
Jesus de Santos Garcia
2007-11-15 09:33:26 UTC
Permalink
You are using a manual call-stack recovering algorithm, aren't you?

A safer (and a lot slower) option is using Debugging Tools for this. You
should never get a messed up call-stack with this option.
Post by speedy
Hello Jesus, Russell,
It turned out that C++ exception stack unwinding can occasionaly
mess up the call stack - which got fixed by disabling C++ exceptions
compile option.
SetUnhandledExceptionFilter() works even without C++ exceptions
enabled - I'm really glad to be rid of that essentially unused
option. ;)
As a side note, perhaps useful to someone, ATI OpenGL drivers
also mess up the stack, but one can recover quite precise and
useful info from the minidumps using WinDbg's .ecxr and then
dps esp+offset.
Thanks for the great advice guys! :]
JdSG> The C++ exception model is broken is this sense.
JdSG> C++ exception do not mix very well with Operating System
JdSG> Exception (In windows, SEH, Structured Exception Handled).
JdSG> You can have crash dumping of Operating System Exceptions
JdSG> SetUnhandledExceptionFilter if your ally here (for Windows). :)
JdSG> Hello Sweng-gamdedev crew,
JdSG> on the exception handling tangent, even if one does not use them,
JdSG> they have to be enabled for the purpouse of catching Kernel and
JdSG> C Run Time errors and acting upon them. (crash dumping, backtrace
JdSG> printing...)
JdSG> Is there any way to do it without them enabled? O:)
CS>> This sort of style has been positive in my experience as well.
CS>> The other thing is that if you use exception handling (some do,
CS>> some think it's the devil) then RAII in general and smart pointers
CS>> in particular start to make a lot more sense.
JdSG> --
JdSG> Best regards,
JdSG> _______________________________________________
JdSG> Sweng-Gamedev mailing list
JdSG>
http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-midnightryder.com
JdSG>
JdSG>
--
Best regards,
_______________________________________________
Sweng-Gamedev mailing list
http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-midnightryder.com
speedy
2007-11-17 12:21:55 UTC
Permalink
Hello Jesus,

I'm using the one from dbghelp.dll. (StackWalk64())

Using C++ exception handlers (/EHsc) and try / catch block
I've had the bug show up as crash in kernel32.dll with
unusable stack:

pr_no_editor caused a Microsoft C++ Exception (0xe06d7363)
in module kernel32.dll at 001b:7c812a5b.

and with exceptions disabled and SetUnhandledExceptionFilter()
I've got the proper error message and WinDbg-explorable stack.

pr_no_editor_crash_test caused an Access Violation (0xc0000005)
in module atioglxx.dll at 001b:690ea637.

I didn't dig into the stack-unwinding / kernel mode crash
interaction, because your method simplified and fixed things
gracefully. :)

Thursday, November 15, 2007, 10:33:26 AM, you wrote:

JdSG> You are using a manual call-stack recovering algorithm, aren't you?

JdSG> A safer (and a lot slower) option is using Debugging Tools for
JdSG> this. You should never get a messed up call-stack with this option.

JdSG> On Nov 15, 2007 2:11 AM, speedy <***@3d-io.com> wrote:
JdSG> Hello Jesus, Russell,

JdSG>      It turned out that C++ exception stack unwinding can occasionaly
JdSG>      mess up the call stack - which got fixed by disabling C++ exceptions
JdSG>      compile option.

JdSG>      SetUnhandledExceptionFilter() works even without C++ exceptions
JdSG>      enabled - I'm really glad to be rid of that essentially unused
JdSG>      option. ;)

JdSG>      As a side note, perhaps useful to someone, ATI OpenGL drivers
JdSG>      also mess up the stack, but one can recover quite precise and
JdSG>      useful info from the minidumps using WinDbg's .ecxr and then
JdSG>      dps esp+offset.

JdSG>      Thanks for the great advice guys! :]

JdSG> Monday, October 22, 2007, 8:08:31 PM, you wrote:

JdSG>> The C++ exception model is broken is this sense.

JdSG>> C++ exception do not mix very well with Operating System
JdSG>> Exception (In windows, SEH, Structured Exception Handled).

JdSG>> You can have crash dumping of Operating System Exceptions
JdSG>> without enabling C++ exceptions in your project:
JdSG>> SetUnhandledExceptionFilter if your ally here (for Windows). :)

JdSG>> On 10/22/07, speedy <***@3d-io.com > wrote:
JdSG>>  Hello Sweng-gamdedev crew,

JdSG>> on the exception handling tangent, even if one does not use them,
JdSG>> they have to be enabled for the purpouse of catching Kernel and
JdSG>> C Run Time errors and acting upon them. (crash dumping, backtrace
JdSG>> printing...)

JdSG>> Is there any way to do it without them enabled? O:)

JdSG>> Monday, October 22, 2007, 5:44:58 PM, you wrote:

CS>>> This sort of style has been positive in my experience as well.
 CS>>> The other thing is that if you use exception handling (some do,
CS>>> some think it's the devil) then RAII in general and smart pointers
CS>>> in particular start to make a lot more sense.

JdSG>> --
JdSG>>  Best regards,
JdSG>>  speedymailto:***@3d-io.com

JdSG>> _______________________________________________
JdSG>> Sweng-Gamedev mailing list
JdSG>>  Sweng-***@lists.midnightryder.com
JdSG>> http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-midnightryder.com

JdSG>>

JdSG>>


JdSG> --
JdSG> Best regards,
JdSG>  speedy                            mailto: ***@3d-io.com

JdSG> _______________________________________________
JdSG> Sweng-Gamedev mailing list
JdSG> Sweng-***@lists.midnightryder.com
JdSG> http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-midnightryder.com

JdSG>
JdSG>
--
Best regards,
speedy mailto:***@3d-io.com
Loading...