Thread overview
static array of structs clarification questions
Feb 12, 2016
WhatMeWorry
Feb 12, 2016
anonymous
Feb 13, 2016
Marc Schütz
Feb 13, 2016
ZombineDev
Feb 13, 2016
Marc Schütz
February 12, 2016
I was thinking about fixed length arrays of structures the other day so I played around with the flowing code:

   struct Foo
    {
        int    i;
        string str;
        void info() { writeln("i = ", i, "str = ", str); }
    }

    Foo[2] foos;

    auto f1 = Foo(1, "6chars");  // this string is 6 chars long
    auto f2 = Foo(2, "ThisVeryVeryVeryLongStringHas36Chars");

    foos[0] = f1;
    foos[1] = f2;

    writeln("f1 = ", foos[0]);
    writeln("f2 = ", foos[1]);

    writeln("array foos size in bytes is ", foos.arrayByteSize);
    writeln("array foos has ", foos.length, " elements");
    writeln("foos array consists of ", foos);

The output was
f1 = Foo(1, "6chars", null)
f2 = Foo(2, "ThisVeryVeryVeryLongStringHas36Chars", null)
array foos size in bytes is 32
array foos has 2 elements
foos array consists of [Foo(1, "6chars", null), Foo(2, "ThisVeryVeryVeryLongStri
ngHas36Chars", null)]


question #1: The static array must contain the fat pointers to str variables. But where is the string data itself actually held: the stack? the heap? somewhere else? (does it vary depending on location or scope)

question #2: If the above struct was to contain the same struct and the second one contains a third, how would the lower structs be allocated?  Is it "turtles all the way down?

question #2: Of what use is the nulls in the array elements?  When I took out the member function: void info(), the nulls went away.

Thanks.


February 12, 2016
On 12.02.2016 22:08, WhatMeWorry wrote:
> question #1: The static array must contain the fat pointers to str
> variables. But where is the string data itself actually held: the stack?
> the heap? somewhere else? (does it vary depending on location or scope)

Depends on how the string was created. You can create dynamic arrays over any memory. (Remember: string is an alias of immutable(char)[], i.e. a dynamic array.)

I'm not sure where strings from literals are located. Could be some static data section in the executable, or some such. That's beyond me.

> question #2: If the above struct was to contain the same struct and the
> second one contains a third, how would the lower structs be allocated?
> Is it "turtles all the way down?

Struct data is put right where the variable is. Unlike classes and arrays, structs are not references to some other location.

When a struct has a struct member, then the data of the member is put right next to the parent's data. The size of the member is added to the parent's size.

One consequence of this is that you can't have trees with just structs: `struct Node {Node left; Node right;}` - not gonna fly.

> question #2: Of what use is the nulls in the array elements? When I took
> out the member function: void info(), the nulls went away.

My guess is that you declared the struct in a function (e.g. main), and the null is the context pointer. Put the struct declaration at module scope, or make it `static`, and the null thing should go away.

A context pointer is needed when the struct references data from the surrounding function scope. You don't do that here, but the compiler is apparently not smart enough to figure that out.
February 12, 2016
On 2/12/16 4:08 PM, WhatMeWorry wrote:
> I was thinking about fixed length arrays of structures the other day so
> I played around with the flowing code:
>
>     struct Foo
>      {
>          int    i;
>          string str;
>          void info() { writeln("i = ", i, "str = ", str); }
>      }
>
>      Foo[2] foos;
>
>      auto f1 = Foo(1, "6chars");  // this string is 6 chars long
>      auto f2 = Foo(2, "ThisVeryVeryVeryLongStringHas36Chars");
>
>      foos[0] = f1;
>      foos[1] = f2;
>
>      writeln("f1 = ", foos[0]);
>      writeln("f2 = ", foos[1]);
>
>      writeln("array foos size in bytes is ", foos.arrayByteSize);
>      writeln("array foos has ", foos.length, " elements");
>      writeln("foos array consists of ", foos);
>
> The output was
> f1 = Foo(1, "6chars", null)
> f2 = Foo(2, "ThisVeryVeryVeryLongStringHas36Chars", null)
> array foos size in bytes is 32
> array foos has 2 elements
> foos array consists of [Foo(1, "6chars", null), Foo(2,
> "ThisVeryVeryVeryLongStri
> ngHas36Chars", null)]
>
>
> question #1: The static array must contain the fat pointers to str
> variables. But where is the string data itself actually held: the stack?
> the heap? somewhere else? (does it vary depending on location or scope)

It's stored in the static data segment. Basically, directly in the executable.

> question #2: If the above struct was to contain the same struct and the
> second one contains a third, how would the lower structs be allocated?
> Is it "turtles all the way down?

The only way to compose a struct with itself is to use pointers. You can't place a Foo inside a Foo, because it would be infinite in size (the compiler will complain)

> question #2: Of what use is the nulls in the array elements? When I took
> out the member function: void info(), the nulls went away.

That's odd. I think anonymous probably has the answer (they are context pointers), but I'm also surprised they are null, they shouldn't be.

-Steve

February 13, 2016
On Friday, 12 February 2016 at 21:56:09 UTC, Steven Schveighoffer wrote:
> That's odd. I think anonymous probably has the answer (they are context pointers), but I'm also surprised they are null, they shouldn't be.

In this example, `void foo()` doesn't access any outer variables, so there's no need for a context to be created.
February 13, 2016
On Saturday, 13 February 2016 at 10:22:36 UTC, Marc Schütz wrote:
> On Friday, 12 February 2016 at 21:56:09 UTC, Steven Schveighoffer wrote:
>> That's odd. I think anonymous probably has the answer (they are context pointers), but I'm also surprised they are null, they shouldn't be.
>
> In this example, `void foo()` doesn't access any outer variables, so there's no need for a context to be created.

Yes, but the compiler will create a context regardless of this. See also this issue:
https://issues.dlang.org/show_bug.cgi?id=15343
February 13, 2016
On Saturday, 13 February 2016 at 14:53:39 UTC, ZombineDev wrote:
> On Saturday, 13 February 2016 at 10:22:36 UTC, Marc Schütz wrote:
>> On Friday, 12 February 2016 at 21:56:09 UTC, Steven Schveighoffer wrote:
>>> That's odd. I think anonymous probably has the answer (they are context pointers), but I'm also surprised they are null, they shouldn't be.
>>
>> In this example, `void foo()` doesn't access any outer variables, so there's no need for a context to be created.
>
> Yes, but the compiler will create a context regardless of this. See also this issue:
> https://issues.dlang.org/show_bug.cgi?id=15343

It adds a hidden member, but it doesn't actually allocate a context, therefore the member is null:

    auto foo() @nogc {
        int j;
        struct Foo {
            int i;
            void info() { i += 5; }
        }
        return Foo();
    }

Replace `i` by `j`, and it no longer compiles, because then it really allocates a context.

As for your bug report: I believe the documentation specifies the current behaviour somewhere, but I cannot find it now. Nested structs always have a context pointer, except if they don't have methods (for layout compatibility with C). I think this is necessary to avoid "paradoxa" like the following:

    int j;

    struct Foo {
        int i;
        void info() {
            static if(Foo.sizeof == 4)
                j += 5;
            else
                i += 5;
        }
    }