January 15, 2007
Got it!  No, really, this time FOR SURE ^_^

The end solution is utterly evil, but is, AFAIK, correct.

What I had to do in the end was to say that the size of a class instance is the maximum of:

1) size of the base class + if(implements interfaces)
           padding + interface count * pointer size,
2) max(class member alignof + sizeof) + if(implements interfaces)
           padding + interface count * pointer size

I also added a minimum size of 2*pointer size to account for classes that don't have any member variables.

Getting the padding for interfaces was fun:

> struct Align
> {
>     ubyte a;
>     void* b;
> }
>
> const PTR_ALIGN = Align.tupleof[1].alignof;

I *think* that's right.  In any case, it's reporting the correct size for every test case I throw at it.  Anything else that needs fixing? :P

	-- Daniel

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

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

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;
}

private
template InstanceSizeAligned(T, size_t alignment)
{
    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;
}

January 15, 2007
Daniel Keep wrote:
> 
> Got it!  No, really, this time FOR SURE ^_^
> 
> The end solution is utterly evil, but is, AFAIK, correct.
> 
> What I had to do in the end was to say that the size of a class instance is the maximum of:
> 
> 1) size of the base class + if(implements interfaces)
>            padding + interface count * pointer size,
> 2) max(class member alignof + sizeof) + if(implements interfaces)
>            padding + interface count * pointer size
> 
> I also added a minimum size of 2*pointer size to account for classes that don't have any member variables.
> 
> Getting the padding for interfaces was fun:
> 
>  > struct Align
>  > {
>  >     ubyte a;
>  >     void* b;
>  > }
>  >
>  > const PTR_ALIGN = Align.tupleof[1].alignof;

Nifty!

> I *think* that's right.  In any case, it's reporting the correct size for every test case I throw at it.  Anything else that needs fixing? :P

One other nasty case is where the last member of a class is an 80-bit real, with padding that is different between Windows, Linux32, and Linux64. Does that work correctly?

> 
>     -- Daniel
> 
> private
> struct Align
> {
>     ubyte a;
>     void* b;
> }
> 
> private
> const PTR_ALIGN = Align.tupleof[1].alignof;
> 
> 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;
> }
> 
> private
> template InstanceSizeAligned(T, size_t alignment)
> {
>     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;
> }
> 
January 15, 2007
Don Clugston wrote:
> Daniel Keep wrote:
>> I *think* that's right.  In any case, it's reporting the correct size for every test case I throw at it.  Anything else that needs fixing? :P
> 
> 
> One other nasty case is where the last member of a class is an 80-bit real, with padding that is different between Windows, Linux32, and Linux64. Does that work correctly?

Well, I tried sticking a real into the test cases in various spots, and it seemed fine.  That said, I'm not sure what the potential problem is; it currently determines the size of the member variables by finding the maximum of (member.alignof+member.sizeof) for every member variable. This means that, even if the last member is a real, that it will correctly account for any padding *before* the real, as well as its' full size.

Unless the compiler does really weird stuff like, for example, sticking in padding after a real that isn't part of its' ".sizeof" on non Windows platforms, I don't think it should be a problem.

That said, if you DO find anything amiss, I'd love to know :)

That reminds me.  Walter: I know you're probably busy, but if you could just give this the once-over to verify that it's counting the bytes properly, that would be awesome.

	-- Daniel
January 15, 2007
Daniel Keep wrote:
> Don Clugston wrote:
>> Daniel Keep wrote:
>>> I *think* that's right.  In any case, it's reporting the correct size for every test case I throw at it.  Anything else that needs fixing? :P
>>
>>
>> One other nasty case is where the last member of a class is an 80-bit real, with padding that is different between Windows, Linux32, and Linux64. Does that work correctly?
> 
> Well, I tried sticking a real into the test cases in various spots, and it seemed fine.  That said, I'm not sure what the potential problem is; it currently determines the size of the member variables by finding the maximum of (member.alignof+member.sizeof) for every member variable. This means that, even if the last member is a real, that it will correctly account for any padding *before* the real, as well as its' full size.
> 
> Unless the compiler does really weird stuff like, for example, sticking in padding after a real that isn't part of its' ".sizeof" on non Windows platforms, I don't think it should be a problem.

That's exactly what I was worried about -- reals are followed by 0 bytes of padding on Windows, 2 on Linux32, and 6 on linux64. But it seems it is already included in sizeof; real.sizeof varies between platforms, even though the hardware is identical. real.alignof is constant for all of them.


> That said, if you DO find anything amiss, I'd love to know :)
> 
> That reminds me.  Walter: I know you're probably busy, but if you could just give this the once-over to verify that it's counting the bytes properly, that would be awesome.
> 
>     -- Daniel
1 2
Next ›   Last »