On Tuesday, 30 April 2019 at 13:44:00 UTC, Simen Kjærås wrote:
Now, for the abomination that is callMemberFunctionWithParamsStruct!(t, "f")(combined)... It's just t.f(combined.tupleof) in a bad disguise, and I really can't see the benefit.

If you are doing function parameters, there are two kinda fun things you can do. (Personally, I kinda prefer to just do hand-written builder patters, nicer to document, often easier to read, but this is D, so let's go nuts!)


First, this is an automatically generated struct with members corresponding to function parameters:

---

void foo(int a, string cool = "low temperature", int[] c = [1, 2, 3]) {
        import std.stdio;
        writeln("a = ", a);
        writeln("cool = ", cool);
        writeln("c = ", c);
}

// this works for free functions, but not delegates, function pointers, or other callable objects // it also will not automatically call a method, but you can build parameters for it.
struct ParamsFor(F...) if(F.length == 1) {
        static if(is(typeof(F[0]) Parameters == __parameters)) {
                static foreach(idx, _; Parameters) {
static if(__traits(compiles, ((Parameters[idx .. idx + 1] i) => i[0])()))
                        mixin("
                                Parameters[idx .. idx + 1][0] // type
                                "~__traits(identifier, Parameters[idx .. idx + 
1])~" // name
                                = ((Parameters[idx .. idx + 1] i) => i[0])() // 
initial value
                        ;");
                        else
                        mixin("
                                Parameters[idx .. idx + 1][0] // type
                                "~__traits(identifier, Parameters[idx .. idx + 
1])~" // name
                                // no initial value
                        ;");
                }
} else static assert(0, typeof(F[0]).stringof ~ " is not a plain callable");

        auto opCall()() {
                static if(__traits(compiles, F[0](this.tupleof)))
                        return F[0](this.tupleof);
else static assert(0, __traits(identifier, F[0]) ~ " is not callable this way since it needs a `this` object, do it yourself on the outside with obj.method(params.tupleof)");
        }
}

class Test {
        void foo(int a, int b = 10, int c = 20) {
                import std.stdio;
                writeln(a, " ", b, " ", c);
        }
}

void main() {
        ParamsFor!foo params;

        params.c = [4,5,6];

        params(); // calls foo(params.tupleof) for you


        ParamsFor!(Test.foo) p2;
        p2.c = 30;
        auto f = new Test();

        //p2(); // will static assert cuz of this

        f.foo(p2.tupleof); // use this instead
}

---



But there, required parameters can be left out too - you don't have to set anything. (It also doesn't work with const params and other such troubles, but that really complicates this idea - and is part of why I prefer a hand-written builder thing, so you can handle all those details explicitly.)


We can solve that with a constructor. Right below the first static if in the example, add:

---
                static if(!__traits(compiles, ((Parameters _) {}) () )) {
                        @disable this();
                        this(Parameters params) {
                                this.tupleof = params;
                        }
                }
---

And now you get an obscure error if you don't specific parameters when creating the Params object. But.... eh I don't love it.



Regardless, still though, this stuff is kinda cool. And if you combine with the `with` statement:


---
void main() {
        // this assumes the version with the constructor
        // but if you didn't add that code, just remove
        // the 5 and thus ParamsFor!foo()
        with(ParamsFor!foo(5)) {
                c = [4,5,6]; // set the param c...
                opCall(); // call the function
        }
}
---

so yeah, kinda cool.

Reply via email to