September 10, 2020
[tldr; I have come up with a way of removing all undesired effects of large static array struct members. See conclusion at the bottom.]

Continuing the discussion at

  https://forum.dlang.org/thread/rdk3m2$725$1@digitalmars.com

and understanding kinke's comments there better... And this is all with dmd...


1) Assuming that static array members are really required in the program, the following definition is not desirable because S.init is embedded into the binary image as 8000 bytes (I have much bigger ones in the wild):

struct S {
  double[1000] a;
}

static assert (S.init.sizeof == 8000);
// That S.init is embedded into the binary

One way of proving that S.init is indeed embedded into the binary is running obj2asm that is shipped with dmd: 'obj2asm deneme.o')

So, that is not good.


2) In order to remove that huge S.init from the program, one can initialize the member with '= void':

struct S {
  double[1000] a = void;
}

pragma(msg, S.init); // <-- Aside: As a side effect, this output is
                     //     now just "S()" and does not include
                     //     an array of 1000 elements.

Although now the binary does not contain an S.init of 8000 bytes, it contains CPU instructions to set 1000 elements:

		xor	EAX,EAX
		mov	0FFFFE0C0h[RBP],EAX
		mov	0FFFFE0C4h[RBP],EAX
    [... many more to set all elements ...]

WAT!!! :)

That is not good either because now the compiled code is large. (I think and hope other compilers use memset() here.)

As explained in that earlier thread and as seen above, contrary to spec (perhaps to an earlier spec?) and fortunately, '= void' does not "leave the elements uninitialized" but the elements are now 0.0.

So, that's doubly [pun] bad: The code is large and the elements are not double.nan.


3) To remove that huge struct initialization code, one can @disable the default constructor. And to provide double.nan values, one can provide a function; which may be named specially, or marked with a UDA or some other way. Below, I use a constructor that takes a special type to mark that this is my "default constructor":

struct DefaultCtor {}    // <-- My special "marker"

struct S {
  double[1000] a = void;
  @disable this();

  this(DefaultCtor) {    // <-- My "default constructor"
    a[] = double.init;   // <-- Good: not 0.0 anymore
  }
}

void main() {
  auto s = S(DefaultCtor());    // <-- But now the syntax is ugly

  import std;
  assert(s.a[42].isNaN);
}


4) CONCLUSION: The following 'defaulted' template makes the syntax acceptable as well at least for Ali:

struct DefaultCtor {}     // Special type used as a user-defined UDA ;)

struct S {
  double[1000] a = void;  // To not embed S.init into the binary
  @disable this();        // To not generate many CPU instructions

  this(DefaultCtor) {     // My "default constructor". (This could
    a[] = double.init;    // be a template to not even need a
                          // theoretical rvalue parameter.)
  }
}

template defaulted(T) {  // Generic template for my default constructor syntax
  enum defaulted = {
    return T(DefaultCtor());
  }();
}

void main() {
  auto s = defaulted!S;  // My default construction syntax
}

That method works for me. Am I missing something?

Ali