www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Function to delegate conversion

reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Some of the functionality of C++-style pointers to member functions is
lost with D's delegates. (In exchange for many advantages, don't get me 
wrong!)

For instance, I am currently looking at how to wrap D classes and
present them to Python. Parts of this look suprisingly easy. (Conversion
between the Python type and the D class, for instance, is going to be
much easier than I had previously feared.) But exposing a class's member
functions is tricky without C++-style pointers to member functions.
(Boost.Python utilizes them for this purpose.)

For instance:

class A {
     void foo() { }
}

void main() {
     A a = new A;
     void function() fn = &A.foo;
}

Without the ability to convert fn into a delegate to a.foo, implementing 
this class-wrapping functionality is all but impossible.

The way I'd do it if I had the ability to make this conversion is 
something not totally unlike this:

// T is the wrapped class
// Fn is the member function we're wrapping
template method_wrap(T, alias Fn) {
     extern(C)
     PyObject* func(PyObject* self, PyObject* args) {
         // Some concern needs to be had with respect to the GC, but
         // nevermind that.
         T t = *cast(T*)PyCObject_AsVoidPtr(self);
         // Something like this, only with a billion static ifs and more
         // template hooha.
         auto dg = delegate(t, &Fn); // Convert to a delegate
         dg();
         return something;
     }
}

-Kirk McDonald
Jun 24 2006
next sibling parent Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Kirk McDonald wrote:
 Without the ability to convert fn into a delegate to a.foo, implementing 
 this class-wrapping functionality is all but impossible.

Okay, that IS a bit of hyperbole. I can easily do it with wrappers: class A { void foo() { } } void A_foo(A a) { a.foo(); } // Then wrap A_foo and pass the object as the first arg... But this imposes a bit of a burden on the user. -Kirk McDonald
Jun 25 2006
prev sibling parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Kirk McDonald wrote:
 For instance:
 
 class A {
     void foo() { }
 }
 
 void main() {
     A a = new A;
     void function() fn = &A.foo;
 }
 
 Without the ability to convert fn into a delegate to a.foo, implementing 
 this class-wrapping functionality is all but impossible.
 

The following dirty, dirty hack /appears/ to work, at least in this trivial case: import std.stdio; struct DG { Object instance; void function() fn; } union U { DG fake_dg; void delegate() real_dg; } class A { void foo() { writefln("A.foo()"); } } void main() { A a = new A; void function() fn = &A.foo; U u; u.fake_dg.instance = a; u.fake_dg.fn = fn; // Call it u.real_dg(); } This can be templatized into a more general solution. However, I am worried about how this scales (for functions with more arguments, etc), and of course about the fact that it is not cross-platform /at all/. I now this subject has come up before, but I feel it is important enough to bring up again. -Kirk McDonald
Jun 26 2006
parent reply BCS <BCS pathlink.com> writes:
Kirk McDonald wrote:
 
 The following dirty, dirty hack /appears/ to work, at least in this 
 trivial case:
 
 import std.stdio;
 
 struct DG {
     Object instance;
     void function() fn;
 }
 
 union U {
     DG fake_dg;

- void delegate() real_dg; + void* real_dg;
 }
 
 class A {
     void foo() { writefln("A.foo()"); }
 }
 
 void main() {
     A a = new A;
     void function() fn = &A.foo;
 
     U u;
     u.fake_dg.instance = a;

- u.fake_dg.fn = fn; + u.fake_dg.fn = cast(void*)fn;
 
     // Call it
     u.real_dg();
 }
 

That should also work and won't care about types. But it is still really, really dirty. But it's more portable that the last solution I saw, That one used ASM hacking.
Jun 26 2006
parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
BCS wrote:
 Kirk McDonald wrote:
 
 The following dirty, dirty hack /appears/ to work, at least in this 
 trivial case:

 import std.stdio;

 struct DG {
     Object instance;


- void function() fn; + void* fn;
 }

 union U {
     DG fake_dg;
     void delegate() real_dg;
 }

 class A {
     void foo() { writefln("A.foo()"); }
 }

 void main() {
     A a = new A;
     void function() fn = &A.foo;

     U u;
     u.fake_dg.instance = a;

- u.fake_dg.fn = fn; + u.fake_dg.fn = cast(void*)fn;
     // Call it
     u.real_dg();
 }

That should also work and won't care about types. But it is still really, really dirty. But it's more portable that the last solution I saw, That one used ASM hacking.

But yes, that is a good idea. Templatizing real_dg, however, is going to be a royal pain in the ass, but possible. With Daniel Keep's ftype module, we can get something like: struct DG { Object instance; void* fn; } template dg_unionT(Fn) { const uint ARGS = NumberOfArgs!(Fn); alias ReturnType!(Fn) RetType; union U { DG fake_dg; static if (ARGS == 0) { RetType delegate() real_dg; } else static if (ARGS == 1) { RetType delegate(ArgType!(Fn, 1)) real_dg; } else statif if (ARGS == 2) { RetType delegate(ArgType!(Fn, 1), ArgType!(Fn, 2)) real_dg; } // and so on... } } template U(Fn) { alias dg_unionT!(Fn).U U; } void main() { A a = new A; auto fn = &A.foo; U!(typeof(fn)) u; u.fake_dg.instance = a; u.fake_dg.fn = cast(void*)fn; u.real_dg(); } Also not pretty, but it should work, insofar as this whole trick works. -Kirk McDonald
Jun 26 2006
parent reply BCS <BCS pathlink.com> writes:
Kirk McDonald wrote:
 BCS wrote:
 
 Kirk McDonald wrote:

 The following dirty, dirty hack /appears/ to work, at least in this 
 trivial case:

 import std.stdio;

 struct DG {
     Object instance;


I think you mean: - void function() fn; + void* fn;
 }



Actually I did mean what I said, but what I meant (and said) was wrong. [...]
 
 
 But yes, that is a good idea. Templatizing real_dg, however, is going to 
 be a royal pain in the ass, but possible. With Daniel Keep's ftype 
 module, we can get something like:
 

 Also not pretty, but it should work, insofar as this whole trick works.
 
 -Kirk McDonald

how about <code> int function(int, float, char[], Object) fnp = getFNP(); int delegate(int, float, char[], Object) dg; DG* dgp = cast(DG*)cast(void*)&dg; dgp.fn = cast(void*)fnp; </code> It's about as type safe as ASM (a.k.a. not at all) but it should work cleanly (sorta).
Jun 26 2006
parent Kirk McDonald <kirklin.mcdonald gmail.com> writes:
BCS wrote:
 how about
 
 <code>
 int function(int, float, char[], Object) fnp = getFNP();
 
 int delegate(int, float, char[], Object) dg;
 DG* dgp = cast(DG*)cast(void*)&dg;
 
 dgp.fn = cast(void*)fnp;
 </code>
 
 It's about as type safe as ASM (a.k.a. not at all) but it should work 
 cleanly (sorta).

Ah! Now that is appealing as it cuts the union out of the thing entirely. I still need a template that can convert a function type into an equivalent delegate type, but that previous post of mine already outlines how that can be done. (The IsExpression can perform the reverse operation of converting a delegate type into a function type, though I don't believe I need that.) -Kirk McDonald
Jun 27 2006