Archive for November, 2008

Callback system in D

Posted in Development on November 24th, 2008 by Nayruden – 2 Comments

We’ve been evaluating D for use in Daydream, and I decided to see how easy it would be to create a callback system in the D  language (aka events or signals). This is a daunting task in C++ because C++ templates can only accept a static number of arguments… very bad when you have a function that can accept any number of arguments. To solve this problem in C++ you need to create a separate template for each number of possible arguments.

In D you can create templates that accept any number of arguments! You can treat these as a tuple, an array, or use them with tail recursion (à la PROLOG).

Combine this with the natural awesomeness of D and you’re setup for a power punch. Following this text is a very simple callback system in D.
A short but sweet 50 lines of code; it stores both functions and delegates and gives you a good launching point to create a more complicated call back system.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import tango.io.Stdout;

// Converts a function to a delegate. Stolen from http://dsource.org/projects/tango/ticket/1174
// Note that it doesn't handle ref or out though
R delegate(T) toDg(R, T...)(R function(T) fp) {
    struct dg {
        R opCall(T t) {
            return (cast(R function(T)) this) (t);
        }
    }
    R delegate(T) t;
    t.ptr = fp;
    t.funcptr = &dg.opCall;
    return t;
}

class SimpleCallback(R, P...)
{
    alias R delegate(P) callbacktype;
    alias R function(P) function_callbacktype;

    private callbacktype[] callback_list;

    typeof( this ) opCatAssign( in callbacktype callback )
    {
        callback_list ~= callback;
        return this;
    }

    typeof( this ) opCatAssign( in function_callbacktype callback )
    {
        auto dg = toDg!(R, P)( callback );
        return this ~= dg;
    }

    R emit( P p )
    {
        static if ( !is( R == void ) )
        R last;

        foreach( callback; callback_list )
        {
            static if ( !is( R == void ) )
            last = callback( p );
            else
                callback( p );
        }

        static if ( !is( R == void ) )
        return last;
    }

    alias emit opCall;
}

Here’s some example code:

SimpleCallback!( void ) sc = new SimpleCallback!( void );
SimpleCallback!( bool, char[] ) sc2 = new SimpleCallback!( bool, char[] );
sc ~= function void() { Stdout.formatln( "#1" ); };
sc ~= function void() { Stdout.formatln( "#2" ); };
sc2 ~= function bool( char[] str ) { Stdout.formatln( "#1 called with {}, returning false", str ); return false; };
sc2 ~= function bool( char[] str ) { Stdout.formatln( "#2 called with {}, returning true", str ); return true; };
sc();
Stdout.formatln( "Last sc2 callback returned {}", sc2( "coffee" ) );

And here’s the output:

#1
#2
#1 called with coffee, returning false
#2 called with coffee, returning true
Last sc2 callback returned true

Nayruden's /dev/random is Digg proof thanks to caching by WP Super Cache