See more at the announce forum.
This is copy/pasted from a post I made on the forums.
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:
1 2 void foo(int a, string cool = "low temperature", int[] c = [1, 2, 3]) { 3 import std.stdio; 4 writeln("a = ", a); 5 writeln("cool = ", cool); 6 writeln("c = ", c); 7 } 8 9 // this works for free functions, but not delegates, function pointers, or other callable objects 10 // it also will not automatically call a method, but you can build parameters for it. 11 struct ParamsFor(F...) if(F.length == 1) { 12 static if(is(typeof(F[0]) Parameters == __parameters)) { 13 static foreach(idx, _; Parameters) { 14 static if(__traits(compiles, ((Parameters[idx .. idx + 1] i) => i[0])())) 15 mixin(" 16 Parameters[idx .. idx + 1][0] // type 17 "~__traits(identifier, Parameters[idx .. idx + 1])~" // name 18 = ((Parameters[idx .. idx + 1] i) => i[0])() // initial value 19 ;"); 20 else 21 mixin(" 22 Parameters[idx .. idx + 1][0] // type 23 "~__traits(identifier, Parameters[idx .. idx + 1])~" // name 24 // no initial value 25 ;"); 26 } 27 } else static assert(0, typeof(F[0]).stringof ~ " is not a plain callable"); 28 29 auto opCall()() { 30 static if(__traits(compiles, F[0](this.tupleof))) 31 return F[0](this.tupleof); 32 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)"); 33 } 34 } 35 36 class Test { 37 void foo(int a, int b = 10, int c = 20) { 38 import std.stdio; 39 writeln(a, " ", b, " ", c); 40 } 41 } 42 43 void main() { 44 ParamsFor!foo params; 45 46 params.c = [4,5,6]; 47 48 params(); // calls foo(params.tupleof) for you 49 50 51 ParamsFor!(Test.foo) p2; 52 p2.c = 30; 53 auto f = new Test(); 54 55 //p2(); // will static assert cuz of this 56 57 f.foo(p2.tupleof); // use this instead 58 }
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:
1 static if(!__traits(compiles, ((Parameters _) {}) () )) { 2 @disable this(); 3 this(Parameters params) { 4 this.tupleof = params; 5 } 6 }
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:
1 void main() { 2 // this assumes the version with the constructor 3 // but if you didn't add that code, just remove 4 // the 5 and thus ParamsFor!foo() 5 with(ParamsFor!foo(5)) { 6 c = [4,5,6]; // set the param c... 7 opCall(); // call the function 8 } 9 }
so yeah, kinda cool.
Another bit of copy/pasta here, from this post. It was in reply to someone asking about disabling bool to int conversion in a custom struct, and is only lightly edited.
First, toensure we are on the same page for vocabulary: it is *explicitly* constructed with MyStruct a = something;, it just happens to share the = syntax with assignment... but since it is a new variable being declared here, with its type given, this is explicit construction.
And this construction can occur in a a = x; context too, without a declaration, if it happens in an aggregate constructor.
1 MyStruct a = x; // explicit construction, but with = syntax 2 class A { 3 MyStruct a; 4 this() { 5 a = x; // considered explicit construction! 6 } 7 }
But:
1 void foo(MyStruct a) {} 2 3 foo(MyStruct(x)); // explicit construction 4 5 foo(not_a_struct); // this is implicit construction, and banned by D
And meanwhile:
1 MyStruct a; 2 3 a = x; // now this is assignment 4 5 class A { 6 MyStruct a; 7 void foo() { 8 a = x; // this is also assignment 9 } 10 }
The syntax needs to be taken in context to know if it is assignment or construction.
If it is construction, it calls this(rhs) {} function, if assignment, it calls opAssign(rhs) {} function.
But, once the compiler has decided to call that function, it will allow implicit conversion to its arguments. And that's what you saw: implicit conversion to the necessary type for an explicit construction.
So, it is the implicit conversion to our type we want to prohibit. But, remember that implicit construction, the function call thing we mentioned thing, is banned. Which brings us to a potential solution.
Change "ID" from an alias to a struct of some sort. I've been trying to find similar issues, and I saw once suggested that a person could make a struct with one member and conversions to and from different types. I've also seen "alias X this;" a lot. But my main issues is stopping these conversions, and everything I've seen is about enabling automatic conversion. Ideally, I would have something that's convertible TO a uint when needed, but can't be converted FROM other data types.
This is your answer (though keep reading, I do present another option at the end of this email too that you might like).
struct ID { uint handle; }
And then, if you must allow it to convert to uint, do:
1 struct ID { 2 uint handle; 3 alias handle this; 4 // and then optionally disable other functions 5 // since the alias this now enables ALL uint ops... 6 }
or if you want it to only be visible as a uint, but not modifiable as one:
1 struct ID { 2 private uint handle_; 3 @property uint handle() { return handle_; } 4 alias handle this; // now aliased to a property getter 5 // so it won't allow modification through that/ 6 }
Which is probably the best medium of what you want.
Let's talk about why this works. Remember my example before:
void foo(MyStruct a) {} foo(MyStruct(x)); // explicit construction foo(not_a_struct); // this is implicit construction, and banned by D
And what the construction is rewritten into:
MyStruct a = x; // becomes auto a = MyStruct.this(x);
alias this works as implicit conversion *from* the struct to the thing. Specifically, given:
MyStruct a;
If, a.something does NOT compile, then it is rewritten into a.alias_this.something instead, and if that compiles, that code is generated: it just sticks the alias_this member in the middle automatically.
It will *only* ever do this if: 1) you already have an existing MyStruct and 2) something will not automatically work with MyStruct directly, but will work with MyStruct.alias_this.
#1 is very important: alias this is not used for construction, in any of the forms I described above. It may be used for assignment, but remember, not all uses of = are considered assignment.
Let's go back to your code, but using a struct instead.
1 2 struct ID { 3 uint handle_; 4 @property uint handle() { return handle_; } 5 alias handle this; 6 } 7 8 struct Data 9 { 10 ID id; 11 this(ID id) 12 { 13 this.id = id; 14 } 15 } 16 17 // Forgot to refactor a function to return its 18 // loaded data, rather than a success/fail bool 19 bool load_data() 20 { 21 // some processing 22 return true; 23 } 24 25 int main() 26 { 27 // Very obviously a bug, 28 // but it still compiles 29 Data d = load_data(); 30 return 0; 31 }
kk.d(31): Error: constructor kk.Data.this(ID id) is not callable using argument types (bool) kk.d(31): cannot pass argument load_data() of type bool to parameter ID id
Yay, an error! What happens here?
Data d = load_data();
rewritten into
Data d = Data.this(load_data() /* of type bool */);
Data.this requires an ID struct... but D doesn't do implicit construction for a function arg, so it doesn't even look at the alias this. All good.
What if you *wanted* an ID from that bool?
Data d = ID(load_data());
That compiles, since you are now explicitly constructing it, and it does the bool -> uint thing. But meh, you said ID() so you should expect that. But, what if we want something even more exact? Let's make a constructor for ID.
This might be the answer you want without the other struct too, since you can put this anywhere to get very struct. Behold:
1 struct ID { 2 uint handle_; 3 4 @disable this(U)(U u); 5 this(U : U)(U u) if(is(U == uint)) { 6 handle_ = u; 7 } 8 9 @property uint handle() { return handle_; } 10 alias handle this; 11 }
That stuff in the middle is new. First, it disables generic constructors.
Then it enables one specialized on itself and uses template constraints - which work strictly on the input, with no implicit conversion at all (unless you want it - is(U : item) learn more about is allows implicit conversion there and you can filter through).
Now you can get quite strict. Given that ID struct:
Data d = ID(load_data()); // will not compile! Data d2 = ID(0u); // this one will Data d3 = ID(0); // will not compile!
The difference between 2 and 3 is just that u... it is strict even on signed vs unsigned for these calls.
(thanks to Walter for this general pattern. to learn more, I wrote about this more back in 2016: http://arsdnet.net/this-week-in-d/2016-sep-04.html )
There's a lot of options here, lots of control if you want to write your own structs and a bit more code to disable stuff.
I will just link this post: I wrote about how to do a multi-dimensional array, listing the various overloadable functions to hopefully clarify some of the official language docs.
D 2.086 came out last week. The big news this week though is that DConf is happening in London. It is supposed to be livestreamed and recorded, but I haven't heard any details as to where yet.
GCC 9's stable release came out, which includes D! What this means is D is another language that can be enabled when building gcc.... which happens to bootstrap further D updates later, since gcc and now compile D's D code for later frontends. gdc/gcc should now track new D releases more frequently than it has recently.
Meanwhile, I wrote a couple posts on the forums that I will link to for fun tips, one on automated builder pattern for named function parameter emulation, and one on struct construction, alias this, and disallowing implicit conversions via templates, and also one talking about operator overloading.