Flexible Default Function Parameters via structs with Nullable Fields
Adam D. Ruppe
destructionator at gmail.com
Tue Apr 30 15:17:00 UTC 2019
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.
More information about the Digitalmars-d-announce
mailing list