www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Initializing a table of delegates with no-op stubs

reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
Jan 09 2020
next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
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
Jan 09 2020
prev sibling next sibling parent reply Arine <arine123445128843 gmail.com> writes:
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™?
Jan 10 2020
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Jan 10, 2020 at 05:59:53PM +0000, Arine via Digitalmars-d wrote:
 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™?
[...] https://issues.dlang.org/show_bug.cgi?id=20498 T -- English is useful because it is a mess. Since English is a mess, it maps well onto the problem space, which is also a mess, which we call reality. Similarly, Perl was designed to be a mess, though in the nicest of all possible ways. -- Larry Wall
Jan 10 2020
prev sibling parent reply Rainer Schuetze <r.sagitario gmx.de> writes:
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; }
Jan 11 2020
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Jan 11 2020
next sibling parent Arine <arine123445128843 gmail.com> writes:
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.
Jan 11 2020
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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.
Jan 13 2020
parent reply Rainer Schuetze <r.sagitario gmx.de> writes:
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?
Jan 13 2020
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
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
Jan 13 2020
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
Jan 20 2020