D vs. placement new (for classes) aka why D needs .sizeof and .alignof for classes (values not references)

Daniel Keep daniel.keep.lists at gmail.com
Mon Apr 16 05:50:51 PDT 2007



Forest Ray wrote:
> Before I add this to the bug database I wanted to run this by everyone and see if I'm overlooking something.  Classes in D are always a reference.  Therefore the .sizeof and .alignof a class (reference) are always (void*).sizeof and (void*).alignof.  D allows the class allocator (new) to be overloaded.  The number of bytes required to hold the class (value) is passed as the first parameter to new(size_t size).  This works well unless you are wanting to perform a placement new.  When using placement new you pass in the storage for the object to be placed.  
> 
> new(size_t size, void* ptr) {
>     return ptr;
> }
> 
> The problem is at compile time you can NOT determine the size or alignment of a class (value), only the reference.  Therefore you can not know the sizeof the buffer to preallocate to hold the class (value).  I believe this to be an oversight in the language design.  The alignment issue is easier to overcome, the ABI states the first field of a class (value) is the vbtbl pointer which will always mean the class (value) is (void*).alignof aligned.  The .sizeof property for a class (value) is not available, here in lies the problem.  Placement new is the easy example where the sizeof the class (value) is needed.  Also consider the case of a blocks of class (values) being preallocated for a free list.

OH but you CAN!  It took me a while to work this out, and I must admit
that I have *not* tested the code in some time.  That said, it should
still work.

In a nutshell, here's how it works: given a class type, you can deduce
the size of an instance at compile time by finding the maximum of
(offset+size) for each member variable of the class.  If the class has
no members, then use 2*(void*).sizeof, to account for the ClassInfo and
vtbl pointers.  You also need to add in some padding for any additional
interfaces.  Finally, you take the maximum of that and the size of the
super class (if any).

It's messy, and it was largely arrived at by "define a whole bunch of
classes and fudge it until it's right", but I've used it in two custom
allocators and never had a problem.

FYI, I release the below code into the public domain: share and enjoy!

	-- Daniel

-----
module etc.mem.util;

private
struct Align
{
    ubyte a;
    void* b;
}

private
const PTR_ALIGN = Align.tupleof[1].alignof;

version( X86 )
    const MEM_ALIGN = 8u;
else
    static assert(false, "Unknown memory alignment for this platform.");

private
template AlignPad(size_t base, size_t aligned)
{
    static if( aligned == 0 )
        const AlignPad = base;
    else
        const AlignPad = ((base+PTR_ALIGN-1)/PTR_ALIGN)*PTR_ALIGN
            + aligned;
}

template InstanceSize(T)
{
    static if( is( T == Object ) )
        const InstanceSize = 2*(void*).sizeof;
    else
        const InstanceSize = Max!(
            AlignPad!(
                InstanceSize!(Super!(T)),
                InterfaceCount!(T)*(void*).sizeof),

            AlignPad!(
                InstanceSizeImpl!(T, 0),
                + InterfaceCount!(T)*(void*).sizeof));
}

private
template Super(T)
{
    static if( is( T S == super ) )
        alias First!(S) Super;
    else
        static assert(false, "Can't get super of "~T.mangleof);
}

private
template First(T)
{
    alias T First;
}

private
template First(T, Ts...)
{
    alias T First;
}

private
template InstanceSizeImpl(T, size_t i)
{
    static if( i < T.tupleof.length )
        const InstanceSizeImpl = Max!(
            T.tupleof[i].offsetof + T.tupleof[i].sizeof,
            InstanceSizeImpl!(T, i+1));
    else
        // This is necessary to account for classes without member
        // variables.
        const InstanceSizeImpl = 2*(void*).sizeof;
}

private
template Max(size_t a, size_t b)
{
    static if( a > b )
        const Max = a;
    else
        const Max = b;
}

template InstanceSizeAligned(T, size_t alignment=MEM_ALIGN)
{
    static if( alignment == 0 )
        const InstanceSizeAligned = InstanceSize!(T);
    else
        const uint InstanceSizeAligned
            = InstanceSizeAlignImpl!(T, alignment).result;
}

private
template InstanceSizeAlignedImpl(T, size_t alignment)
{
    private const base_size = InstanceSize!(T);
    const result = ((base_size+alignment-1)/alignment)*alignment;
}

private
template InterfaceCount(T)
{
    static if( is( T == Object ) )
        const InterfaceCount = 0u;
    else static if( is( T S == super ) )
        const InterfaceCount = InterfaceCountImpl!(S);
}

private
template InterfaceCountImpl(TBase, TInterfaces...)
{
    const InterfaceCountImpl = TInterfaces.length;
}

version( etc_mem_util_main )
{
    import std.stdio;

    interface I1 { }
    interface I2 { }
    interface I3 : I2 { }
    class A { }
    class B : A, I1 { }
    class C : B { ubyte x; real y; }
    class D : C, I3 { }
    class E : D, I3 { }

    template SuperTuple(T)
    {
        static if( is( T S == super ) )
            alias S SuperTuple;
    }

    void main()
    {
        writefln("A: %s; %d, %d", typeid(SuperTuple!(A)),
                A.classinfo.init.length, InstanceSize!(A));
        writefln("B: %s; %d, %d", typeid(SuperTuple!(B)),
                B.classinfo.init.length, InstanceSize!(B));
        writefln("C: %s; %d, %d", typeid(SuperTuple!(C)),
                C.classinfo.init.length, InstanceSize!(C));
        writefln("D: %s; %d, %d", typeid(SuperTuple!(D)),
                D.classinfo.init.length, InstanceSize!(D));
        writefln("E: %s; %d, %d", typeid(SuperTuple!(E)),
                E.classinfo.init.length, InstanceSize!(E));

        writefln("InterfaceCount!(E) == %d", InterfaceCount!(E));
        writefln("ptr alignment: %d", Align.tupleof[1].alignof);
    }
}

-- 
int getRandomNumber()
{
    return 4; // chosen by fair dice roll.
              // guaranteed to be random.
}

http://xkcd.com/

v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D
i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP  http://hackerkey.com/



More information about the Digitalmars-d mailing list