Interesting Research Paper on Constructors in OO Languages
H. S. Teoh
hsteoh at quickfur.ath.cx
Tue Jul 16 08:13:17 PDT 2013
On Tue, Jul 16, 2013 at 01:17:30AM -0700, H. S. Teoh wrote:
[...]
> mixin template BuilderArgs(string fields) {
> struct Args {
> typeof(super).Args base;
> alias base this;
> mixin(fields);
> }
> };
>
> class MyClass : BaseClass {
> public:
> // Hmm, doesn't look too bad!
> mixin BuilderArgs!(q{
> int parm1 = 1;
> int parm2 = 2;
> });
> this(Args args) {
> super(args);
> ...
> }
> }
>
> class AnotherClass : BaseClass {
> public:
> // N.B. Looks exactly the same like MyClass.args except
> // for the fields! The template automatically picks up
> // the right base class Args to "inherit" from.
> mixin BuilderArgs!(q{
> string anotherparm1 = "abc";
> string anotherparm2 = "def";
> });
> this(Args args) {
> super(args);
> ...
> }
> }
>
> Not bad at all! Though, I haven't actually tested any of this code, so
> I've no idea if it will actually work yet. But it certainly looks
> promising! I'll give it a spin tomorrow morning (way past my bedtime
> now).
[...]
Yep, confirmed that this code actually works! Here's the actual test
code that I wrote:
import std.stdio;
mixin template CtorArgs(string fields) {
struct Args {
static if (!is(typeof(super) == Object)) {
typeof(super).Args base;
alias base this;
}
mixin(fields);
}
}
class Base {
private:
int sum;
public:
mixin CtorArgs!(q{
int basefield1 = 1;
int basefield2 = 2;
});
this(Args args) {
sum = args.basefield1 + args.basefield2;
}
int getResult() {
return sum;
}
}
class Derived : Base {
int derivedSum;
public:
mixin CtorArgs!(q{
int parm1 = 3;
int parm2 = 4;
});
this(Args args) {
super(args);
derivedSum = args.parm1 + args.parm2;
}
override int getResult() {
return super.getResult() + derivedSum;
}
}
class AnotherDerived : Base {
private:
int anotherSum;
public:
mixin CtorArgs!(q{
int another1 = 5;
int another2 = 6;
});
this(Args args) {
super(args);
anotherSum = args.another1 + args.another2;
}
override int getResult() {
return super.getResult() + anotherSum;
}
}
// Test usage in a deeper hierarchy
class VeryDerived : AnotherDerived {
int divisor;
public:
mixin CtorArgs!(q{
int divisor = 5;
});
this(Args args) {
super(args);
this.divisor = args.divisor;
}
override int getResult() {
return super.getResult() / divisor;
}
}
void main() {
Derived.Args args1;
args1.basefield1 = 10;
args1.parm1 = 20;
auto obj1 = new Derived(args1);
assert(obj1.getResult() == 10 + 2 + 20 + 4);
AnotherDerived.Args args2;
args2.basefield2 = 20;
args2.another1 = 30;
auto obj2 = new AnotherDerived(args2);
assert(obj2.getResult() == 1 + 20 + 30 + 6);
VeryDerived.Args args3;
args3.divisor = 7;
auto obj3 = new VeryDerived(args3);
assert(obj3.getResult() == 2);
}
Note the nice thing about this: you can construct the ctor arguments
(har har) in any order you like, and it Just Works. Referencing ctor
parameters of base class ctors is just as easy; no need for ugliness
like "args.base.base.base.baseparm1" thanks to alias this. The ctors
themselves just hand Args over to the base class: alias this makes the
struct inheritance pretty transparent. The mixin line itself is
identical across the board, thanks to the static if in the mixin
template, so you can actually re-root the class hierarchy or otherwise
move classes around the hierarchy without having to re-wire any of the
Args handling, and things will Just Work.
Wow. So not only this technique works in D, it's working much *better*
than my original C++ code! I think I shall add this to my personal D
library. :) (Unless people think this is Phobos material.)
T
--
People who are more than casually interested in computers should have at
least some idea of what the underlying hardware is like. Otherwise the
programs they write will be pretty weird. -- D. Knuth
More information about the Digitalmars-d
mailing list