www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Run-time initialised static variables

reply dekevin <dekevin student.ethz.ch> writes:
Hello everyone,
I just ran into the problem, that I need a static variable, where 
the initialisation code for that variable is only accessible 
during run-time (since part of the initialisation code will be 
dynamically linked).

Is there a way to do this in D?

To be a bit more concrete, this is where I have the problem 
(where ℚ uses GMP, which is dynamically linked):

struct ℚInf {
    ℚ qval;
    immutable static ℚInf zero = ℚInf(0,1);
    this(long num, long den) {
         qval = ℚ(num,den); //this initialisation requires 
dynamically linked code
     }
}
Feb 06 2018
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, February 06, 2018 23:03:07 dekevin via Digitalmars-d-learn 
wrote:
 Hello everyone,
 I just ran into the problem, that I need a static variable, where
 the initialisation code for that variable is only accessible
 during run-time (since part of the initialisation code will be
 dynamically linked).

 Is there a way to do this in D?

 To be a bit more concrete, this is where I have the problem
 (where ℚ uses GMP, which is dynamically linked):

 struct ℚInf {
     ℚ qval;
     immutable static ℚInf zero = ℚInf(0,1);
     this(long num, long den) {
          qval = ℚ(num,den); //this initialisation requires
 dynamically linked code
      }
 }
So, you want qval to be static and initialized at runtime? Then use a static constructor. e.g. struct QInf { ... static this() { qval = ...; } ... } That doesn't work if you're dealing with a static local variable, but it works for static members of structs and classes, and it works for module-level variables. And if you want to make qval immutable, then use a shared static constructor. e.g. struct QInf { ... shared static this() { qval = ...; } ... } You can currently initialize immutable static variables with non-shared static constructors, but that's a bug and will result in the immutable variable being reinitialized whenever a new thread is created. - Jonathan M Davis
Feb 06 2018
parent reply dekevin <dekevin student.ethz.ch> writes:
On Tuesday, 6 February 2018 at 23:21:36 UTC, Jonathan M Davis 
wrote:
 On Tuesday, February 06, 2018 23:03:07 dekevin via 
 Digitalmars-d-learn wrote:
 Hello everyone,
 I just ran into the problem, that I need a static variable, 
 where
 the initialisation code for that variable is only accessible
 during run-time (since part of the initialisation code will be
 dynamically linked).

 Is there a way to do this in D?

 To be a bit more concrete, this is where I have the problem 
 (where ℚ uses GMP, which is dynamically linked):

 struct ℚInf {
     ℚ qval;
     immutable static ℚInf zero = ℚInf(0,1);
     this(long num, long den) {
          qval = ℚ(num,den); //this initialisation requires
 dynamically linked code
      }
 }
So, you want qval to be static and initialized at runtime? Then use a static constructor. e.g. struct QInf { ... static this() { qval = ...; } ... } That doesn't work if you're dealing with a static local variable, but it works for static members of structs and classes, and it works for module-level variables. And if you want to make qval immutable, then use a shared static constructor. e.g. struct QInf { ... shared static this() { qval = ...; } ... } You can currently initialize immutable static variables with non-shared static constructors, but that's a bug and will result in the immutable variable being reinitialized whenever a new thread is created. - Jonathan M Davis
Thanks a lot! I will change all my initialisations to static constructors now. The only additional problem I have, is that ℚInf has a disabled default constructor. 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 } } Best, Kevin
Feb 06 2018
next sibling parent dekevin <dekevin student.ethz.ch> writes:
On Tuesday, 6 February 2018 at 23:50:52 UTC, dekevin wrote:
 On Tuesday, 6 February 2018 at 23:21:36 UTC, Jonathan M Davis 
 wrote:
 [...]
Thanks a lot! I will change all my initialisations to static constructors now. The only additional problem I have, is that ℚInf has a disabled default constructor. 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 } } Best, Kevin
I should perhaps add, that I get the following DMD error message: source/intervalarithmeticrationals.d(15): Error: variable intervalarithmeticrationals.ℚInf.posinf default construction is disabled for type immutable(ℚInf)
Feb 06 2018
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Feb 06 2018
parent reply dekevin <dekevin student.ethz.ch> writes:
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)); } }
Feb 07 2018
parent reply Dominikus Dittes Scherkl <dominikus.scherkl continental-corporation.com> writes:
On Wednesday, 7 February 2018 at 12:10:38 UTC, dekevin wrote:


 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
You can use a different default initializer: ℤ num = 0, den = 1; Thus avoiding the bad denominator.
Feb 07 2018
parent reply dekevin <dekeyser.kevin97 gmail.com> writes:
On Wednesday, 7 February 2018 at 16:26:16 UTC, Dominikus Dittes 
Scherkl wrote:
 On Wednesday, 7 February 2018 at 12:10:38 UTC, dekevin wrote:


 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
You can use a different default initializer: ℤ num = 0, den = 1; Thus avoiding the bad denominator.
I appreciate the help, but I cannot instantiate these variables with a constructor. Whenever I do, the compiler tries to figure out its values at compile-time. This it cannot do however, since the code requires gmp_init for constructing an explicit value, which is only available after dynamically linking GMP. Here is the error I get: source/gmp/z.d(117): Error: __gmpz_init_set_si cannot be interpreted at compile time, because it has no available source code source/util.d(575): called from here: _MpZ(__mpz_struct(0, 0, null)).this(0) source/gmp/z.d(117): Error: __gmpz_init_set_si cannot be interpreted at compile time, because it has no available source code source/util.d(575): called from here: _MpZ(__mpz_struct(0, 0, null)).this(1) I don't really know what to do now, since I have another struct which already uses static variables with the same naming convention and I'll also need it a second time when I write the bindings to MPFR.
Feb 07 2018
parent dekevin <dekeyser.kevin97 gmail.com> writes:
On Wednesday, 7 February 2018 at 20:10:10 UTC, dekevin wrote:
 On Wednesday, 7 February 2018 at 16:26:16 UTC, Dominikus Dittes 
 Scherkl wrote:
 On Wednesday, 7 February 2018 at 12:10:38 UTC, dekevin wrote:


 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
You can use a different default initializer: ℤ num = 0, den = 1; Thus avoiding the bad denominator.
I appreciate the help, but I cannot instantiate these variables with a constructor. Whenever I do, the compiler tries to figure out its values at compile-time. This it cannot do however, since the code requires gmp_init for constructing an explicit value, which is only available after dynamically linking GMP. Here is the error I get: source/gmp/z.d(117): Error: __gmpz_init_set_si cannot be interpreted at compile time, because it has no available source code source/util.d(575): called from here: _MpZ(__mpz_struct(0, 0, null)).this(0) source/gmp/z.d(117): Error: __gmpz_init_set_si cannot be interpreted at compile time, because it has no available source code source/util.d(575): called from here: _MpZ(__mpz_struct(0, 0, null)).this(1) I don't really know what to do now, since I have another struct which already uses static variables with the same naming convention and I'll also need it a second time when I write the bindings to MPFR.
I should perhaps add how ℤ looks like. Here is a stripped-down version: struct ℤ { disable this(); this(T)(T value) trusted if (isArithmetic!T) { version(ccc) { ++_ccc; } static if (isUnsigned!T) __gmpz_init_set_ui(_ptr, value); else static if (isFloating!T) __gmpz_init_set_d(_ptr, value); else // isSigned integral __gmpz_init_set_si(_ptr, value); } ~this() trusted { assert(_ptr, "Pointer is null"); //if(hasBeenInit) __gmpz_clear(_ptr); version(ccc) { ++_ccc; } } inout(__mpz_struct)* _ptr() inout return system { return &_z; } }
Feb 07 2018
prev sibling parent dekevin <dekeyser.kevin97 gmail.com> writes:
On Tuesday, 6 February 2018 at 23:03:07 UTC, dekevin wrote:
 Hello everyone,
 I just ran into the problem, that I need a static variable, 
 where the initialisation code for that variable is only 
 accessible during run-time (since part of the initialisation 
 code will be dynamically linked).

 Is there a way to do this in D?

 To be a bit more concrete, this is where I have the problem 
 (where ℚ uses GMP, which is dynamically linked):

 struct ℚInf {
    ℚ qval;
    immutable static ℚInf zero = ℚInf(0,1);
    this(long num, long den) {
         qval = ℚ(num,den); //this initialisation requires 
 dynamically linked code
     }
 }
In case anyone is curios, thanks to tgehr i was able to resolve the issue. For static variables: struct ℚInf { ℚ qval; static ℚInf zero = void; static this() { zero = ℚInf(0,1); } } For immutable static variables (a bit hacky since it sidesteps the type system): struct ℚInf { ℚ qval; immutable static ℚInf zero = void; static this() trusted { import std.conv: emplace; emplace!ℚInf(cast(ℚInf*)&zero,ℚInf(0,1)); } }
Feb 09 2018