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.

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,
bearophile

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

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?
David

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?
David

Always use structs and classes with exactly 4 fields and always name them (as
well
as the constructor arguments) a,b,c,d.

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

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.«

[snip ginormous reply]

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…
David

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?

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 Davis

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.«

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 Davis

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.«

[snip ginormous reply]

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…

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 Davis