www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Library writing - delegates or fp's?

reply Chad J <gamerChad _spamIsBad_gmail.com> writes:
So I have decided to roll my own GUI library, one dedicated to computer 
games.  But I've run into a bit of an unpleasant design problem - for 
handling callbacks, how should my library expose function pointers and 
delegates?

I suppose I could choose one or the other, but I think it desirable to 
allow use of both since I don't want to force the user of the library 
into a particular style or make them use ugly workarounds.  I am 
currently thinking of a few possible solutions, none of which look very 
pleasing to me:

#1:  Force them to use delegates and work around it to do module-level 
programming.  Example -

import std.stdio;

int delegate(int) increment;

void main()
{
     Dummy d = new Dummy();
     increment = &d.classIncr;
     writefln( increment(1) );
}

int incr( int a )
{
     return a + 1;
}

class Dummy
{
     this() {}

     int classIncr( int a )
     {
         return incr(a);
     }
}

#2:  Provide a templated struct kind of thing that can double as a 
function pointer or a delegate, but is kinda clumsy to deal with.  EX:

import std.stdio;

alias CallBack!(int function(int,Object)) IncCallBack;
IncCallBack increment;

void main()
{
     increment.functionPointer = &incr;
     writefln( increment.functionPointer(1,null) );
}

int incr( int a )
{
     return a + 1;
}

struct CallBack( fpType )
{
     Object object = null;
     fpType functionPointer;

     static CallBack!(fpType) opCall( fpType fp )
     {
         CallBack!(fpType) result;
         result.functionPointer = fp;
         return result;
     }
}

#3:  Expose both function pointers and delegates, with some kind of 
naming convention.  I suppose there could be a better naming convention 
here, but it seems hackish as well.  EX:

void delegate(int,int,ubyte) mouseButtonUp_dg;
void function(int,int,ubyte) mouseButtonUp_fp;


Maybe I'm missing something obvious.  I'd appreciate it if someone has a 
better solution than any of these.  Otherwise I'll settle for some wise 
advice from those who have already been here :)
Jun 20 2006
next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Wed, 21 Jun 2006 00:40:15 -0400, Chad J wrote:

 So I have decided to roll my own GUI library, one dedicated to computer 
 games.  But I've run into a bit of an unpleasant design problem - for 
 handling callbacks, how should my library expose function pointers and 
 delegates?
 
 I suppose I could choose one or the other, but I think it desirable to 
 allow use of both since I don't want to force the user of the library 
 into a particular style or make them use ugly workarounds.  I am 
 currently thinking of a few possible solutions, none of which look very 
 pleasing to me:
 
 #1:  Force them to use delegates and work around it to do module-level 
 programming.  Example -

It doesn't have to be as complex as that. All you need to do to turn module-level functions into delegates is to place them inside the module's constructor or any other function for that matter, including main(). For example .. import std.stdio; int delegate(int) increment; static this() { int incr( int a ) { return a + 1; } increment = &incr; } void main() { writefln( increment(1) ); } Or if you prefer the new function literal syntax (and not use the module constructor) ... import std.stdio; int delegate(int) increment; int delegate(int) decrement; void Init_Delegates() { increment = delegate int ( int a ) { return a + 1; }; decrement = delegate int ( int a ) { return a - 1; }; } void main() { Init_Delegates(); writefln( increment(1) ); writefln( decrement(1) ); } -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocrity!" 21/06/2006 3:18:11 PM
Jun 20 2006
parent reply Chad J <gamerChad _spamIsBad_gmail.com> writes:
Derek Parnell wrote:
 
 It doesn't have to be as complex as that. All you need to do to turn
 module-level functions into delegates is to place them inside the module's
 constructor or any other function for that matter, including main(). For
 example ..
 
  import std.stdio;
  int delegate(int) increment;
  static this()
  {
     int incr( int a )
     {
          return a + 1;
     }
     increment = &incr;
  }
 
  void main()
  {
     writefln( increment(1) );
  }
 

Pretty nice, but then I can't do something like this: import std.stdio; int delegate(int) increment; void main() { writefln( increment(1) ); writefln( incr(2) ); } static this() { int incr( int a ) { return a + 1; } increment = &incr; } I'm thinking incase someone wanted to say, have a mouse handling function be triggered by the GUI's delegate and also have it artificially triggered by some other code via a direct function call (maybe a tutorial, debug routine, or something like that). I suppose they should just call the delegate though. I like it. Thanks.
Jun 20 2006
parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Wed, 21 Jun 2006 02:12:54 -0400, Chad J wrote:

 Pretty nice, but then I can't do something like this:

      writefln( increment(1) );
      writefln( incr(2) );

...
 I'm thinking incase someone wanted to say, have a mouse handling 
 function be triggered by the GUI's delegate and also have it 
 artificially triggered by some other code via a direct function call 
 (maybe a tutorial, debug routine, or something like that).

Sure you can! How about this ... import std.stdio; int delegate(int) increment; void main() { writefln( increment(1) ); writefln( incr(2) ); } int incr( int a ) { return a + 1; } static this() { increment = delegate int ( int a ) {return incr(a); }; } Would that do?
 I suppose they should just call the delegate though.  I like it.  Thanks.

Yeah, I guess you could do that as well. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocrity!" 21/06/2006 4:20:09 PM
Jun 20 2006
next sibling parent Chad J <gamerChad _spamIsBad_gmail.com> writes:
Derek Parnell wrote:
 
I'm thinking incase someone wanted to say, have a mouse handling 
function be triggered by the GUI's delegate and also have it 
artificially triggered by some other code via a direct function call 
(maybe a tutorial, debug routine, or something like that).

Sure you can! How about this ... import std.stdio; int delegate(int) increment; void main() { writefln( increment(1) ); writefln( incr(2) ); } int incr( int a ) { return a + 1; } static this() { increment = delegate int ( int a ) {return incr(a); }; } Would that do?

Indeed. That kicks ass. I still have to completely wrap my head around that function literal syntax. All of the sudden it is very useful to me. :)
Jun 20 2006
prev sibling parent reply Carlos Santander <csantander619 gmail.com> writes:
Derek Parnell escribió:
 On Wed, 21 Jun 2006 02:12:54 -0400, Chad J wrote:
 
 Pretty nice, but then I can't do something like this:

      writefln( increment(1) );
      writefln( incr(2) );

....
 I'm thinking incase someone wanted to say, have a mouse handling 
 function be triggered by the GUI's delegate and also have it 
 artificially triggered by some other code via a direct function call 
 (maybe a tutorial, debug routine, or something like that).

Sure you can! How about this ... import std.stdio; int delegate(int) increment; void main() { writefln( increment(1) ); writefln( incr(2) ); } int incr( int a ) { return a + 1; } static this() { increment = delegate int ( int a ) {return incr(a); }; } Would that do?

I thought the frame pointer would get lost and cause a segfault later on (see DFL events handling for an example.) Did that change with 0.161?
 I suppose they should just call the delegate though.  I like it.  Thanks.

Yeah, I guess you could do that as well.

-- Carlos Santander Bernal
Jun 21 2006
parent reply Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Carlos Santander wrote:
 Derek Parnell escribió:
 Sure you can! How about this ...


  import std.stdio;

  int delegate(int) increment;

  void main()
  {
     writefln( increment(1) );
     writefln( incr(2) );
  }

  int incr( int a )
  {
      return a + 1;
  }

  static this()
  {
      increment = delegate int ( int a ) {return incr(a); };
  }

 Would that do?
  

I thought the frame pointer would get lost and cause a segfault later on (see DFL events handling for an example.) Did that change with 0.161?

The frame pointer becomes invalid, but that is no problem since the delegate is function-like and does not access the frame context (i.e., it's not a closure). -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jul 18 2006
parent Carlos Santander <csantander619 gmail.com> writes:
Bruno Medeiros escribió:
 Carlos Santander wrote:
 Derek Parnell escribió:
 Sure you can! How about this ...


  import std.stdio;

  int delegate(int) increment;

  void main()
  {
     writefln( increment(1) );
     writefln( incr(2) );
  }

  int incr( int a )
  {
      return a + 1;
  }

  static this()
  {
      increment = delegate int ( int a ) {return incr(a); };
  }

 Would that do?
  

I thought the frame pointer would get lost and cause a segfault later on (see DFL events handling for an example.) Did that change with 0.161?

The frame pointer becomes invalid, but that is no problem since the delegate is function-like and does not access the frame context (i.e., it's not a closure).

In this case, yes, you're right. I must admit I didn't notice that. However, in other circumstances (more complex, I'd say) it's a valid point to take into account. -- Carlos Santander Bernal
Jul 21 2006
prev sibling next sibling parent Lionello Lunesu <lio lunesu.remove.com> writes:
Chad J wrote:
 So I have decided to roll my own GUI library, one dedicated to computer 
 games.  But I've run into a bit of an unpleasant design problem - for 
 handling callbacks, how should my library expose function pointers and 
 delegates?
 
 I suppose I could choose one or the other, but I think it desirable to 
 allow use of both since I don't want to force the user of the library 
 into a particular style or make them use ugly workarounds.  I am 
 currently thinking of a few possible solutions, none of which look very 
 pleasing to me:

I'd pick delegates.
 #1:  Force them to use delegates and work around it to do module-level 
 programming.  Example -

At the moment, a function can't be cast to a delegate, but I think this will change in the future (since it's theoretically possible). In the mean time you can use the new lambda/literal syntax: int module_level_func() { return 1; } void somefunc( int delegate() callback ) { callback(); } void main() { // somefunc( &module_level_func );// not (yet) possible somefunc( { return module_level_func();} ); //wrap it } L.
Jun 21 2006
prev sibling parent reply BCS <BCS pathlink.com> writes:
Chad J wrote:
 So I have decided to roll my own GUI library, one dedicated to computer 
 games.  But I've run into a bit of an unpleasant design problem - for 
 handling callbacks, how should my library expose function pointers and 
 delegates?
 

a while back someone found out that you can take a delegate and replace the internal function pointer with a regular function pointer and it still works just fine. Given this, you could wright a little function*-to-delegate function and let people do this UseThisCallback(int delegate(int)); int delegate(int) f2d(int function(int) fnp){...} int fn(int); void main() { UseThisCallback(f2d(&fn)); } I known this is an *ugly* hack, OTOH maybe Walter can be convinced to add a function-pointer to delegate cast that does this in a consistent manner. cast(int delegate(int))&fn
Jun 21 2006
next sibling parent reply "Andrei Khropov" <andkhropov nospam_mtu-net.ru> writes:
BCS wrote:

 Chad J wrote:
 So I have decided to roll my own GUI library, one dedicated to computer
 games.  But I've run into a bit of an unpleasant design problem - for
 handling callbacks, how should my library expose function pointers and
 delegates?
 

a while back someone found out that you can take a delegate and replace the internal function pointer with a regular function pointer and it still works just fine. Given this, you could wright a little function*-to-delegate function and let people do this UseThisCallback(int delegate(int)); int delegate(int) f2d(int function(int) fnp){...} int fn(int); void main() { UseThisCallback(f2d(&fn)); } I known this is an ugly hack, OTOH maybe Walter can be convinced to add a function-pointer to delegate cast that does this in a consistent manner. cast(int delegate(int))&fn

maybe just "cast(delegate)&fn" in the spirit of inferred typing? --
Jul 18 2006
parent BCS <BCS pathlink.com> writes:
Andrei Khropov wrote:
 BCS wrote:
a while back someone found out that you can take a delegate and replace the
internal function pointer with a regular function pointer and it still works
just fine. Given this, you could wright a little function*-to-delegate
function and let people do this


I known this is an ugly hack, OTOH maybe Walter can be convinced to add a
function-pointer to delegate cast that does this in a consistent manner.

cast(int delegate(int))&fn

maybe just "cast(delegate)&fn" in the spirit of inferred typing?

That /would/ be better.
Jul 18 2006
prev sibling parent reply Don Clugston <dac nospam.com.au> writes:
BCS wrote:
 Chad J wrote:
 So I have decided to roll my own GUI library, one dedicated to 
 computer games.  But I've run into a bit of an unpleasant design 
 problem - for handling callbacks, how should my library expose 
 function pointers and delegates?

a while back someone found out that you can take a delegate and replace the internal function pointer with a regular function pointer and it still works just fine.

It works fine *most of the time*. IIRC, sometimes it will result in incorrect code. We could deal with those cases if we knew the exact circumstances under which function parameters are passed in registers. But until the ABI gets formalised, it's a non-portable hack that occasionally fails.
Jul 19 2006
parent BCS <BCS pathlink.com> writes:
Don Clugston wrote:
 BCS wrote:
 
 Chad J wrote:

 So I have decided to roll my own GUI library, one dedicated to 
 computer games.  But I've run into a bit of an unpleasant design 
 problem - for handling callbacks, how should my library expose 
 function pointers and delegates?

a while back someone found out that you can take a delegate and replace the internal function pointer with a regular function pointer and it still works just fine.

It works fine *most of the time*. IIRC, sometimes it will result in incorrect code. We could deal with those cases if we knew the exact circumstances under which function parameters are passed in registers. But until the ABI gets formalised, it's a non-portable hack that occasionally fails.

I wouldn't use it (if possible) even if it was portable. The compiler should do this for you. cast(delegate)fnpt;
Jul 25 2006