Callback system in D
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!( 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:
#2
#1 called with coffee, returning false
#2 called with coffee, returning true
Last sc2 callback returned true
Good luck! If you ever need any Lua scripters I’m always here.
Thanks Conna!