digitalmars.D - Post-ctor ctor
- Andrej Mitrovic (74/74) Aug 07 2011 Just throwing an idea..
- bearophile (13/14) Aug 07 2011 I prefer a solution that to me looks simpler, discussed a bit here:
- bearophile (10/11) Aug 07 2011 Or just:
- Trass3r (1/5) Aug 07 2011 Can't express how awkward this is.
- bearophile (9/15) Aug 07 2011 Why don't you try to express how awkward it is?
- Andrei Alexandrescu (8/25) Aug 07 2011 Just define a mixin and use it:
- bearophile (4/10) Aug 08 2011 Do you see people using that in real code?
- Andrei Alexandrescu (3/11) Aug 08 2011 Yes.
- David Nadlinger (6/19) Aug 08 2011 There is currently no »legal« way to get the parameter names in D (oth...
- Timon Gehr (3/23) Aug 08 2011 Always use structs and classes with exactly 4 fields and always name the...
- Jonathan M Davis (3/24) Aug 08 2011 In this particular example, what goes in initFromParameters is specific ...
- David Nadlinger (6/8) Aug 08 2011 If Andrei intended his answer this way, I don't quite see how it would
- Jonathan M Davis (67/75) Aug 08 2011 Well, I don't see what the compiler could do on its own here. It'll gene...
- David Nadlinger (6/15) Aug 08 2011 So you assumed initFromParameters would contain the initialization code
- Jonathan M Davis (16/32) Aug 08 2011 Well, it has to include that code or there was no point in creating the
- Andrej Mitrovic (43/43) Aug 08 2011 Done deal:
- KennyTM~ (28/71) Aug 08 2011 Template-mixin is often shorter and less error-prone.
- Andrej Mitrovic (2/13) Aug 08 2011 Yum! That's much sexier, thanks.
Just throwing an idea.. structs are great since you don't have to make a special constructor just to initialize its fields. E.g.: struct Foo { int a; int b; int c; int d; } void main() { auto foo = Foo(1, 2, 3, 4); } But what if I add an extra couple of fields that have to be initialized based on only the first 4 fields, and I still want to keep the same call in the user code? Here's a hypothetical case: struct Foo { int a; int b; int c; int d; int sum; int average; post this() // post-ctor ctor { sum = a + b + c + d; average = (a + b + c + d) / 4; } } void main() { auto foo = Foo(1, 2, 3, 4); } A post-construction ctor would do any final initializations after field initialization, or after a call to a custom ctor. Otherwise I would have to write: struct Foo { int a; int b; int c; int d; int sum; int average; this(int a, int b, int c, int d) { this.a = a; this.b = b; this.c = c; this.d = d; postCtor(); } private void postCtor() { sum = a + b + c + d; average = (a + b + c + d) / 4; } } void main() { auto foo = Foo(1, 2, 3, 4); } I see that as needlessly implementing in user-code what the compiler can already do on its own. If we had a post-ctor ctor, partial initialization could be done via field initialization instead of using a hand-written ctor, and then a post-ctor would initialize the rest of the fields. Good idea / bad idea? I have a feeling this would clash with that "can't have a default ctor on a struct" rule. I just hate having to manually write ctors for cases where the first N fields are initialized in order while the rest need special initialization.
Aug 07 2011
Andrej Mitrovic:Good idea / bad idea?I prefer a solution that to me looks simpler, discussed a bit here: http://d.puremagic.com/issues/show_bug.cgi?id=3878 With that idea your struct becomes: struct Foo { int a, b, c, d, sum, average; this(int this.a, int this.b, int this.c, int this.d) { sum = a + b + c + d; average = (a + b + c + d) / 4; } } Bye, bearophile
Aug 07 2011
With that idea your struct becomes:Or just: struct Foo { int a, b, c, d, sum, average; this(this.a, this.b, this.c, this.d) { sum = a + b + c + d; average = (a + b + c + d) / 4; } } Bye, bearophile
Aug 07 2011
this(int this.a, int this.b, int this.c, int this.d) { sum = a + b + c + d; average = (a + b + c + d) / 4; }Can't express how awkward this is.
Aug 07 2011
Trass3r:Why don't you try to express how awkward it is? Well, code like this is shorter than what the OP has suggested, it's DRY, it's probably easy to understand (I think you don't need to go look into D docs every time you see something like this), it's easy to type, it avoids a common bug in my code (caused by mixing fields and arguments in the constructor): this(this.a, this.b, this.c, this.d) { sum = a + b + c + d; average = (a + b + c + d) / 4; } Bye, bearophilethis(int this.a, int this.b, int this.c, int this.d) { sum = a + b + c + d; average = (a + b + c + d) / 4; }Can't express how awkward this is.
Aug 07 2011
On 8/7/11 1:16 PM, Andrej Mitrovic wrote:Just throwing an idea.. structs are great since you don't have to make a special constructor just to initialize its fields. E.g.: struct Foo { int a; int b; int c; int d; } void main() { auto foo = Foo(1, 2, 3, 4); } But what if I add an extra couple of fields that have to be initialized based on only the first 4 fields, and I still want to keep the same call in the user code?Just define a mixin and use it: this(int a, int b, int c, int d) { mixin(initFromParameters()); ... } A similar macro could help assignments. Andrei
Aug 07 2011
Andrei Alexandrescu:this(int a, int b, int c, int d) { mixin(initFromParameters()); ... } A similar macro could help assignments.Do you see people using that in real code? Bye, bearophile
Aug 08 2011
On 8/8/11 1:59 PM, bearophile wrote:Andrei Alexandrescu:Yes. Andreithis(int a, int b, int c, int d) { mixin(initFromParameters()); ... } A similar macro could help assignments.Do you see people using that in real code?
Aug 08 2011
On 8/8/11 6:08 AM, Andrei Alexandrescu wrote:On 8/7/11 1:16 PM, Andrej Mitrovic wrote:There is currently no »legal« way to get the parameter names in D (other than parsing the .stringof output for the function type, which is not guaranteed to work, as far as I know), so I don't see how this could be implemented. Did I miss something obvious? Davidstruct Foo { int a; int b; int c; int d; }Just define a mixin and use it: this(int a, int b, int c, int d) { mixin(initFromParameters()); ... }
Aug 08 2011
David Nadlinger wrote:On 8/8/11 6:08 AM, Andrei Alexandrescu wrote:Always use structs and classes with exactly 4 fields and always name them (as well as the constructor arguments) a,b,c,d.On 8/7/11 1:16 PM, Andrej Mitrovic wrote:There is currently no »legal« way to get the parameter names in D (other than parsing the .stringof output for the function type, which is not guaranteed to work, as far as I know), so I don't see how this could be implemented. Did I miss something obvious? Davidstruct Foo { int a; int b; int c; int d; }Just define a mixin and use it: this(int a, int b, int c, int d) { mixin(initFromParameters()); ... }
Aug 08 2011
On 8/8/11 6:08 AM, Andrei Alexandrescu wrote:In this particular example, what goes in initFromParameters is specific to Foo anyway. So, you'd have to write it by hand regardless. - Jonathan M DavisOn 8/7/11 1:16 PM, Andrej Mitrovic wrote:There is currently no »legal« way to get the parameter names in D (other than parsing the .stringof output for the function type, which is not guaranteed to work, as far as I know), so I don't see how this could be implemented. Did I miss something obvious?struct Foo { int a; int b; int c; int d; }Just define a mixin and use it: this(int a, int b, int c, int d) { mixin(initFromParameters()); ... }
Aug 08 2011
On 8/8/11 10:41 PM, Jonathan M Davis wrote:In this particular example, what goes in initFromParameters is specific to Foo anyway. So, you'd have to write it by hand regardless.If Andrei intended his answer this way, I don't quite see how it would be an appropriate solution, as Andrej explicitly stated in his original post: »I see that as needlessly implementing in user-code what the compiler can already do on its own.« David
Aug 08 2011
On 8/8/11 10:41 PM, Jonathan M Davis wrote:Well, I don't see what the compiler could do on its own here. It'll generate the basic constructor which takes the struct's member variables in the order that they're declared, but it doesn't (and can't) generate the extra code that he wants which does other stuff to set those member variables, e.g. sum = a + b + c + d; That kind of code requires the programmer. If we had a post-constructor like Andrej is looking for, then he could put the code in there, but the code still has to go _somewhere_. The initialization of the first 4 member variables as happens in the example might be nice to do via traits so that the first part of the constructor could be generated (which would be what the compiler generates on its own), but the second part (the part which would go in the post-constructor) still has to be written by hand regardless. So, best case, initFromParameters could use traits to generate this.a = a; this.b = b; this.c = c; this.d = d; but the sum = a + b + c + d; average = (a + b + c + d) / 4; would have to be written by hand. Honestly, I think that using a mixin here would be overkill. You might as well just rewrite the whole thing in each constructor. The real gain of something like a post constructor is in not having to write the first portion of the constructor. In the case where you have multiple constructors which need a common set of initialization code but which can't call a common constructor, because there's no default constructor, you can just use a function for that. But if you want to essentially avoid writing half of the constructor be letting the compiler do it for you, you need something like a post constructor. Personally, I'd just write the constructor and be done with it, but I can see why Andrej would want something like this. If you want to do a mixin for this though, then you probably need to mix in the whole constructor rather than just the body like Andrei did. Then it can name the parameters itself, and you don't have the problem of having no way to query for the function parameters. It also avoids having to write the constructor's signature. However, unless you can write the mixin in a generic way which allows you to reuse it among structs (which would probably mean giving it a function to call which did the struct-specific stuff after the basic initalization), a mixin seems like total overkill. So, maybe you could do something like struct Foo { int a; int b; int c; int d; int sum; int average; mixin(structInit!(4, postConstructor)); private postConstructor() { sum = a + b + c + d; average = (a + b + c + d) / 4; } } and structInit would generate this(int a, int b, int c, int d) { this.a = a; this.b = b; this.c = c; this.d = d; initFunc(); } However, that sort of template seems very writeable, and it would be completely reusable, so it's probably a decent solution to the problem. - Jonathan M DavisIn this particular example, what goes in initFromParameters is specific to Foo anyway. So, you'd have to write it by hand regardless.If Andrei intended his answer this way, I don't quite see how it would be an appropriate solution, as Andrej explicitly stated in his original post: »I see that as needlessly implementing in user-code what the compiler can already do on its own.«
Aug 08 2011
On 8/9/11 12:25 AM, Jonathan M Davis wrote:So you assumed initFromParameters would contain the initialization code for sum and average? In this case, using a mixin doesn't make any sense in the first place to me, since it would indeed be very specific to Foo and could be just a private function… DavidOn 8/8/11 10:41 PM, Jonathan M Davis wrote:[snip ginormous reply]In this particular example, what goes in initFromParameters is specific to Foo anyway. So, you'd have to write it by hand regardless.If Andrei intended his answer this way, I don't quite see how it would be an appropriate solution, as Andrej explicitly stated in his original post: »I see that as needlessly implementing in user-code what the compiler can already do on its own.«
Aug 08 2011
On 8/9/11 12:25 AM, Jonathan M Davis wrote:Well, it has to include that code or there was no point in creating the constructor in the first place, since as Andrej notes, Foo(1, 2, 3, 4) works withou declaring a constructor. So, all-in-all, I don't think that Andrei's suggestion works. If you mix in the entire constructor, then you can do it, but just doing a mixin inside of the constructor like Andrei suggested doesn't buy you anything over just putting it directly in the constructor as far as I can see, unless you have multiple constructors. But then the mixin probably wouldn't work anyway, since it was mixing in everything, not just the common stuff. And if all you're doing is the common stuff, then you might as well have a separate, private function to call. So, it looks like the best solution to this is to use either a string mixin or a template mixin to mix in the entire constructor where it's given the number of member variables to put in the constructor's parameters and the function to be the "post constructor" to call after the member variables have been initialized from the function parameters. - Jonathan M DavisSo you assumed initFromParameters would contain the initialization code for sum and average? In this case, using a mixin doesn't make any sense in the first place to me, since it would indeed be very specific to Foo and could be just a private function…On 8/8/11 10:41 PM, Jonathan M Davis wrote:[snip ginormous reply]In this particular example, what goes in initFromParameters is specific to Foo anyway. So, you'd have to write it by hand regardless.If Andrei intended his answer this way, I don't quite see how it would be an appropriate solution, as Andrej explicitly stated in his original post: »I see that as needlessly implementing in user-code what the compiler can already do on its own.«
Aug 08 2011
Done deal: import std.traits; string FieldInit(T, int len, string fun)() { string result; auto fields = [__traits(allMembers, T)]; result ~= "this("; foreach (y, x; (RepresentationTypeTuple!T)[0..len]) { result ~= x.stringof ~ " " ~ fields[y] ~ ", "; } result = result[0.. $-2] ~ ") { "; foreach (x; 0 .. len) { result ~= "this." ~ fields[x] ~ " = " ~ fields[x] ~ "; "; } result ~= fun ~ "();"; result ~= "}"; return result; } struct Foo { int a; int b; int c; int d; mixin( FieldInit!(typeof(this), 4, "_this") ); int sum; int average; void _this() { sum = a + b + c + d; average = (a + b + c + d) / 4; } } void main() { auto foo = Foo(1, 2, 3, 4); assert(foo.sum == 10); assert(foo.average == 2); } Mostly.. Of course it doesn't work too good if the fields are placed below some functions.
Aug 08 2011
On Aug 9, 11 05:05, Andrej Mitrovic wrote:Done deal: import std.traits; string FieldInit(T, int len, string fun)() { string result; auto fields = [__traits(allMembers, T)]; result ~= "this("; foreach (y, x; (RepresentationTypeTuple!T)[0..len]) { result ~= x.stringof ~ " " ~ fields[y] ~ ", "; } result = result[0.. $-2] ~ ") { "; foreach (x; 0 .. len) { result ~= "this." ~ fields[x] ~ " = " ~ fields[x] ~ "; "; } result ~= fun ~ "();"; result ~= "}"; return result; } struct Foo { int a; int b; int c; int d; mixin( FieldInit!(typeof(this), 4, "_this") ); int sum; int average; void _this() { sum = a + b + c + d; average = (a + b + c + d) / 4; } } void main() { auto foo = Foo(1, 2, 3, 4); assert(foo.sum == 10); assert(foo.average == 2); } Mostly.. Of course it doesn't work too good if the fields are placed below some functions.Template-mixin is often shorter and less error-prone. ----------------- mixin template FieldInit(size_t count, alias fun) { this(RepresentationTypeTuple!(typeof(this))[0..count] params) { foreach (y, x; __traits(allMembers, typeof(this))[0..count]) __traits(getMember, this, x) = params[y]; fun(); } } struct Foo { int a; int b; int c; int d; int sum; int average; void _this() { sum = a + b + c + d; average = (a + b + c + d) / 4; } mixin FieldInit!(4, _this); } -----------------
Aug 08 2011
On 8/8/11, KennyTM~ <kennytm gmail.com> wrote:Template-mixin is often shorter and less error-prone. ----------------- mixin template FieldInit(size_t count, alias fun) { this(RepresentationTypeTuple!(typeof(this))[0..count] params) { foreach (y, x; __traits(allMembers, typeof(this))[0..count]) __traits(getMember, this, x) = params[y]; fun(); } }Yum! That's much sexier, thanks.
Aug 08 2011