Thread overview
Does D actually support flexible array members?
Aug 18, 2022
LinguisticMystic
Aug 18, 2022
LinguisticMystic
Aug 18, 2022
Paul Backus
Sep 06, 2022
IchorDev
Sep 06, 2022
Paul Backus
August 18, 2022

I'm porting some C code for arena allocator to D, and somehow the flexible array members (a feature of C99 for dynamically-sized structs) work in D without significant changes in the code. Here's my arena definition:

struct ArenaChunk {
    size_t size;
    ArenaChunk* next;
    char[] memory; // flexible array member
}

struct Arena {
    ArenaChunk* firstChunk;
    ArenaChunk* currChunk;
    int currInd;
}

And here's how I use the FAM's memory for allocating stuff:

    void* result = cast(void*)(&ar.currChunk.memory + ar.currInd);

This seems to work, but I'm a little doubtful, does D really support FAMs in the same way as C, or am I misusing some other D feature here? I mean, FAM's aren't even supported by C++, and aren't listed on the D reference yet somehow the code works.

August 18, 2022

No, D does not support flexible array members or dynamically sized structs.

char[] is a D slice, which is NOT equivalent to a C array.
A slice is basically a pointer+length pair:

// you can't actually name a struct `char[]`, it's just for explanation purposes
struct char[] {
    char* ptr; //pointer to first element
    size_t length;
}

Also the allocation code probably only worked by accident and will likely cause memory corruption:

void* result = cast(void*)(&ar.currChunk.memory + ar.currInd);

&ar.currChunk.memory doesn't give you a pointer to the first slice element - it gives you a pointer to the slice itself (the char[] "struct"). To get a pointer to the first element you can use ar.currChunk.memory.ptr, although since the end goal is to get a pointer to the ar.currInd element it's preferable to replace the entire line with this:

void* result = &ar.currChunk.memory[ar.currInd];

(this way you get bounds checking on slice indexing so you can't get a pointer past the last element of the slice).

Also void[] is a more appropriate type for a raw memory array than char[] (char[] in D is used almost exclusively as "mutable string", and depending on the implementation the garbage collector may not scan char[] elements for pointers).

August 18, 2022

On Thursday, 18 August 2022 at 09:48:48 UTC, Krzysztof Jajeśnica wrote:

>

...

Okay, got it, thanks.

August 18, 2022

On Thursday, 18 August 2022 at 08:41:02 UTC, LinguisticMystic wrote:

>

I'm porting some C code for arena allocator to D, and somehow the flexible array members (a feature of C99 for dynamically-sized structs) work in D without significant changes in the code. Here's my arena definition:

struct ArenaChunk {
    size_t size;
    ArenaChunk* next;
    char[] memory; // flexible array member
}

struct Arena {
    ArenaChunk* firstChunk;
    ArenaChunk* currChunk;
    int currInd;
}

And here's how I use the FAM's memory for allocating stuff:

    void* result = cast(void*)(&ar.currChunk.memory + ar.currInd);

I think the closest way to approximate this in D is to use a zero-length static array:

struct ArenaChunk {
    size_t size;
    ArenaChunk* next;
    char[0] memory;
}

Then your usage example becomes

    void* result = cast(void*)(ar.currChunk.memory.ptr + ar.currInd);

Note that in D you must use .ptr to get a pointer to the array's first element; it does not decay automatically like in C.

September 06, 2022

On Thursday, 18 August 2022 at 11:25:22 UTC, Paul Backus wrote:

>

I think the closest way to approximate this in D is to use a zero-length static array:

struct ArenaChunk {
    size_t size;
    ArenaChunk* next;
    char[0] memory;
}

Would Nullable be a good option as well? https://dlang.org/phobos/std_typecons.html#Nullable

September 06, 2022

On Tuesday, 6 September 2022 at 11:51:35 UTC, IchorDev wrote:

>

On Thursday, 18 August 2022 at 11:25:22 UTC, Paul Backus wrote:

>

I think the closest way to approximate this in D is to use a zero-length static array:

struct ArenaChunk {
    size_t size;
    ArenaChunk* next;
    char[0] memory;
}

Would Nullable be a good option as well? https://dlang.org/phobos/std_typecons.html#Nullable

For a dynamically-sized struct? No, it would not; Nullable has a fixed size.