On Wednesday, 7 February 2018 at 00:31:01 UTC, Jonathan M Davis
wrote:
On Tuesday, February 06, 2018 23:50:52 dekevin via
Digitalmars-d-learn wrote:
Thanks a lot! I will change all my initialisations to static
constructors now.
I should point out that the downside to static constructors is
that if you have modules that recursively depend on each other,
the program throws an Error at startup, because the runtime
can't determine the correct order of initialization. So, while
static constructors can be very useful, you do have to be
careful with them.
The only additional problem I have, is that ℚInf has a
disabled default constructor.
D does not have default constructors. What you're really
disabling is default initialization. This is an important
distinction in understanding how objects are initialized in D.
The init value is known at compile time and can't involve
runtime stuff, whereas a default constructor would be able to
run arbitrary code at runtime.
Is there a way to allow shared static constructors, but not the
default constructor?
struct ℚInf {
ℚ qval;
immutable static ℚInf zero;
@disable this();
shared static this() {
zero.qval = ℚ(0);
}
this(long num, long den) {
qval = ℚ(num,den); //this initialisation requires
dynamically linked code
}
}
If you're initializing an immutable variable, you have to
initialize it in one go. What you're doing is letting it be
default initialized (which results in a compilation error,
because default initialiaztion is disabled for that type) and
then assigning to one of its members. If the default
initialization weren't disabled, you'd get an error about not
being able to mutate an immutable variable, whereas the fact
that you disabled default initialization but did not explicitly
initialize the variable results in a compilation error about
not initializing the variable.
If you want zero to be immutable, you must initialize the
entire object at once. e.g.
zero = QInf(0, 0);
or whatever makes sense. If you need zero to be initialized
differently from other QInfs, then you could make a private
constructor that just takes the value for qval. But it either
has to be default initialized with whatever the init type is
(which you clearly don't want, since you disabled that), or it
needs to be explicitly given a value.
- Jonathan M Davis
Ah I see the distinction between initialisation and construction
now.
Disabling default initialisation was necessary, because part of
the code of the initialisation code of ℚ is linked at runtime
(mainly to initialise ℤ, which relies on gmp_init which is linked
at runtime).
But even if I initialise it in one go, the compiler still
complains and wants to initialise posinf (complains that default
construction is disabled).
I also tested it with a static constructor and a non-immutable
type and that didn't work either.
Here is a stripped down version of the important bits:
struct ℚInf {
ℚ qval;
enum Inf {
negInf=-1,
nonInf=0,
posInf=+1,
}
Inf inf;
immutable static ℚInf posinf; //at this line the compiler
complains that default construction is disabled for type
immutable(ℚInf)
@disable this();
shared static this() {
posinf = ℚInf(Inf.posInf);
//neginf = ℚInf(Inf.negInf);
}
private this(Inf inf) {
assert(inf != nonInf);
this.inf = inf;
qval = ℚ(0,1);
}
this(long num, long den) {
inf=Inf.nonInf;
qval = ℚ(num,den);
}
}
struct ℚ{
ℤ num, den; //cannot call constructors on these, since they
require gmp_init, which requires runtime code
//Default initialiser disabled, since else num=0,den=0
@disable this();
this(long num){
this(num.ℤ);
}
this(ℤ num){
this.num=ℤ(num);
this.den = 1;
}
this(ℤ num,ℤ den){
assert(den != ℤ(0)); //Disable this for speed
if(den<0){ num=-num; den=-den; }
auto d=gcd(abs(num),den);
num/=d, den/=d;
this.num=num;
this.den=den;
}
this(long num, long den) {
this(ℤ(num),ℤ(den));
}
}