www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to wrap SDL?

reply Sean Eskapp <eatingstaples gmail.com> writes:
I'm using the Derelict SDL bindings, and I'm wrapping some parts of SDL in my
own objects. Originally, I was using classes, but this caused a number of
errors when the program exited, since Derelict unloaded itself before the
garbage collector took care of destructing my classes.

So I switched to scope classes. This works fine, but I've been told they're
being removed from the language (?) and to use structs instead for RAII.

How should I be wrapping this SDL stuff?
Jan 22 2011
next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
You can use scoped!() from std.typecons:

import std.stdio;
import std.typecons;

class A
{
    ~this()
    {
        writeln("A destructor");
    }
}

void foo()
{
    auto a1 = scoped!A();
}

void main()
{
    foo();
    writeln("exiting..");
}


You must not escape a reference to the object outside of the foo()
scope. Note that you will get a runtime error if you try to do
something like this:

import std.stdio;
import std.typecons;

class A
{
    ~this()
    {
        writeln("A destructor");
    }
}

auto foo()
{
    auto a1 = scoped!A();
    return a1;
}

void main()
{
    auto result = foo();
    writeln("exiting..");
}

Illegal call to Scoped this(this)
A destructor
core.exception.AssertError std.typecons(2506): Assertion failure
Jan 22 2011
parent reply Sean Eskapp <eatingstaples gmail.com> writes:
== Quote from Andrej Mitrovic (andrej.mitrovich gmail.com)'s article
 You can use scoped!() from std.typecons:
 import std.stdio;
 import std.typecons;
 class A
 {
     ~this()
     {
         writeln("A destructor");
     }
 }
 void foo()
 {
     auto a1 = scoped!A();
 }
 void main()
 {
     foo();
     writeln("exiting..");
 }
 You must not escape a reference to the object outside of the foo()
 scope. Note that you will get a runtime error if you try to do
 something like this:
 import std.stdio;
 import std.typecons;
 class A
 {
     ~this()
     {
         writeln("A destructor");
     }
 }
 auto foo()
 {
     auto a1 = scoped!A();
     return a1;
 }
 void main()
 {
     auto result = foo();
     writeln("exiting..");
 }
 Illegal call to Scoped this(this)
 A destructor
 core.exception.AssertError std.typecons(2506): Assertion failure

But these can't be passed to functions either, meaning I can't pass a Screen, Text, or Font wrapper around, all of which I use in my project!
Jan 22 2011
next sibling parent Christopher Nicholson-Sauls <ibisbasenji gmail.com> writes:
On 01/22/11 14:53, Sean Eskapp wrote:
 == Quote from Andrej Mitrovic (andrej.mitrovich gmail.com)'s article
 You can use scoped!() from std.typecons:
 import std.stdio;
 import std.typecons;
 class A
 {
     ~this()
     {
         writeln("A destructor");
     }
 }
 void foo()
 {
     auto a1 = scoped!A();
 }
 void main()
 {
     foo();
     writeln("exiting..");
 }
 You must not escape a reference to the object outside of the foo()
 scope. Note that you will get a runtime error if you try to do
 something like this:
 import std.stdio;
 import std.typecons;
 class A
 {
     ~this()
     {
         writeln("A destructor");
     }
 }
 auto foo()
 {
     auto a1 = scoped!A();
     return a1;
 }
 void main()
 {
     auto result = foo();
     writeln("exiting..");
 }
 Illegal call to Scoped this(this)
 A destructor
 core.exception.AssertError std.typecons(2506): Assertion failure

But these can't be passed to functions either, meaning I can't pass a Screen, Text, or Font wrapper around, all of which I use in my project!

Then try struct, with parameters mostly defined as ref. And possibly take a look at std.typecons.RefCounted. -- Chris N-S
Jan 22 2011
prev sibling parent Dan Olson <zans.is.for.cans yahoo.com> writes:
Sean Eskapp <eatingstaples gmail.com> writes:

 == Quote from Andrej Mitrovic (andrej.mitrovich gmail.com)'s article
 You can use scoped!() from std.typecons:


snip
 You must not escape a reference to the object outside of the foo()
 scope. Note that you will get a runtime error if you try to do
 something like this:
 import std.stdio;
 import std.typecons;
 class A
 {
     ~this()
     {
         writeln("A destructor");
     }
 }
 auto foo()
 {
     auto a1 = scoped!A();
     return a1;
 }
 void main()
 {
     auto result = foo();
     writeln("exiting..");
 }
 Illegal call to Scoped this(this)
 A destructor
 core.exception.AssertError std.typecons(2506): Assertion failure

But these can't be passed to functions either, meaning I can't pass a Screen, Text, or Font wrapper around, all of which I use in my project!

It should be ok to pass A to a function for temporary use. { auto a1 = scoped!A(); dostuff(a1); } void dostuff(A a) { // .. but don't hang on to 'a' because dtor will be eventually called } Dan
Jan 23 2011
prev sibling parent Stanislav Blinov <blinov loniir.ru> writes:
22.01.2011 23:04, Sean Eskapp пишет:
 I'm using the Derelict SDL bindings, and I'm wrapping some parts of SDL in my
 own objects. Originally, I was using classes, but this caused a number of
 errors when the program exited, since Derelict unloaded itself before the
 garbage collector took care of destructing my classes.

 So I switched to scope classes. This works fine, but I've been told they're
 being removed from the language (?) and to use structs instead for RAII.

 How should I be wrapping this SDL stuff?

right?), I remember I intended to write this but I think I got distracted and forgot about it. You can go the other way around: Since the initial problem with segfaults occurs because the order of calling dtors for your objects and dtor for derelict.sdl.sdl is indeterminable, you may want to somewhat 'determine' it: 1. Create a module, say, sdlresource.d. 2. In this module, import derelict.sdl.sdl. (see below why). 3. In this module, create a base class for all your SDL wrappers. 4. This base class' (default) ctor should store a 'this' reference in some (module-private) storage such as array/AA. 5. This base class' dtor should remove 'this' reference from that storage. 6. Create a static dtor for sdlresource.d, which would iterate the storage and delete/clear() all references that are still left at that point. That's it. You now can use dtors of subclasses to customize SDL resource deallocation. Such approach guarantees that DerelictSDL will not be unloaded before all your resources are freed (because sdlresource.d imports derelict.sdl.sdl). The only caveat that's left is calling that dtor. For now, you can still use 'delete', but you'll have to switch to clear() later, when it's finished and 'delete' is finally deprecated. But that's easily localized by creating something like this: void safeDelete(T)(T p) if (is(T == class) || is(pointerTarget!(T) == struct)) // pointerTarget is in std.traits { delete p; // switch to clear() later } With this approach, either the GC will deallocate resources, or forced deallocation in sdlresource.d's static destructor will occur. I agree, this is a hacky method, but it should work well. The 'storage' may be a simple array. In this case, for the sake of efficiency, resources may be indexed and simple unstable removal (backswapping and removing last element) may be used. PS. The other 'caveat' that actually messes not only this approach, but the whole Derelict usage, is that in Derelict modules, loader objects (such as DerelictSDL) and static dtors are not shared. (This is actually a result of unfinished introduction for handling TLS by default in D2). That means that in a multithreaded program, as soon as one of the threads finishes, Derelict bindings will be invalidated for the whole application. That's nasty, I should probably mention that to aldacron or even come up with the fix. I hope this helps.
Jan 24 2011