www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Functors

reply "Craig Black" <craigblack2 cox.net> writes:
I just realized that a Functor class is much much easier to express in D 
than in C++.  Functors are more flexible than delegates because they can be 
associated with static/global functions as well as functions local to a 
class or struct.  In essense they can be either a function or a delegate. 
The implementation is also pretty efficient.  The implementation below does 
not support functions/delegates with return values, but that would be easy 
to accommodate.  Anyway, thought it might be useful to someone.

struct Functor(A...)
{
private:

  union
  {
    void function(A) fun;
    void delegate(A) del;
    int[2] words;
  }

public:

  void clear()
  {
    words[0] = 0;
    words[1] = 0;
  }

  void opAssign(void function(A) arg)
  {
    fun = arg;
    words[1] = 0;
  }

  void opAssign(void delegate(A) arg)
  {
    del = arg;
  }

  void invoke(A args)
  {
    if(words[0] == 0) return;
    if(words[1] == 0) fun(args);
    else del(args);
  }
}

int main(char[][] args)
{
  // A functor that takes no parameters
  Functor!() functor;

  static void fun() { printf("In static function.\n"); }
  functor = &fun;
  functor.invoke;

  struct A { public void fun() { printf("In local function.\n"); } }
  A a;
  functor = &a.fun;
  functor.invoke;

  return 0;
}
Jun 28 2007
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Craig,

 Functors are more flexible than delegates because they
 can be associated with static/global functions as well as functions
 local to a class or struct.
if all you want to do is convert fn ptrs to delegates this works: |T delegate(A) Fn2Dg(T, A...)(T function(A) f) |{ | struct tmp | { | T ret(A args){ return (cast(T function(A))this)(args); } | }; | return &(cast(tmp*)f).ret; |}
Jun 28 2007
parent reply "Craig Black" <craigblack2 cox.net> writes:
 if all you want to do is convert fn ptrs to delegates this works:

 |T delegate(A) Fn2Dg(T, A...)(T function(A) f)
 |{
 | struct tmp
 | {
 | T ret(A args){ return (cast(T function(A))this)(args); }
 | };
 | return &(cast(tmp*)f).ret;
 |}
I didn't know this was possible. Are you sure this works? I thought there were some issues with calling conventions. -Craig
Jun 28 2007
parent reply BCS <ao pathlink.com> writes:
Reply to Craig,

 if all you want to do is convert fn ptrs to delegates this works:
 
 |T delegate(A) Fn2Dg(T, A...)(T function(A) f)
 |{
 | struct tmp
 | {
 | T ret(A args){ return (cast(T function(A))this)(args); }
 | };
 | return &(cast(tmp*)f).ret;
 |}
I didn't know this was possible. Are you sure this works? I thought there were some issues with calling conventions. -Craig
What goes on there is that the context pointer (which is supposed to be a object, struct or stack frame) is in fact a function pointer. The calling convention for delegates never actually de references the context pointer so what is is doesn't matter. In this case I convert it back to a function pointer, and then call it with the arguments I was given. All of the values in the arguments get copied and arranged correctly for the function call and everything is fine and dandy.
Jun 28 2007
parent reply "Craig Black" <craigblack2 cox.net> writes:
Very useful hack.  Thanks!

"BCS" <ao pathlink.com> wrote in message 
news:ce0a3343b7368c987a5d27c1f2e news.digitalmars.com...
 Reply to Craig,

 if all you want to do is convert fn ptrs to delegates this works:

 |T delegate(A) Fn2Dg(T, A...)(T function(A) f)
 |{
 | struct tmp
 | {
 | T ret(A args){ return (cast(T function(A))this)(args); }
 | };
 | return &(cast(tmp*)f).ret;
 |}
I didn't know this was possible. Are you sure this works? I thought there were some issues with calling conventions. -Craig
What goes on there is that the context pointer (which is supposed to be a object, struct or stack frame) is in fact a function pointer. The calling convention for delegates never actually de references the context pointer so what is is doesn't matter. In this case I convert it back to a function pointer, and then call it with the arguments I was given. All of the values in the arguments get copied and arranged correctly for the function call and everything is fine and dandy.
Jun 28 2007
parent reply BCS <ao pathlink.com> writes:
Reply to Craig,

 Very useful hack.  Thanks!
 
 "BCS" <ao pathlink.com> wrote in message
 news:ce0a3343b7368c987a5d27c1f2e news.digitalmars.com...
 
 Reply to Craig,
 
 if all you want to do is convert fn ptrs to delegates this works:
 
 |T delegate(A) Fn2Dg(T, A...)(T function(A) f)
 |{
 | struct tmp
 | {
 | T ret(A args){ return (cast(T function(A))this)(args); }
 | };
 | return &(cast(tmp*)f).ret;
 |}
I didn't know this was possible. Are you sure this works? I thought there were some issues with calling conventions. -Craig
What goes on there is that the context pointer (which is supposed to be a object, struct or stack frame) is in fact a function pointer. The calling convention for delegates never actually de references the context pointer so what is is doesn't matter. In this case I convert it back to a function pointer, and then call it with the arguments I was given. All of the values in the arguments get copied and arranged correctly for the function call and everything is fine and dandy.
It gets better <g type=evil> that 32 bits of the pointer can be used for anything as long as it isn't set to null struct S { union U { S* ptr; struct { short s; byte b; char c; } } void delegate() build(short s, byte b, char c) { U u; u.s=s; u.b=b; u.c=c; return &s.ptr.go; } void go() { U u; U.ptr = this; writef("%s, %s, %s\n", u.s, u.b, u.c); } }
Jun 28 2007
parent "Craig Black" <craigblack2 cox.net> writes:
I'll keep that in mind evil genius.

"BCS" <ao pathlink.com> wrote in message 
news:ce0a3343b7398c987a7d80a1836 news.digitalmars.com...
 Reply to Craig,

 Very useful hack.  Thanks!

 "BCS" <ao pathlink.com> wrote in message
 news:ce0a3343b7368c987a5d27c1f2e news.digitalmars.com...

 Reply to Craig,

 if all you want to do is convert fn ptrs to delegates this works:

 |T delegate(A) Fn2Dg(T, A...)(T function(A) f)
 |{
 | struct tmp
 | {
 | T ret(A args){ return (cast(T function(A))this)(args); }
 | };
 | return &(cast(tmp*)f).ret;
 |}
I didn't know this was possible. Are you sure this works? I thought there were some issues with calling conventions. -Craig
What goes on there is that the context pointer (which is supposed to be a object, struct or stack frame) is in fact a function pointer. The calling convention for delegates never actually de references the context pointer so what is is doesn't matter. In this case I convert it back to a function pointer, and then call it with the arguments I was given. All of the values in the arguments get copied and arranged correctly for the function call and everything is fine and dandy.
It gets better <g type=evil> that 32 bits of the pointer can be used for anything as long as it isn't set to null struct S { union U { S* ptr; struct { short s; byte b; char c; } } void delegate() build(short s, byte b, char c) { U u; u.s=s; u.b=b; u.c=c; return &s.ptr.go; } void go() { U u; U.ptr = this; writef("%s, %s, %s\n", u.s, u.b, u.c); } }
Jun 28 2007
prev sibling next sibling parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Craig Black wrote:
 I just realized that a Functor class is much much easier to express in D 
 than in C++.  Functors are more flexible than delegates because they can 
 be associated with static/global functions as well as functions local to 
 a class or struct.  In essense they can be either a function or a 
 delegate. The implementation is also pretty efficient.  The 
 implementation below does not support functions/delegates with return 
 values, but that would be easy to accommodate.  Anyway, thought it might 
 be useful to someone.
 
 struct Functor(A...)
 {
 private:
 
  union
  {
    void function(A) fun;
    void delegate(A) del;
    int[2] words;
  }
 
 public:
 
  void clear()
  {
    words[0] = 0;
    words[1] = 0;
  }
 
  void opAssign(void function(A) arg)
  {
    fun = arg;
    words[1] = 0;
  }
 
  void opAssign(void delegate(A) arg)
  {
    del = arg;
  }
 
  void invoke(A args)
  {
    if(words[0] == 0) return;
    if(words[1] == 0) fun(args);
    else del(args);
  }
 }
 
 int main(char[][] args)
 {
  // A functor that takes no parameters
  Functor!() functor;
 
  static void fun() { printf("In static function.\n"); }
  functor = &fun;
  functor.invoke;
 
  struct A { public void fun() { printf("In local function.\n"); } }
  A a;
  functor = &a.fun;
  functor.invoke;
 
  return 0;
 }
 
Instead of calling it "invoke", why not overload opCall? -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Jun 28 2007
parent "Craig Black" <craigblack2 cox.net> writes:
 Instead of calling it "invoke", why not overload opCall?
Yes opCall would be better. Thanks.
Jun 28 2007
prev sibling parent "Nathan Allan" <nathan alphora.com> writes:
Isn't the "words" part of the union assuming 32bit pointers?  How about 
using an array of void pointers, or better yet assign both members to null 
before setting either of them.  This is more defensive as it allows future 
versions of D to change the internal representations of delegates and 
function pointers.

Best,

--
Nathan Allan

"Craig Black" <craigblack2 cox.net> wrote in message 
news:f60vdo$2fb8$1 digitalmars.com...
I just realized that a Functor class is much much easier to express in D 
than in C++.  Functors are more flexible than delegates because they can be 
associated with static/global functions as well as functions local to a 
class or struct.  In essense they can be either a function or a delegate. 
The implementation is also pretty efficient.  The implementation below does 
not support functions/delegates with return values, but that would be easy 
to accommodate.  Anyway, thought it might be useful to someone.

 struct Functor(A...)
 {
 private:

  union
  {
    void function(A) fun;
    void delegate(A) del;
    int[2] words;
  }

 public:

  void clear()
  {
    words[0] = 0;
    words[1] = 0;
  }

  void opAssign(void function(A) arg)
  {
    fun = arg;
    words[1] = 0;
  }

  void opAssign(void delegate(A) arg)
  {
    del = arg;
  }

  void invoke(A args)
  {
    if(words[0] == 0) return;
    if(words[1] == 0) fun(args);
    else del(args);
  }
 }

 int main(char[][] args)
 {
  // A functor that takes no parameters
  Functor!() functor;

  static void fun() { printf("In static function.\n"); }
  functor = &fun;
  functor.invoke;

  struct A { public void fun() { printf("In local function.\n"); } }
  A a;
  functor = &a.fun;
  functor.invoke;

  return 0;
 }
 
Jun 29 2007