www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 20498] New: A way to initialize a struct of delegates with

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

          Issue ID: 20498
           Summary: A way to initialize a struct of delegates with no-op
                    stubs?
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: enhancement
          Priority: P1
         Component: dmd
          Assignee: nobody puremagic.com
          Reporter: hsteoh quickfur.ath.cx

Given a struct of delegates:

------
struct S {
    void delegate(int x) dg1;
    void delegate(float y) dg2;
    void delegate(int x, int y) dg3;
    ... // etc., many more delegates here
}
------

There ought to be an easy, hassle-free way of initializing the delegates to
point to a no-op stub delegate that simply does nothing and returns. 
Currently, it's not possible to do this because dmd complains that lambdas or
delegates of any kind of not readable at compile-time:

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

    // But none of the following works (they all fail to compile):
    void delegate(int) dg1 = &dummy; // NG
    void delegate(int) dg2 = (int) {}; // NG
    void delegate(int) dg3 = (int) => dummy(0); // NG

    import std.functional;
    void delegate(int) dg4 = toDelegate((int) {}); // NG
    void delegate(int) dg5 = toDelegate(&dummy); // NG
}
------

The fact that the function pointer f1 can be initialized to point to a function
implies that the address of a function can be used to initialize struct fields,
even though it may not actually be bound until runtime.  By that logic, a dummy
delegate that does nothing should also be assignable to the struct's .init
value (the context pointer is ignored so it can simply be set to null).

Currently the only way to initialize the delegate fields is to do so at runtime
by using a verbose, circuitous workaround:

------
struct S {
    void delegate(int) dg1;
    void delegate(float) dg2;
    void delegate(string) dg3;
    void delegate(int,int) dg4;
    void delegate(bool,dchar) dg5;
}

S s;
static this()
{       
        static void nop(Args...)(Args) {}
        foreach (ref dg; s.tupleof)
        {       
                import std.functional : toDelegate;
                import std.traits : Parameters;
                dg = toDelegate(&nop!(Parameters!(typeof(dg))));
        }
}
------

The template for `nop` is necessary because each struct field may have
different parameters, requiring a function of a different signature, and
putting inside the foreach loop doesn't work because the compiler complains
that it's defined multiple times.

This seems extremely verbose and cumbersome for something so simple as setting
the context pointer to null and .ptr to the address of a `ret` instruction.


Desired behaviour: there should be a simple, concise way to initialize a series
of void delegates to point to a do-nothing stub.  This should be doable at
compile-time, and should not require the above obscure verbiage (or
equivalent).

Possible syntax:  perhaps something along these lines:

------
void delegate(int) = {};
void delegate(string,bool) = {};
... // and so on
------

or perhaps:

------
void delegate(int) = () {};
------

--
Jan 10 2020