Thread overview
Initializing a table of delegates with no-op stubs
Jan 09, 2020
H. S. Teoh
Jan 10, 2020
Arine
Jan 11, 2020
Rainer Schuetze
Jan 11, 2020
Arine
Jan 13, 2020
H. S. Teoh
Jan 13, 2020
Rainer Schuetze
Jan 14, 2020
Timon Gehr
Jan 20, 2020
H. S. Teoh
January 09, 2020
I have a struct containing a bunch of delegates:

	struct S {
		void delegate(int blah) dg1;
		void delegate(string blah) dg2;
		void delegate(int x, int y) dg3;
		... // lots of these
	}

How do I initialize them to no-op stubs?  I've tried all sorts of ways but couldn't get it to work.  It's easy to do with a *function pointer* -- just declare a dummy no-op function with the right signature and take its address:

	void dummy(int) {}
	struct S {
		void function(int) f1 = &dummy; // OK

		// But it doesn't work with delegates, no way, no how:
		void delegate(int) dg1 = &dummy; // NG
		void delegate(int) dg2 = (int) {}; // NG
		void delegate(int) dg3 = (int) => dummy(0); // NG
	}

I even tried to make a function that returns an empty delegate, but when I try to assign that to the struct member the compiler complains that it's a non-constant expression.

Seriously, why is it so hard to initialize a delegate field to point to a 'ret' instruction?!  That's all I need.  It doesn't even need a context pointer, since it's supposed to do nothing.  Since it's possible to initialize struct fields to function pointers, surely it can't be *that* hard to also initialize them to point to empty delegates?

The only success I have is to defer to runtime, and even then it involves a whole mouthful of periphrasis:

	S s;
	foreach (ref dg; s.tupleof)
	{
		import std.traits : Parameters;
		void impl(size_t i)(Parameters!(typeof(dg))) {}
		dg = &impl;
	}

Am I missing something obvious??


T

-- 
Never ascribe to malice that which is adequately explained by incompetence. -- Napoleon Bonaparte
January 09, 2020
On 1/9/20 5:02 PM, H. S. Teoh wrote:
> I have a struct containing a bunch of delegates:
> 
> 	struct S {
> 		void delegate(int blah) dg1;
> 		void delegate(string blah) dg2;
> 		void delegate(int x, int y) dg3;
> 		... // lots of these
> 	}
> 
> How do I initialize them to no-op stubs?  I've tried all sorts of ways
> but couldn't get it to work.  It's easy to do with a *function pointer*
> -- just declare a dummy no-op function with the right signature and take
> its address:
> 
> 	void dummy(int) {}
> 	struct S {
> 		void function(int) f1 = &dummy; // OK
> 
> 		// But it doesn't work with delegates, no way, no how:
> 		void delegate(int) dg1 = &dummy; // NG
> 		void delegate(int) dg2 = (int) {}; // NG
> 		void delegate(int) dg3 = (int) => dummy(0); // NG
> 	}

I thought this might work, but toDelegate seems to not be usable at compile time:

void delegate(int) dg1 = toDelegate((int) {});

> 
> I even tried to make a function that returns an empty delegate, but when
> I try to assign that to the struct member the compiler complains that
> it's a non-constant expression.
> 
> Seriously, why is it so hard to initialize a delegate field to point to
> a 'ret' instruction?!  That's all I need.  It doesn't even need a
> context pointer, since it's supposed to do nothing.  Since it's possible
> to initialize struct fields to function pointers, surely it can't be
> *that* hard to also initialize them to point to empty delegates?
> 
> The only success I have is to defer to runtime, and even then it
> involves a whole mouthful of periphrasis:
> 
> 	S s;
> 	foreach (ref dg; s.tupleof)
> 	{
> 		import std.traits : Parameters;
> 		void impl(size_t i)(Parameters!(typeof(dg))) {}
> 		dg = &impl;
> 	}
> 
> Am I missing something obvious??

toDelegate should work at runtime, but you'd have to know the types anyway, so your runtime thing is probably just as verbose using that. I'd recommend still using static void impl, and using toDelegate to avoid any closures.

It would be useful if toDelegate either worked at compile time, or the compiler allowed auto conversion to delegates (as it's always possible, a la toDelegate).

-Steve
January 10, 2020
This works too:

    struct S {
        void function(int) f1 = (int) { }; // OK
    }

Which makes me think, it isn't allowing it because of the context pointer. But if that context pointer is null, it should work. File a bug report and hope it gets fixed soon™?





January 11, 2020

On 09/01/2020 23:02, H. S. Teoh wrote:
> void dummy(int) {}
> 	struct S {
> 		void function(int) f1 = &dummy; // OK
> 
> 		// But it doesn't work with delegates, no way, no how:
> 		void delegate(int) dg1 = &dummy; // NG
> 		void delegate(int) dg2 = (int) {}; // NG
> 		void delegate(int) dg3 = (int) => dummy(0); // NG
> 	}
> 
[...]
> Am I missing something obvious??
> 

This seems to work:

void noop(int) {}

enum void delegate(int) dg_noop = (int x){ noop(x); };

struct S
{
    void delegate(int) dg = dg_noop;
}

January 11, 2020
On 1/11/20 4:40 AM, Rainer Schuetze wrote:
> 
> 
> On 09/01/2020 23:02, H. S. Teoh wrote:
>> void dummy(int) {}
>> 	struct S {
>> 		void function(int) f1 = &dummy; // OK
>>
>> 		// But it doesn't work with delegates, no way, no how:
>> 		void delegate(int) dg1 = &dummy; // NG
>> 		void delegate(int) dg2 = (int) {}; // NG
>> 		void delegate(int) dg3 = (int) => dummy(0); // NG
>> 	}
>>
> [...]
>> Am I missing something obvious??
>>
> 
> This seems to work:
> 
> void noop(int) {}
> 
> enum void delegate(int) dg_noop = (int x){ noop(x); };

No need for the function call

enum void delegate(int) dg_noop = (int x) {};

> 
> struct S
> {
>      void delegate(int) dg = dg_noop;
> }
> 

OK, so I think what is happening here is that a delegate defined inside a struct is going to use a struct `this` reference as the context pointer, and I get a weird message (why didn't you show this in the first place?): Error: delegate onlineapp.S.__lambda2 cannot be struct members

It could be a possibility that if you use a delegate as an initializer outside a member function context, then it becomes like a global delegate?

In any case, here is a solution that works and is pretty reasonable:

enum void delegate(T) dummydg(T...) = (T params) {};

struct S
{
    void delegate(int) dg = dummydg!int;
    void delegate(string, int) dg2 = dummydg!(string, int);
}

void main()
{
    S s;
    s.dg(1);
    s.dg2("hi", 1);
}

-Steve
January 11, 2020
On Saturday, 11 January 2020 at 14:51:24 UTC, Steven Schveighoffer wrote:
> In any case, here is a solution that works and is pretty reasonable:

I think there's probably a check that just stops delegates completely from being default initialized in structs.

 	struct S {
 		void delegate(int) dg2 = cast(void delegate(int)) function (int x) {};
 	}

That has the same error message.

Just cause there's a (hacky) workaround, doesn't mean the most intuitive way to do it shouldn't be fixed.
January 13, 2020
On Sat, Jan 11, 2020 at 09:51:24AM -0500, Steven Schveighoffer via Digitalmars-d wrote:
> On 1/11/20 4:40 AM, Rainer Schuetze wrote:
[...]
> > void noop(int) {}
> > 
> > enum void delegate(int) dg_noop = (int x){ noop(x); };
> 
> No need for the function call
> 
> enum void delegate(int) dg_noop = (int x) {};
[...]

Mmm, very nice!  Kind of a weird paraphrasis, but at least it works. To minimize hassle, I turned it into a template:

	enum void delegate(Args) doNothing(Args...) = (Args args) {};

	struct S {
		void delegate(int x) dg1 = doNothing!(int);
		void delegate(float y) dg2 = doNothing!(float);
		void delegate(int x, int y) dg3 = doNothing!(int,int);
	}

Do you think the first line is worth an addition to Phobos, maybe alongside toDelegate?  It's non-obvious enough that it might save a newbie (or a not-so-newbie like myself :-P) a lot of headache.


T

-- 
Political correctness: socially-sanctioned hypocrisy.
January 13, 2020

On 13/01/2020 19:06, H. S. Teoh wrote:
> On Sat, Jan 11, 2020 at 09:51:24AM -0500, Steven Schveighoffer via Digitalmars-d wrote:
>> On 1/11/20 4:40 AM, Rainer Schuetze wrote:
> [...]
>>> void noop(int) {}
>>>
>>> enum void delegate(int) dg_noop = (int x){ noop(x); };
>>
>> No need for the function call
>>
>> enum void delegate(int) dg_noop = (int x) {};
> [...]
> 
> Mmm, very nice!  Kind of a weird paraphrasis, but at least it works. To minimize hassle, I turned it into a template:
> 
> 	enum void delegate(Args) doNothing(Args...) = (Args args) {};
> 
> 	struct S {
> 		void delegate(int x) dg1 = doNothing!(int);
> 		void delegate(float y) dg2 = doNothing!(float);
> 		void delegate(int x, int y) dg3 = doNothing!(int,int);
> 	}
> 
> Do you think the first line is worth an addition to Phobos, maybe alongside toDelegate?  It's non-obvious enough that it might save a newbie (or a not-so-newbie like myself :-P) a lot of headache.
> 

I think it's a bug in the compiler. If you can assign an enum value, why should the value itself not be good enough aswell?
January 14, 2020
On 13.01.20 23:43, Rainer Schuetze wrote:
> I think it's a bug in the compiler. If you can assign an enum value, why
> should the value itself not be good enough aswell?

Related: https://issues.dlang.org/show_bug.cgi?id=12288
January 20, 2020
On Mon, Jan 13, 2020 at 11:43:22PM +0100, Rainer Schuetze via Digitalmars-d wrote:
> 
> 
> On 13/01/2020 19:06, H. S. Teoh wrote:
[.[..]
> > Mmm, very nice!  Kind of a weird paraphrasis, but at least it works. To minimize hassle, I turned it into a template:
> > 
> > 	enum void delegate(Args) doNothing(Args...) = (Args args) {};
> > 
> > 	struct S {
> > 		void delegate(int x) dg1 = doNothing!(int);
> > 		void delegate(float y) dg2 = doNothing!(float);
> > 		void delegate(int x, int y) dg3 = doNothing!(int,int);
> > 	}
> > 
> > Do you think the first line is worth an addition to Phobos, maybe alongside toDelegate?  It's non-obvious enough that it might save a newbie (or a not-so-newbie like myself :-P) a lot of headache.
> > 
> 
> I think it's a bug in the compiler. If you can assign an enum value, why should the value itself not be good enough aswell?

Added info to the bug:

	https://issues.dlang.org/show_bug.cgi?id=20498


T

-- 
Why is it that all of the instruments seeking intelligent life in the universe are pointed away from Earth? -- Michael Beibl