Thread overview | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
January 13, 2007 Size of a class instance at compile time | ||||
---|---|---|---|---|
| ||||
Ok, ASSUMING: 1. I'm only going to be compiling with dmd... 2. ...for x86... 3. ...under Windows, is this an accurate way of getting the size of a class instance at compile time? > template InstanceSize(T) > { > const uint InstanceSize = 12 + CountSizes!(typeof(T.tupleof)); > } > > template CountSizes() > { > const uint CountSizes = 0; > } > > template CountSizes(T) > { > const uint CountSizes = T.sizeof; > } > > template CountSizes(T, Tail...) > { > const uint CountSizes = T.sizeof + CountSizes!(Tail); > } It seems to report the right sizes, but I'm not entirely sure what the '12' is for. Pointer to ClassInfo, ... what else? For those wondering: I'm doing this for some custom mixin memory allocators I'm playing with. -- Daniel "isizeof, isizeof, my kingdom for isizeof!" -- "Rick 3" by Bill Wobblepike. |
January 13, 2007 Re: Size of a class instance at compile time | ||||
---|---|---|---|---|
| ||||
Posted in reply to Daniel Keep | Daniel Keep wrote: > > Ok, ASSUMING: > > 1. I'm only going to be compiling with dmd... > 2. ...for x86... > 3. ...under Windows, (2) is a bit redundant after (1)... Also, AFAIK (3) doesn't matter in this respect. > is this an accurate way of getting the size of a class instance at compile time? > > > template InstanceSize(T) > > { > > const uint InstanceSize = 12 + CountSizes!(typeof(T.tupleof)); > > } > > > > template CountSizes() > > { > > const uint CountSizes = 0; > > } > > > > template CountSizes(T) > > { > > const uint CountSizes = T.sizeof; > > } > > > > template CountSizes(T, Tail...) > > { > > const uint CountSizes = T.sizeof + CountSizes!(Tail); > > } Sorry, it's not. > It seems to report the right sizes, but I'm not entirely sure what the '12' is for. Pointer to ClassInfo, ... what else? There are only 8 bytes of overhead: pointer to vtable and pointer to monitor. (the vtable contains the classinfo pointer) See http://www.digitalmars.com/d/abi.html, under "Classes". It doesn't specifically mention how non-static members are formatted, but I think I figured it out (see below). You also forgot to take alignment gaps into account. Try this: ----- template InstanceSize(T) { const InstanceSize = CountSizes!(8, typeof(T.tupleof)); } template CountSizes(size_t N) { const CountSizes = N; } template CountSizes(size_t N, T) { const uint CountSizes = AlignUp!(T.alignof, N) + T.sizeof; } template CountSizes(size_t N, T, Tail...) { const uint CountSizes = CountSizes!(AlignUp!(T.alignof, N) + T.sizeof, Tail); } template AlignUp(size_t Align, size_t N) { static assert((Align & (Align - 1)) == 0, "Alignment not power of two"); const AlignUp = (N + Align - 1) & ~(Align - 1); } // // Test code // class Test { /// Change members to test different layouts char c; int i; byte b; } import std.stdio; void main() { writefln("Static size: ", InstanceSize!(Test)); writefln("Actual size: ", Test.classinfo.init.length); } ----- |
January 13, 2007 Re: Size of a class instance at compile time | ||||
---|---|---|---|---|
| ||||
Posted in reply to Frits van Bommel | Frits van Bommel wrote:
>
> You also forgot to take alignment gaps into account.
I think this may be a problem. The ABI allows class data to be rearranged, so alignment gaps can't be predicted (I think) from the output of tupleof. I still wish we could get .isizeof for this :-p
Sean
|
January 13, 2007 Re: Size of a class instance at compile time | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | Sean Kelly wrote:
> Frits van Bommel wrote:
>>
>> You also forgot to take alignment gaps into account.
>
> I think this may be a problem. The ABI allows class data to be rearranged, so alignment gaps can't be predicted (I think) from the output of tupleof. I still wish we could get .isizeof for this :-p
As I noted earlier in that post, the ABI indeed doesn't specify the format of the non-static member part of an object. It seems to be one of the incomplete parts.
But from my experiments it seems the current compiler (v1.0) doesn't reorder fields.
I've tested my code with several layouts and AFAICT it consistently provides the same answer as a runtime classinfo.init.length. So assuming no edge cases[1] are triggered it should work until the next ABI break at the very least...
Since ABI breaks seem to be indicated by red 'Need to recompile because:' clauses in the changelog, it should be pretty clear when this needs to be changed.
[1]: e.g. I haven't tested using anything with explicit align(N) declarations, not sure what happens there (would .alignof be adjusted?).
|
January 13, 2007 Re: Size of a class instance at compile time | ||||
---|---|---|---|---|
| ||||
Posted in reply to Frits van Bommel | Frits van Bommel wrote:
> [1]: e.g. I haven't tested using anything with explicit align(N) declarations, not sure what happens there (would .alignof be adjusted?).
Yep, definitely breaks there. But I did find a much more elegant implementation that *does* work in that case (as well as in normal cases):
-----
template InstanceSize(T)
{
const InstanceSize =
T.tupleof[$-1].offsetof + T.tupleof[$-1].sizeof;
}
------
_Way_ shorter, and always provides the correct answer under two simple assumptions:
1) The last non-static member is also last in the layout
2) There's no padding at the end.
Assumption (1) is pretty likely to be broken if and when member reordering is implemented, though.
|
January 14, 2007 Re: Size of a class instance at compile time | ||||
---|---|---|---|---|
| ||||
Posted in reply to Frits van Bommel | Frits van Bommel wrote:
> Frits van Bommel wrote:
>
>> [1]: e.g. I haven't tested using anything with explicit align(N) declarations, not sure what happens there (would .alignof be adjusted?).
>
>
> Yep, definitely breaks there. But I did find a much more elegant implementation that *does* work in that case (as well as in normal cases):
>
> -----
> template InstanceSize(T)
> {
> const InstanceSize =
> T.tupleof[$-1].offsetof + T.tupleof[$-1].sizeof;
> }
> ------
>
> _Way_ shorter, and always provides the correct answer under two simple assumptions:
> 1) The last non-static member is also last in the layout
> 2) There's no padding at the end.
>
> Assumption (1) is pretty likely to be broken if and when member reordering is implemented, though.
Oh that's beautiful; I can't believe I forgot about offsetof! Thanks very much for that: this should make the allocators a bit more efficient :)
-- Daniel
|
January 14, 2007 Re: Size of a class instance at compile time | ||||
---|---|---|---|---|
| ||||
Posted in reply to Frits van Bommel | Frits van Bommel wrote: > Frits van Bommel wrote: > >> [1]: e.g. I haven't tested using anything with explicit align(N) declarations, not sure what happens there (would .alignof be adjusted?). > > > Yep, definitely breaks there. But I did find a much more elegant implementation that *does* work in that case (as well as in normal cases): > > ----- > template InstanceSize(T) > { > const InstanceSize = > T.tupleof[$-1].offsetof + T.tupleof[$-1].sizeof; > } > ------ Just wondering; do we even need to worry about align(N) attributes? AFAIK, that only applies to structures, and we can get the size of those using sizeof since they're value types. http://digitalmars.com/d/attribute.html#align -- says down the bottom of that heading that it only applies to structs and struct members. > _Way_ shorter, and always provides the correct answer under two simple assumptions: > 1) The last non-static member is also last in the layout > 2) There's no padding at the end. > > Assumption (1) is pretty likely to be broken if and when member reordering is implemented, though. I just knocked up a slightly more robust version that should *theoretically* still work even if the members are moved around. It basically just does the same thing yours does, but it runs over the whole tuple, and passes out the largest value it finds. Obviously, it doesn't help for the above two points, but Walter seems to like stuff at the start of the block, not trailing off the end. Plus, when would a class require padding? -- Daniel template InstanceSize(T) { const InstanceSize = InstanceSizeImpl!(T, 0); } 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 const InstanceSizeImpl = 0u; } template Max(size_t a, size_t b) { static if( a > b ) const Max = a; else const Max = b; } |
January 14, 2007 Re: Size of a class instance at compile time | ||||
---|---|---|---|---|
| ||||
Posted in reply to Daniel Keep | Daniel Keep wrote: > Frits van Bommel wrote: >> Frits van Bommel wrote: >> >>> [1]: e.g. I haven't tested using anything with explicit align(N) declarations, not sure what happens there (would .alignof be adjusted?). >> >> >> Yep, definitely breaks there. But I did find a much more elegant implementation that *does* work in that case (as well as in normal cases): >> >> ----- >> template InstanceSize(T) >> { >> const InstanceSize = >> T.tupleof[$-1].offsetof + T.tupleof[$-1].sizeof; >> } >> ------ > > Just wondering; do we even need to worry about align(N) attributes? AFAIK, that only applies to structures, and we can get the size of those using sizeof since they're value types. They work for classes too. Not sure if it's documented, but in my test file I have the following: ----- template InstanceSize(T) { const InstanceSize = T.tupleof[$-1].offsetof + T.tupleof[$-1].sizeof; } class Test { char c; int i; } class Test2 { char c; align(1) int i; } import std.stdio; void main() { writefln("Test:"); writefln("Static size: ", InstanceSize!(Test)); writefln("Actual size: ", Test.classinfo.init.length); writefln(); writefln("Test2:"); writefln("Static size: ", InstanceSize!(Test2)); writefln("Actual size: ", Test2.classinfo.init.length); } ----- Which gives size 16 and 13 for Test and Test2, respectively. So alignment declarations definitely makes a difference even for classes. > http://digitalmars.com/d/attribute.html#align -- says down the bottom of that heading that it only applies to structs and struct members. Then I guess either the spec or the implementation is wrong. > I just knocked up a slightly more robust version that should *theoretically* still work even if the members are moved around. It basically just does the same thing yours does, but it runs over the whole tuple, and passes out the largest value it finds. Ah, that's a good solution to that problem I guess. Unless there's ever any weird need to put overhead bytes at the end that should work. (Where are interface vtable pointers put? [tests] Damn, looks like they're at the end :( ) > Obviously, it doesn't help for the above two points, but Walter seems to like stuff at the start of the block, not trailing off the end. Plus, when would a class require padding? Apparently, implementing an interface counts as well... |
January 14, 2007 Re: Size of a class instance at compile time | ||||
---|---|---|---|---|
| ||||
Posted in reply to Frits van Bommel | Frits van Bommel wrote:
> Ah, that's a good solution to that problem I guess.
> Unless there's ever any weird need to put overhead bytes at the end that should work.
> (Where are interface vtable pointers put? [tests] Damn, looks like they're at the end :( )
So I was wondering, could is(T supertuple == super) be of help here?
Specifically, how many interface vptrs are there in an object? One per interface? Or are inherited interfaces folded like classes, one vptr per most-derived interface?
I think this could be made to work if we figure out the details.
It looks to a bit complicated though, since I'm guessing interfaces of base classes come before members of derived classes. That plus double.alignof > (void*).alignof on x86 means that to determine alignment gaps correctly we'd probably need to work from Object up...
I don't feel like coding that right now :(.
|
January 15, 2007 Re: Size of a class instance at compile time | ||||
---|---|---|---|---|
| ||||
Posted in reply to Frits van Bommel | Frits van Bommel wrote:
> Daniel Keep wrote:
>> I just knocked up a slightly more robust version that should *theoretically* still work even if the members are moved around. It basically just does the same thing yours does, but it runs over the whole tuple, and passes out the largest value it finds.
>
>
> Ah, that's a good solution to that problem I guess.
> Unless there's ever any weird need to put overhead bytes at the end that should work.
> (Where are interface vtable pointers put? [tests] Damn, looks like they're at the end :( )
>
>> Obviously, it doesn't help for the above two points, but Walter seems to like stuff at the start of the block, not trailing off the end. Plus, when would a class require padding?
>
>
> Apparently, implementing an interface counts as well...
Oh... BUGGER. Well, that's inconvenient. I guess I'm just going to have to keep trying until I either find a way to do this properly, or Walter adds isizeof :P
Interface vtable pointers; they're just regular D arrays, right? One for each interface?
Here it is: expression.html#IsExpression -- we can use is(T I == super) to get a tuple of the base classes and all the interfaces. If we can accurately predict how large the extra padding is from the number of interfaces, it's sorted.
Time to go test the theory, then :)
-- Daniel
|
Copyright © 1999-2021 by the D Language Foundation