On Monday, 17 December 2012 at 08:23:02 UTC, Jeremy DeHaan wrote:
I am somewhat confused by some things that are being talked
about in this thread.
Mike Parker said that this Image is handled by GC. Would that
mean that any class members are handled by the GC too? If so,
wouldn't the sfImage* be collected by the GC?
Any class members that are allocated on the D heap, sure. But the
memory for sfImage is allocated by sfml outside of the D garbage
collector's heap. The GC knows nothing about it. The GC can only
free the memory it controls. When you allocate memory from a C
library, you are responsible for cleaning it up. But, you cannot
rely on a class destructor to do this for you. There is
absolutely no guarantee about when or if class destructors will
be run, or in what order, during program execution. So relying on
a destructor is totally nondeterministic.
And when using Derelict, if you try to call a bound library in a
destructor, but the object is not released until the GC cleans up
at program exit, you will always get a crash because Derelict
will have already unloaded
If not, and you DO need to manually delete the pointer, is
there anything wrong with something like:
~this
{
destroy(image);
}
See above. You should never do this sort of thing in a destructor.
I am just thinking that it would suck to have to manually call
destroy/delete on each object before exiting main. I tested
this, and I didn't get any memory errors, so I can assume it is
working? I did get an error if I used delete image, however.
What's the difference between the two?
I'm not quite sure what you are referring to when you say, "I
tested this." Which? Freeing the image in a destructor?
I always clean up manually. When making a game in D, this is the
approach I take.
==================
module mygame;
private void init()
{
// Throw Error on failure
DerelictFoo.load();
graphicsInit();
audioInit();
...
}
private void term()
{
// Release all resources here
...
audioTerm();
graphicsTerm();
}
void main(string[] args)
{
scope(exit) term();
init();
gameLoop();
}
=================
Then in each subsystem, there's always some sort of
list/manager/registry that managers different objects. Each of
these will have initialize/terminate method/free functions, and
all of the objects they track that need to cleanup will have a
terminate method. For example, I might have an aa in the graphics
module that serves as a texture registry. And each texture will
have a terminate method that calles into OpenGL to delete a
texture.
=========
private Texture[string] _texRegistry;
class Texture
{
private uint id;
public void term()
{
glDeleteTextures(1, &id);
}
}
void gfxTerm()
{
if(!DerelictGL3.isLoaded) return;
foreach(tex; _texRegistry.byValue)
tex.term();
clear(_texRegistry);
}
=========
The scope(exit) in the main method ensures that this termination
chain is run every time the app exits regardless of the reason --
even when the Derelict libraries fail to load. So, if I'm going
to be calling into the bound libraries in any of the termination
functions, then at the top of the manager-level terminators I
test to make sure the lib is loaded. If it isn't, I exit quick.
Because loading the libraries is always the very first thing I
do, if the libraries aren't loaded then there aren't going to be
any resources to release.
For any short lived objects, I still don't rely on destructors
but call the terminator manually. Perhaps it's my years of
programming in C without ctors and dtors, but I find this process
painless and just as easy as using a destructor.