Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Ticket #1174 (reopened enhancement)

Opened 16 years ago

Last modified 13 years ago

function to convert functions to delegates

Reported by: mwarning Assigned to: DRK
Priority: normal Milestone: 2.0
Component: Core Functionality Version: 0.99.6 Jeff
Keywords: Cc: DRK, fawzi

Description

Tango lacks a helper function to convert an arbitrary function into a delegate.

Don't let the users reinvent the wheel.

R delegate(T) toDg(R, T...)(R function(T) fp)
{
	alias typeof(fp) FP;
	struct Wrapper
	{
		FP fp;
		R call(T t)
		{
			static if (is(R == void)) this.fp(t);
			else return this.fp(t);
		}
	};
	auto x = new Wrapper;
	x.fp = fp;
	return &x.call;
}

Change History

07/04/08 14:32:23 changed by mandel

tango.util.Convert might be a place for this.

07/04/08 15:00:07 changed by korDen

This one is shorter. But is it safe?

R delegate(T) toDg(R, T...)(R function(T) fp) {

R delegate(T) t; t.funcptr = fp; return t;

}

Test: void test(int i, char[] text) {

Stdout(i, text).newline;

}

auto t = toDg(&test); t(42, "hello");

07/04/08 15:23:02 changed by mandel

I tried this simple approach some time ago and switched to the more complex version because I got segmentation faults under certain conditions.

But, in retrospective, I can't remember why your simple version shouldn't work.

07/04/08 19:02:24 changed by larsivi

  • milestone changed from 0.99.7 to 0.99.8.

But this is implementation specific and not specified such that it may change later?

07/06/08 21:29:55 changed by mandel

What do you mean?

07/06/08 21:39:58 changed by larsivi

I was of the impression that such a conversion wasn't safe, because the specification doesn't specify the layouts. But I may be wrong.

10/20/08 12:32:22 changed by fvbommel

Korden's suggestion is indeed unsafe. Indeed, on my (x86-64) gdc it segfaults. But the one in the initial ticket allocates, which is also an undesirable property. Try this one. It should work with any sane implementation of delegates*:

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;
}

As you can see, it uses an internal struct that's never actually instantiated. It's just used to get the calling convention for the delegate right: the function pointer is passed in as 'this' (being cast to a pointer to the struct type). It's immediately cast back and called. On my gdc, the initial toDg compiles to almost nothing (2 movs and a ret, inlinable) while the generated delegate function just shuffles some parameters around before performing a tail call (so it's as fast as the compiler can make it, and doesn't even grow the stack...). (Note: above is with -O -release, without -release it also asserts 'this' (aka the function pointer) is not null -- which isn't a bad check to make, I guess) (Note 2: I can't take credit for this idea, I think I first saw it on the newsgroups somewhere)

* For these purposes, sane is defined as "dg.ptr is passed as 'this' to dg.funcptr"

PS. if above code is too hackish and you guys decide to go with something along the lines of the initial version above, please note: you can 'return foo();' from a void function just fine so the static if is unnecessary.

Oh, and I'm pretty sure none of these versions properly handle 'out', 'inout'/'ref' and lazy parameters. I'm not sure about variadics, but I wouldn't count on it...

10/21/08 03:30:59 changed by mandel

this toDG implementation works fine. I've tested it with dmd (v1.034, with/without -O -release), gdc trunk (with/without ridiculous optimisation flags) and ldc trunk r711) on i686-pc-linux-gnu 32bit (Debian).

I also tested with my own applications. As far as I can tell. Everything works fine. :)

10/21/08 07:25:51 changed by larsivi

So then the only question left is where should this reside?

Considering it is to and from a D type, I think tango.util.Convert probably is good with this.

10/23/08 10:53:00 changed by mandel

tango.util.Convert might not be the right place. Imo, functions/delegates are no ordinary values.

But otherwise, I don't have a better idea. Fwiw, I have toDg in util/Utils.d in my own code. :>

10/23/08 11:27:56 changed by larsivi

Not ordinary values no, but language constructs - as opposed to user/library defined constructs.

10/26/08 14:47:07 changed by mandel

Another issue is that this function might get unnoticed in Convert.d.

10/26/08 17:20:39 changed by mandel

There is a Bind from team0xf http://www.h3.team0xf.com/Bind.d that works for Tango and is nearly identical to std.bind (same origin). It's probalbly more up-to-date.

According to KeYeR-L/h3r3tic, Bind can wrap functions to delegates but is not optimal (heap allocation). They suggested http://team0xf.com:8080/utils/file/cf920ea6fe90/Meta.d#l558. It is a bit different to the code snippet fvbommel suggested in a comment above.

h3r3tic pointed out that downs made an improved version that also handles ref-ness.

I would propose to include Bind in Tango and to put toDg or functionToDelegate along into the Bind.d file. - When the authors agree.

Then #396 could also be closed.

11/01/08 16:40:46 changed by Cyborg16

One thing all but korDen's (unsafe) suggestion miss is what happens if a null function pointer is passed? A null delegate might be expected but this is not returned, so the expected "if (dg !is null)" test doesn't work.

02/25/09 17:40:58 changed by larsivi

Ok, now that bind is gone (won't be included as is/was), what are the thoughts on this ticket?

02/26/09 19:59:19 changed by mandel

A version fixed version for refs is the way to go. Imo downs code need to be stripped down a bit for inclusion.

I will look into it at the weekend.

03/02/09 18:05:08 changed by mandel

Making the code work for refness includes quite some template-fu so far.
At least the compilers don't let it compile for ref parameters.
Maybe it's worthwhile to include it without ref support and make an enhancement ticket instead?

It looks very simple without the need for ref support.

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;
}

03/29/09 15:00:47 changed by larsivi

  • cc set to DRK.
  • milestone changed from 0.99.8 to 0.99.9.

03/29/09 22:36:29 changed by DRK

To correctly duplicate the function signature, you'll need to use ParameterTypeTuple?!(fp).stringof. This will tell you what the types of the arguments are including their storage classes.

You'll need to parse that string and turn it into an argument list. You can then build the opCall using a string mixin.

Generating the return type of the function itself should look something like this:

mixin("ReturnType!(fp) delegate"~ParameterTypeTuple!(fp).stringof)

04/26/09 16:55:33 changed by larsivi

  • cc changed from DRK to DRK, fawzi.
  • owner changed from larsivi to DRK.

Ok, util.Convert should probably support it, but all of that should probably not be necessary to use this functionality.

So an additional question is if it should be in its own module somewhere, maybe together with the ctfe functions in core.Traits? Since they IMO don't belong there.

08/17/09 07:17:18 changed by DRK

I've got working code for a toDg function. I don't want to put the code into util.Convert directly because it's about 150 lines of actual code, and I want to keep everything other than trivial conversion code out of Convert.

I was thinking of creating util.Func where this and any other function-related code can go if we add any in the future. Any other thoughts or suggestions?

08/17/09 07:17:48 changed by DRK

  • status changed from new to assigned.

08/17/09 20:43:31 changed by fawzi

util.Func is a good possibility, otherwise core.Traits as you proposed seems like a good candidate

11/23/09 10:43:42 changed by DRK

  • status changed from assigned to closed.
  • resolution set to fixed.

(In [5219]) Added tango.util.Func with toDg. Closes #1174.

11/23/09 10:45:51 changed by DRK

It's gone into util.Func. I've elected not to add this to Convert for the moment. The problem is that doing the conversion via to!(T) would be significantly more verbose than just using toDg which can automatically determine the result type.

I'll add it to Convert if there's demand for it, but you'd have to be a masochist. :)

11/23/09 11:15:19 changed by DRK

  • status changed from closed to reopened.
  • resolution deleted.

Reverted fix for more discussion.

11/24/09 22:47:09 changed by kris

  • milestone changed from 0.99.9 to 2.0.

05/20/11 19:48:42 changed by larsivi

Why was further discussion for this needed? It seems to me that tango.util.Func was a good solution.

05/20/11 19:49:08 changed by larsivi

See also #2042