Thread overview
Debug help - Programming in D, page 670 allocate buffer of 10 MyClass class instances
4 days ago
Brother Bill
3 days ago
Ali Çehreli
3 days ago
Brother Bill
3 days ago
Ali Çehreli
4 days ago

The code snippet from the book is:

 // Allocate room for 10 MyClass objects
 MyClass * buffer =
     cast(MyClass*)GC.calloc(
         __traits(classInstanceSize, MyClass) * 10);
class MyClass
{
	int a;
	string name;
	char c;
}

MyClass is defined as a simple class, which requires padding after char c (intentionally).
I want to use GC.calloc to allocate memory for 10 MyClass instances.
But using __traits(classInstanceSize, MyClass) result in 41 bytes, which will require padding.
So I manually add extra bytes to allow each MyClass instance to have an address divisible by 8.
Then I want to initialize them with with core.lifetime.emplace() with default constructor.
Finally, use array index syntax to write to member variables. (This part breaks at run time)

Hopefully, this will be interesting.
Thank you for your assistance!

Console when run:

myClassActualSize: 41
myPaddedClassSize: 48
bytesNeeded: 480
myClasses address: 2864A4A1000
myClassPtrs[0]: 2864A4A1000
i: 0, buffer: 2864A4A1000

 *  The terminal process "C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe -Command "& 'rdmd' 'c:\dev\D\81 - 90\c88_3c_GC_calloc_MyClass\source\app.d'"" terminated with exit code: 1.

source/app.d

import std.stdio;
import core.memory;
import core.lifetime;

void main()
{
    // Allocate room for 10 MyClass objects
    immutable size_t myClassActualSize = __traits(classInstanceSize, MyClass);
    writeln("myClassActualSize: ", myClassActualSize);

    // ensure myClassSize is divisible by 8, to allow for padding
    immutable padding = 8;
    immutable size_t myPaddedClassSize =
        (myClassActualSize % padding == 0) ? myClassActualSize : (myClassActualSize + padding) / padding * padding;

    writeln("myPaddedClassSize: ", myPaddedClassSize);

    immutable bytesNeeded = myPaddedClassSize * 10;
    writeln("bytesNeeded: ", bytesNeeded);
    MyClass* myClasses = cast(MyClass *) GC.calloc(bytesNeeded);
    writeln("myClasses address: ", myClasses);

	alias MyClassPtr = MyClass *;
	MyClassPtr[10] myClassPtrs;

	foreach (i; 0 .. 9) {
		myClassPtrs[i] = myClasses + i * myPaddedClassSize / padding;
		writefln("myClassPtrs[%s]: %s ", i, myClassPtrs[i]);
		auto buffer = cast(MyClass *) emplace(myClassPtrs[i]);
		writefln("i: %s, buffer: %s", i, buffer);
		writeln("i: %s, a: %s", i, myClassPtrs[i].a);		// Compiles. Run time hangs/exception
	}
}

class MyClass
{
    int a;
    string name;
    char c;
}

3 days ago
On 9/2/25 4:51 PM, Brother Bill wrote:
> The code snippet from the book is:
> ```
>   // Allocate room for 10 MyClass objects
>   MyClass * buffer =
>       cast(MyClass*)GC.calloc(
>           __traits(classInstanceSize, MyClass) * 10);
> ```

For clarity, we are talking about this page:

  https://ddili.org/ders/d.en/memory.html

I was wrong there. MyClass* rarely makes sense. Casting GC.calloc that way is not useful at all.

> ```
> class MyClass
> {
>      int a;
>      string name;
>      char c;
> }
>
> ```
> MyClass is defined as a simple class, which requires padding after char
> c (intentionally).
> I want to use GC.calloc to allocate memory for 10 MyClass instances.
> But using __traits(classInstanceSize, MyClass) result in 41 bytes, which
> will require padding.
> So I manually add extra bytes to allow each MyClass instance to have an
> address divisible by 8.

Ok.

> Then I want to initialize them with with core.lifetime.emplace() with
> default constructor.
> Finally, use array index syntax to write to member variables. (This part
> breaks at run time)
>
> Hopefully, this will be interesting.
> Thank you for your assistance!
>
> Console when run:
> ```
> myClassActualSize: 41
> myPaddedClassSize: 48
> bytesNeeded: 480
> myClasses address: 2864A4A1000
> myClassPtrs[0]: 2864A4A1000
> i: 0, buffer: 2864A4A1000
>
>   *  The terminal process "C:
> \WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe -Command "&
> 'rdmd' 'c:\dev\D\81 - 90\c88_3c_GC_calloc_MyClass\source\app.d'""
> terminated with exit code: 1.
> ```
>
> source/app.d
> ```
> import std.stdio;
> import core.memory;
> import core.lifetime;
>
> void main()
> {
>      // Allocate room for 10 MyClass objects
>      immutable size_t myClassActualSize = __traits(classInstanceSize,
> MyClass);
>      writeln("myClassActualSize: ", myClassActualSize);
>
>      // ensure myClassSize is divisible by 8, to allow for padding
>      immutable padding = 8;
>      immutable size_t myPaddedClassSize =
>          (myClassActualSize % padding == 0) ? myClassActualSize :
> (myClassActualSize + padding) / padding * padding;
>
>      writeln("myPaddedClassSize: ", myPaddedClassSize);
>
>      immutable bytesNeeded = myPaddedClassSize * 10;
>      writeln("bytesNeeded: ", bytesNeeded);
>      MyClass* myClasses = cast(MyClass *) GC.calloc(bytesNeeded);

Again, I misled you there.

>      writeln("myClasses address: ", myClasses);
>
>      alias MyClassPtr = MyClass *;

That's not useful because the type MyClass is already a reference type, implemented as a pointer by the compiler.

Yes, pointer to a class type will make sense in some cases but not here.

>      MyClassPtr[10] myClassPtrs;

So, that could be MyClass[]. Note, not the objects but MyClass values will be stored there, which are references to MyClass objects.

>      foreach (i; 0 .. 9) {

I removed hard-coded 10 above because your loop was one less than 10 iterations.

>          myClassPtrs[i] = myClasses + i * myPaddedClassSize / padding;
>          writefln("myClassPtrs[%s]: %s ", i, myClassPtrs[i]);
>          auto buffer = cast(MyClass *) emplace(myClassPtrs[i]);
>          writefln("i: %s, buffer: %s", i, buffer);
>          writeln("i: %s, a: %s", i, myClassPtrs[i].a);        //

That's due to a syntax simplicity of D: Although your array element type is a pointer, D uses the dot instead of the arrow on the pointer, which redicets to the object.

So, the syntax above is actually this:

  (*myClassPtr[i]).a;

However, the elements are not MyClass references.

> Compiles. Run time hangs/exception
>      }
> }
>
> class MyClass
> {
>      int a;
>      string name;
>      char c;
> }
>
> ```

With apologies, I modified your code heavily with notes added as [Ali]:

import std.stdio;
import core.memory;
import core.lifetime;

void main()
{
    // Allocate room for 10 MyClass objects
    immutable size_t myClassActualSize = __traits(classInstanceSize, MyClass);
    writeln("myClassActualSize: ", myClassActualSize);

    // ensure myClassSize is divisible by 8, to allow for padding
    immutable padding = 8;
    immutable size_t myPaddedClassSize =
        (myClassActualSize % padding == 0) ? myClassActualSize : (myClassActualSize + padding) / padding * padding;

    writeln("myPaddedClassSize: ", myPaddedClassSize);

    enum elementCount = 10;    // [Ali] Removing code duplication:

    immutable bytesNeeded = myPaddedClassSize * elementCount;
    writeln("bytesNeeded: ", bytesNeeded);

    // [Ali] Fixing my mistake by removing the cast:
    void* myClassObjects = GC.calloc(bytesNeeded);

    writeln("myClasses address: ", myClassObjects);

    // [Ali] 1) Removing static array size not for correctness but just because
    // [Ali] 2) Note the type of the array: MyClass is a
    //          reference to object; no pointer needed
    // [Ali] 3) Renamed as myClasses
    MyClass[] myClasses;

    // [Ali] Replaced 9 with elementCount
    foreach (i; 0 .. elementCount) {
        // [Ali] 1) Removed cast because now it's already void*
        // [Ali] 2) Dividing by padding did not make sense
        auto address = myClassObjects + i * myPaddedClassSize;

        // [Ali] 1) Removed the index; just appending to the array
        //          (not necessary, just because...);
        // [Ali] 2) Used emplace to call the constructor (this
        //          works because I add a constructor below)
        // [Ali] 3) Note emplace's first parameter is a void slice
        myClasses ~= emplace!MyClass(address[0..myPaddedClassSize], i);

        writefln("myClasses[%s]: %s ", i, myClasses[i]);
        writefln("i: %s, a: %s", i, myClasses[i].a);
    }
}

class MyClass
{
    int a;
    string name;
    char c;

    // [Ali] Added a constructor
    this(int a) {
        this.a = a;
    }
}


Ali

3 days ago

On Wednesday, 3 September 2025 at 03:09:45 UTC, Ali Çehreli wrote:

>

GC.calloc(bytesNeeded);

Again, I misled you there.

>
 writeln("myClasses address: ", myClasses);

 alias MyClassPtr = MyClass *;

That's not useful because the type MyClass is already a reference type, implemented as a pointer by the compiler.

Yes, pointer to a class type will make sense in some cases but not here.

>
 MyClassPtr[10] myClassPtrs;

So, that could be MyClass[]. Note, not the objects but MyClass values will be stored there, which are references to MyClass objects.

>
 foreach (i; 0 .. 9) {

I removed hard-coded 10 above because your loop was one less than 10 iterations.

Ali

Made some further changes, and have additional questions.

Console output:

myClassActualSize: 41
myPaddedClassSize: 48
bytesNeeded: 480
myClasses address: 25DF6931000

0: {a: 0, name: Sally, c: D}, &myClasses[i]: 25DF6932000, &myClasses[i].a: 25DF6931010
1: {a: 1, name: Sally, c: D}, &myClasses[i]: 25DF6932008, &myClasses[i].a: 25DF6931040
2: {a: 2, name: Sally, c: D}, &myClasses[i]: 25DF6932010, &myClasses[i].a: 25DF6931070
3: {a: 3, name: Sally, c: D}, &myClasses[i]: 25DF6932018, &myClasses[i].a: 25DF69310A0
4: {a: 4, name: Sally, c: D}, &myClasses[i]: 25DF6932020, &myClasses[i].a: 25DF69310D0
5: {a: 5, name: Sally, c: D}, &myClasses[i]: 25DF6932028, &myClasses[i].a: 25DF6931100
6: {a: 6, name: Sally, c: D}, &myClasses[i]: 25DF6932030, &myClasses[i].a: 25DF6931130
7: {a: 7, name: Sally, c: D}, &myClasses[i]: 25DF6932038, &myClasses[i].a: 25DF6931160
8: {a: 8, name: Sally, c: D}, &myClasses[i]: 25DF6932040, &myClasses[i].a: 25DF6931190
9: {a: 9, name: Sally, c: D}, &myClasses[i]: 25DF6932048, &myClasses[i].a: 25DF69311C0

source/app.d

import std.stdio;
import std.string;
import core.memory;
import core.lifetime;

void main()
{
	enum elementCount = 10;

    // Allocate room for 10 MyClass objects
    immutable size_t myClassActualSize = __traits(classInstanceSize, MyClass);
    writeln("myClassActualSize: ", myClassActualSize);

    // ensure myClassSize is divisible by 8, to allow for padding
    immutable padding = 8;
    immutable size_t myPaddedClassSize =
        (myClassActualSize % padding == 0)
			? myClassActualSize
			: (myClassActualSize + padding) / padding * padding;
    writeln("myPaddedClassSize: ", myPaddedClassSize);

    immutable bytesNeeded = myPaddedClassSize * elementCount;
    writeln("bytesNeeded: ", bytesNeeded);

    void* myClassObjects = GC.calloc(bytesNeeded);
    writeln("myClasses address: ", myClassObjects);

    // [Ali] Note the type of the array:
	//       MyClass is a reference to object; no pointer needed
    MyClass[] myClasses;

    foreach (i; 0 .. elementCount) {
        void * address = myClassObjects + (i * myPaddedClassSize);

        // The first parameter guides emplace() of slice to instantiate MyClass instance.
        myClasses ~= emplace!MyClass(address[0 .. myPaddedClassSize], i, "Sally", 'D');
    }
	writeln;

	foreach (i; 0 .. elementCount) {
		writefln("%s: %s, &myClasses[i]: %s, &myClasses[i].a: %s",
				  i, myClasses[i], &myClasses[i], &myClasses[i].a);
	}
}

class MyClass
{
    int a;
    string name;
    char c;

    this(int a, string name, char c) {
        this.a = a;
		this.name = name;
		this.c = c;
    }

	override string toString() const {
		return format("{a: %s, name: %s, c: %s}", a, name, c);
	}
}

Questions:

  1. Is it necessary to pad myPaddedClassSize to an 8 byte granularity on 64 bit machine?
    If so, should emplace() be modified to provide the padding internally?

  2. There is a 16 byte header before myClasses[0].a
    Is this for class overhead?

3 days ago
On 9/3/25 5:10 AM, Brother Bill wrote:

> Made some further changes, and have additional questions.

One of your question involves object aligment, which I mention here:

  https://ddili.org/ders/d.en/memory.html#ix_memory..alignof

I discovered two (somewhat cryptic) functions there that computes sizes of objectn with padding:

T * nextAlignedAddress(T)(T * candidateAddr) {
    import std.traits;

    static if (is (T == class)) {
        const alignment = classInstanceAlignment!T;

    } else {
        const alignment = T.alignof;
    }

    const result = (cast(size_t)candidateAddr + alignment - 1)
                   / alignment * alignment;
    return cast(T*)result;
}

size_t sizeWithPadding(T)() {
    static if (is (T == class)) {
        const candidateAddr = __traits(classInstanceSize, T);

    } else {
        const candidateAddr = T.sizeof;
    }

    return cast(size_t)nextAlignedAddress(cast(T*)candidateAddr);
}

>      // ensure myClassSize is divisible by 8, to allow for padding

So, instead of the hard-coded 8, I think the sizeWithPadding() function template above should be helpful here.

> Questions:
> 1. Is it necessary to pad myPaddedClassSize to an 8 byte granularity on
> 64 bit machine?

Yes but you we use the .alignof property instead of relying on a constant like 8.

>     If so, should emplace() be modified to provide the padding internally?

I feel like it's too much freedom for a standard library to take. But I can see how it could take an option meaning "place on the next appropriate address".

> 2. There is a 16 byte header before myClasses[0].a
>     Is this for class overhead?

D classes have two pointers as overhead:

* A pointer to the virtual function pointer table of that class type.

* Monitor, another pointer, which I heard is a concept borrowed from C#.

Monitor allows class instances to be used as thread syncronization primitives.

When the monitor is not needed (i.e. almost always), you can define the class as extern(C++):

extern (C++)
class Foo {
  // ...
}

Now there is less overhead because there is no monitor.

Ali