-
sizeof() class with empty base class
Hello all,
I'm attempting to write some portable code between VC++ and GCC. However, GCC is generating something different and unusable for me without fundamental changes to what I want to do. I have the following hierarchy:
Code:
class Base
{
// helper functions here
};
class Vector4 : public Base
{
float x, y, z, w;
};
class Matrix4x4 : public Base
{
Vector4 rows[4];
};
int main()
{
printf( "sizeof( Matrix4 ) = %i\n", (int)sizeof( Matrix4x4 ) );
return 0;
}
VC++ outputs 64.
GCC outputs 68.
I've tried GCC versions 4.3.4 and 4.5.2, and experimental 4.6.1 and 4.7.0 and they all produce 68.
At first glance you'd think the correct output is 64, but I could be missing some loop-hole rule in the C++ standard that allows GCC to inflate the size of the class in such a way.
Removing the base class in either 'Vector4' or 'Matrix4x4' fixes the problem. However, I plan on adding helper functions to 'Base' class that I want accessible from both the child classes. And 'Base' will remain empty of member variables.
Do you all think this is correct behavior or should I post a GCC bug to their database? Or am SoL because the C++ standard doesn't care.
Thanks.
-
Re: sizeof() class with empty base class
Forgot to mention that both VC++ and GCC output: sizeof(Vector4) = 16
Hooray.
-
Re: sizeof() class with empty base class
My guess is it has to do with a difference in if / when / how those compilers generate vtables. AFAIK that implementation detail is not covered in the C++ standard.
Why does this affect what you want to do?
-
Re: sizeof() class with empty base class
Notice that it occurs only when both Vector4 AND Matrix4x4 inherit from Base.
Why exactly do you need inheritance in this situation? When are you going to need a heterogeneous Base* container of Vector4s and Matrix4x4s? That wouldn't make any sense.
Edit: Also, judging by the names, you intend to use these for graphics processing. I found out the hard way that a OO approach is generally detrimental to performance in this case. You are better off using one of the open source graphics math libraries, which have the advantage of using SSE.
-
Re: sizeof() class with empty base class
Why do you care about the class' size in the first place?
-
Re: sizeof() class with empty base class
When I see 'sizeof' in C++ code it usually means that it is about to solve a problem in a very 'C' like way. There may be a C++ idiom that you could use instead.
-
Re: sizeof() class with empty base class
Quote:
Originally Posted by
chopin952
At first glance you'd think the correct output is 64,
No. To any experienced C or C++ programmer, at first glance, the correct output is this:
A programmer should never assume what the sizeof() a type is other than sizeof(char), which is guaranteed to be 1. Basing an application on sizeof(type) sounds like a design flaw. A compiler is free to make the sizeof() any user-defined type anything it wants it to be. A user-defined struct or class can have padding or different alignment that would cause different results -- even the same compiler can cause sizeof() to be different if compiled with different options.
But if your design really needed a type to be sizeof() some specific number, then you should have coded it to make sure of this:
Code:
char tmp[sizeof(MyType) == 64)?1:0];
Now the code will fail to compile if the compiler's sizeof(MyType) doesn't produce the numbers that you're looking for.
Quote:
Do you all think this is correct behavior
Yes, the behaviour is correct.
Quote:
or should I post a GCC bug to their database?
It isn't a bug.
Quote:
Or am SoL because the C++ standard doesn't care.
The standard doesn't care, as long as the sizeof() is >= 1.
Quote:
but I could be missing some loop-hole rule in the C++ standard that allows GCC to inflate the size of the class in such a way.
It has nothing to do with the compiler, and everything to do with the requirements of your application, which are rather restrictive.
As to the standard, the standard only describes the sizeof() built-in types with respect to another built-in type. Nowhere does the standard give any concrete numbers as to what sizeof(x) must be (except for sizeof(char)).
Regards,
Paul McKenzie
-
Re: sizeof() class with empty base class
Code:
char tmp[sizeof(MyType) == 64)?1:0];
Also, you could use:
Code:
static_assert(sizeof(Matrix4x4) == 64, "Error: sizeof(Matrix4x4) was not 64.");
Since static_assert is supported now by both MSVC and GCC.
-
Re: sizeof() class with empty base class
Quote:
Originally Posted by
Chris_F
Code:
char tmp[sizeof(MyType) == 64)?1:0];
Also, you could use:
Code:
static_assert(sizeof(Matrix4x4) == 64, "Error: sizeof(Matrix4x4) was not 64.");
Since static_assert is supported now by both MSVC and GCC.
Yes, that's true, but just in case the OP is willing to try on an older compiler, the char array version will work (or the boost equivalent of this will also work).
Regards,
Paul McKenzie
-
Re: sizeof() class with empty base class
Quote:
Originally Posted by
chopin952
but I could be missing some loop-hole rule in the C++ standard that allows GCC to inflate the size of the class in such a way.
.
Actually, it is a loophole that allows the empty base class to occupy zero bytes.
See http://www2.research.att.com/~bs/bs_...l#sizeof-empty
In 2003 Standard : 10 Derived Classes, paragraph 5
In draft of new standard : 10 Derived Classes, paragraph 8
-
Re: sizeof() class with empty base class
Thanks to Paul and others for your replies.
Quote:
Originally Posted by
Paul McKenzie
No. To any experienced C or C++ programmer, at first glance, the correct output is this: sizeof(Matrix4x4); A programmer should never assume what the sizeof() a type is other than sizeof(char), which is guaranteed to be 1. Basing an application on sizeof(type) sounds like a design flaw. ... A user-defined struct or class can have padding or different alignment that would cause different results.
Ouch, but fair enough. :) I definitely understand alignment rules. I assumed though, that on the same architecture, two compilers that generate alignment compatible code would generate sizeof() compatible code. I'm obviously mistaken.
Regarding sizeof(). I've found three relevant rules in the C++ Standard:
Quote:
Section 5.3.3: sizeof(char), sizeof(signed char) and sizeof(unsigned char) are 1. The result of sizeof applied to any other fundamental type is implementation-defined. ... When applied to a class, the result is the number of bytes in an object of that class including any padding required for placing objects of that type in an array. The size of a most derived class shall be greater than zero.
Section 9: Complete objects and member subobjects of class type shall have nonzero size. Base class subobjects are not so constrained.
Section 10: A base class subobject may be of zero size.
I understand that none that spells out exact sizing. However, since sizeof(Vector4)==16 and according to 5.3.3, the result of sizeof(Matrix4x4) is the number of bytes in that object including any padding, and according to 9/10 Base class is allowed to have zero size, then my real questions should have been:
- Why is GCC adding an extra 4 bytes?
- Where is the extra padding in the class layout?
- Is it garbage values?
- Is it adding a virtual table?
- If I change Matrix4x4 to contain float[16] then the size becomes 64. Why is that any different than the Vector4 members?
- If I make an array of Matrix4x4 with the empty base, does it now need extra padding versus Matrix4x4 without the empty base?
- The alignment of these structures is 4. Why waste the memory? Why stop at 4? Why not add an extra 8, 12, 16, 28 bytes?
I will do some experiments with reading/writing the 68 bytes to see what's in there.
Re: Design. I don't actually need the derivation. It is merely for convenience of access to static functions in Base. I will never pass around Base pointers that represent vectors and matrices. As Stroustrup says in the link provided by Philip: "It allows a programmer to use empty classes to represent very simple concepts without overhead. Of course I could change it and simply make calls to those helpers using full qualification Base::XXX().
Finally, I care about the size because I auto-generate custom RTTI for these classes to be used in a scripting language. The RTTI is generated on one platform and will be read-in by another. This is proving trickier by the moment and I may have to rethink some other concepts.
Thanks all for your answers.
-
Re: sizeof() class with empty base class
Quote:
Originally Posted by
chopin952
[list][*]Why is GCC adding an extra 4 bytes?
Whatever the reason, it is still valid for the authors to do whatever they please, as long as they adhere to the standard.
Quote:
[*]Where is the extra padding in the class layout?
Write a small program that prints out the addresses of each of the members.
Quote:
[*]If I change Matrix4x4 to contain float[16] then the size becomes 64. Why is that any different than the Vector4 members?
Because arrays are guaranteed to be contiguous, while specifying discrete float variables are just that -- discrete float variables. One float value has no connection to another.
Quote:
Finally, I care about the size because I auto-generate custom RTTI for these classes to be used in a scripting language. The RTTI is generated on one platform and will be read-in by another. This is proving trickier by the moment and I may have to rethink some other concepts.
Then if this is the case, you can either try to work out a better design, or if not, use the static_assert() that was mentioned to guarantee you don't produce builds that do not work correctly, and to determine exactly where the incompatible types occur at compile-time (instead of the program failing miserably at run-time).
Regards,
Paul McKenzie
-
Re: sizeof() class with empty base class
I'd rather use std::vector, pretty much suited for the most cases related with contiguous memory represantation.
Code:
std::vector<4, std::vector<float>(4)> mat4x4;
-
Re: sizeof() class with empty base class
Quote:
Originally Posted by
AvDav
I'd rather use std::vector, pretty much suited for the most cases related with contiguous memory represantation.
Code:
std::vector<4, std::vector<float>(4)> mat4x4;
vector is primarily for arrays of dynamic length. Your Matrix4x4 will always be the same dimensions.
You could use std::array<std::array<float,4>,4> or simply float[16].
-
Re: sizeof() class with empty base class
-
Re: sizeof() class with empty base class
actually, in theory, I think that sizeof(Matrix4x4) should be strictly greater then 16*sizeof(float) as far as standard C++ is concerned, and so that GCC is correct and that VC++ is wrong.
indeed, according to
Quote:
Originally Posted by C++2003 - 10.5
A base class subobject might have a layout (3.7) different from the layout of a most derived object of the same type. A base class subobject might have a polymorphic behavior (12.7) different from the polymorphic behavior of a most derived object of the same type. A base class subobject may be of zero size (clause 9); however, two subobjects that have the same class type and that belong to the same most derived object must not be allocated at the same address (5.10).
the rationale being that every distinct object of the same type should have a distinct address. So, the code
Code:
struct Base
{
// helper functions here
};
struct Vector4 : public Base
{
float x, y, z, w;
};
struct Matrix4x4 : public Base
{
Vector4 rows[4];
};
int main()
{
Matrix4x4 m;
Base* pb1 = &m;
Base* pb2 = m.rows;
_ASSERT( pb1 != pb2 );
}
should never fail on a conforming compiler ( and fails on VC++2008 ); that said, a quick google search on the subject shows that compilers generally behave weirdly on this regard and, to be honest, that the standard is not very clear on this point ( for example, what does it mean "allocating an object of zero size" ? I mean, is the compiler allowed, say, to set the address of the second Base subobject to <any> address included in the memory region spanned by subobjects of different type ? )
-
Re: sizeof() class with empty base class
I think superbonzo wins a prize. It's spelled out in the definition of a standard-layout class in section 9 paragraph 6, which refers to that 5.10 rule.
Quote:
6. A standard-layout class is a class that:
— has no non-static data members of type non-standard-layout class (or array of such types) or reference,
— has no virtual functions (10.3) and no virtual base classes (10.1),
— has the same access control (Clause 11) for all non-static data members,
— has no non-standard-layout base classes,
— either has no non-static data members in the most-derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
— has no base classes of the same type as the first non-static data member. This ensures that two subobjects that have the same class type and that belong to the same most-derived object are not allocated at the same address (5.10).
GCC is trying to do exactly that-- ensure that two Base pointers point to different memory even if they come from the beginning of the same object. Not exactly sure why that's a problem though. Still thinking about that.
This following proves that GCC is following that rule:
Code:
// sizeof( Matrix4x4 ) == 64 (exact size)
struct Matrix4x4 : Base
{
Vector4NoBase row0;
Vector4 row1, row2, row3;
};
Unfortunately I don't know of a way to tell GCC that to me it's ok that ( &m == &m.row ). So I have to resort to not using empty base classes. Not that big a deal.
-
Re: sizeof() class with empty base class
Have you tried #pragma pack(1)? It'll change the generated code to make the class take as little memory as possible instead of optimizing it for performance.
I'm also curious to how VC++ does vtables, sizeof(object with 64 bytes of members) should not equal 64. What does this output on VC++?
Code:
#include <iostream>
class foo {
//empty
};
int main(int argc, char**argv){
cout << sizeof(foo) << endl;
}
-
Re: sizeof() class with empty base class
Quote:
Originally Posted by
ninja9578
Have you tried #pragma pack(1)? It'll change the generated code to make the class take as little memory as possible instead of optimizing it for performance.
Thanks for the suggestion ninja. I tried pragma pack out of curiosity even though I don't want to disable native alignment, but GCC still adhered to that 9-6 rule. sizeof(Matrix4x4) became 65 instead of 68. It has to add at least one byte to make &m different from &m.row0.
GCC is correct. It's doing what was told by the standard.
-
Re: sizeof() class with empty base class
Quote:
Originally Posted by
chopin952
Thanks for the suggestion ninja. I tried pragma pack out of curiosity even though I don't want to disable native alignment,
pragma pack() is only useful for POD structs or classes.
You started introducing derived classes, virtual functions, etc. effectively making the types you're using non-POD. Once you do that, then you are leaving the "well defined" world of C, and entering the world of C++ where what you may think works doesn't work.
Regards,
Paul McKenzie
-
Re: sizeof() class with empty base class
Just to give a related example when it comes to POD and non-POD types, a lot of 'C' programmers who learn C++ start to write code like this. What winds up happening is that code like this stumps them, because it looks like it should work, but instead fails miserably:
Code:
#include <string>
#include <stdlib.h>
#include <iostream>
int main()
{
std::string* p = (std::string*)malloc(sizeof(std::string));
std::cout << *p; // should print a blank string
free(p);
}
What could go wrong? You're creating a std::string from the heap, calling the operator << for it, and then freeing it. But the problem is at the very start -- a std::string object was not created at all. A serious of bytes were allocated, but no object creation occurred. So the p is totally bogus if attempting to call member functions through it. You can add to this example the memcpy() bug that a lot of programmers encounter when given a pointer to a non-POD type, and they just wipe out the object with zeros or some other value.
Once non-POD types enter the picture, you have to drop the 'C' way of thinking how things should work. You will get burned by it at some point.
Regards,
Paul McKenzie
-
Re: sizeof() class with empty base class
Paul I appreciate your answers, but why the pedantic need to point out the stupidities of programmers in years past? That std::string example is at the very least mildly insulting.
I know I'm new on this forum. So some slight background info is probably necessary. I have not been a C programmer in practice. I started with Pascal then moved directly to C++ and have been programming professionally since 1997. And please don't go into, "oh if you had programmed C, you would understand C++ better."
I intended this post to be more technical than "hey guys my program no compile, why no compile? What does stdio stand for? Visual Studio?"
Anyway. #pragma pack still works on non-POD. Just because a construct is C++, OO, derived, with virtuals, etc... doesn't mean you shouldn't care about data layout. If I want to allocate 256 of my Matrix4x4, I care that GCC is making me waste 1KB. My original intent with the example was to make a POD structure with the convenience of an empty base, which is invoking an interesting rule in C++, that even our beloved VisualC++ is not respecting. Maybe the MS programmers thought otherwise when it came to that rule.
I'm going to accomplish what I want without the derivation. And even use C-style containment when I need actual base data, to avoid the pitfalls of a vague standard and various compilers. I may even switch to C to feel even more pure.
Thanks for your 23K+ posts.
Are you on the east coast? Let's get together for an in-depth conversation about the usefulness of template meta-programming over some beer.
Regards.
-Amilcar
-
Re: sizeof() class with empty base class
Quote:
Originally Posted by
chopin952
Paul I appreciate your answers, but why the pedantic need to point out the stupidities of programmers in years past?
Have you read the threads here? You will see that there are many threads where the programmer makes mistakes such as above.
Quote:
That std::string example is at the very least mildly insulting.
Check the qsort() vs. std::sort thread that ran here about 10 or so years ago if you want a more advanced example. That was case of 'C' thinking being applied to a C++ program, and the pitfalls assuming that C++ works the same.
Also, my post is not made to insult anyone -- you are anonymous here and no one knows your level of expertise, plus this is not a private thread -- others are reading it and can learn from the various insights and posts here, including the one I made about assuming that malloc() is the same as new (and again, you will see scores of threads about this). But the main point I was trying to make is that C++ is not 'C' when it comes to trying to "think out" what should happen using 'C' as a guideline or base.
Quote:
I know I'm new on this forum. So some slight background info is probably necessary. I have not been a C programmer in practice. I started with Pascal then moved directly to C++ and have been programming professionally since 1997.
Well, I've gone past 25 years of programming professionally, and 20 years of C++.
Quote:
And please don't go into, "oh if you had programmed C, you would understand C++ better."
No way. I'm one of the ones that complains the most about persons learning 'C" programming before C++ to "get a head start", or the poor ways that students are learning C++ as if it's C with some new syntax. I've usually just directed them to this thread:
http://www.parashift.com/c++-faq-lit....html#faq-28.2
Quote:
Anyway. #pragma pack still works on non-POD. Just because a construct is C++, OO, derived, with virtuals, etc... doesn't mean you shouldn't care about data layout.
The issue really is that you have little control over the non-POD parts of the layout. What works today may not work on a later version of the compiler.
Anyway, if you have a solution, and it's sound, then I have no complaints.
Edit:
And BTW, the qsort()/std::sort thread is one of the longest here. The 'C' guy got banned here because he was flabbergasted that his qsort() wasn't beating std::sort, and to top it off, his solution didn't work for a certain compiler if the types were non-POD. He was so sure of himself, but he couldn't show some humility that his "solution" was no solution. At the end of that thread (or another thread), he started to go off the deep end and started personal insults, giving rise to his being banned here.
Regards,
Paul McKenzie
-
Re: sizeof() class with empty base class
Understood. I searched a bit looking for a C++ forum on which to post, and most I found had the inane beginner mind-bogglers. I chose this one because it was primarily VC++ with a non-VC++ section, and hoped there would be some veterans around with who to brainstorm.
Cheers.
Re: C/C++. Who starts learning with C these days, let alone as a bridge to C++? When I talk to new graduates of my local college, all they talk about is Java and Flash. Some of the more intense courses when C was taught here like Data Structures, Linear Algebra, and Automata Theory are either missing or seemingly dumb-down and suffixed "...for Engineers" bah.
edit: Do you know of other good active C/C++ forums, preferably with cross-platform/cross-compiler concerns?
-
Re: sizeof() class with empty base class
Quote:
Originally Posted by
Paul McKenzie
Edit:
And BTW, the qsort()/std::sort thread is one of the longest here. The 'C' guy got banned here because he was flabbergasted that his qsort() wasn't beating std::sort, and to top it off, his solution didn't work for a certain compiler if the types were non-POD. He was so sure of himself, but he couldn't show some humility that his "solution" was no solution. At the end of that thread (or another thread), he started to go off the deep end and started personal insults, giving rise to his being banned here.
Nice. I'll have to catch up on that thread. I always like reading the roller coaster of a sane thread turn personal. Like a geeky soap opera. hah.
Don't worry. Over the years I've learned to bow to humility as soon as it is possible. No need in being hard headed only to have to disappear in shame when conclusively proven wrong. You notice how I started with "VC++ is correct and GCC looks wrong." It was soon reversed with proper investigation.
Best,
-Amilcar
-
Re: sizeof() class with empty base class
Quote:
Originally Posted by
Paul McKenzie
, he started to go off the deep end and started personal insults, giving rise to his being banned here.
AnthonyMai ?
I've just been browsing some of the old threads. Ay Caramba!
-
Re: sizeof() class with empty base class
I liked this response to one of his replies.
"The poster who made the complaint is known for having issues with functions that do what they are defined to do. If any function does not do "what he thinks it should do", that is grounds for complaint [in his eyes]."
-
Re: sizeof() class with empty base class
Quote:
Originally Posted by
chopin952
Who starts learning with C these days, let alone as a bridge to C++? When I talk to new graduates of my local college, all they talk about is Java and Flash. Some of the more intense courses when C was taught here like Data Structures, Linear Algebra, and Automata Theory are either missing or seemingly dumb-down and suffixed "...for Engineers" bah.
It can be difficult to find a good C++ class these days. In retrospect, my high school AP CS course was better in that respect than college was. While my high school class didn't actually teach STL, it at least required us to use their "apvector" class rather than arrays, which was a good start although I didn't appreciate it at the time.
Once I got to Carnegie Mellon, there was a notable anti-C++ bias. There was a C class (which, bless them, taught us how to use man pages), and there were Java classes, and even SML classes. But I don't recall any C++ classes, and certainly none that taught it the "right" way.