 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Valery Guest
|
Posted: Wed Apr 27, 2005 12:35 pm Post subject: layout of two attributes of a class |
|
|
Would someone help to approve the solution below or suggest a better
one?
To put it simple I need the following two attributes to sit one after
another in the class object layout:
A *pA;
B b;
They have to be the attributes of the class Group, which source code is
generated. In reality B is a very small proxy class and I have a lot of
Bs and they all need pA to function properly. I keep pA outside of B in
order to save B's memory footprint.
To achieve this and knowing that Group itself does not guarantee that
its layout is contiguous, I would like to achieve it through some POD
engagement which has some conditional layout guarantees. Unfortunately,
I can't do this:
class Group {
...
struct POD {
A *pA;
B b;
} pod;
...
};
because I have to have the following syntax to access B:
Group g;
g.b;
// or
g->b;
I tried the following path:
struct POD {
A *pA;
B b;
};
class Group : public POD {
...
};
but then I needed a POD constructor. From what I have learnt from some
C++ standard-related discussions on the net a contiguous layout is
guaranteed for POD types or, more specifically to my case, aggregate
classes (I am using terminology from Walter E. Brown's "C++ Language
Note: POD Types" which is published on the net). Apparently any of:
- user-declared constructor or
- a non-static member of any non-POD class type or
- a non-static pointer to any non-POD class type (like A in my case),
destroys the contiguous layout guarantee, so I may need to have:
class POD {
public:
void *pA;
void *pB;
};
which provides for me a default constructor (and all the other default
member functions).
Questions:
1) Do I have a layout guarantee for the above design?
2) Is there a better option?
Regards,
Valery Aronov
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Maxim Yegorushkin Guest
|
Posted: Thu Apr 28, 2005 1:15 am Post subject: Re: layout of two attributes of a class |
|
|
On Wed, 27 Apr 2005 16:35:12 +0400, Valery <valerka (AT) gmail (DOT) com> wrote:
| Quote: | Would someone help to approve the solution below or suggest a better
one?
To put it simple I need the following two attributes to sit one after
another in the class object layout:
A *pA;
B b;
They have to be the attributes of the class Group, which source code is
generated. In reality B is a very small proxy class and I have a lot of
Bs and they all need pA to function properly. I keep pA outside of B in
order to save B's memory footprint.
To achieve this and knowing that Group itself does not guarantee that
its layout is contiguous, I would like to achieve it through some POD
engagement which has some conditional layout guarantees. Unfortunately,
I can't do this:
class Group {
...
struct POD {
A *pA;
B b;
} pod;
...
};
because I have to have the following syntax to access B:
Group g;
g.b;
// or
g->b;
|
Some compilers accept a nonstandard extension with anonimous structs,
using which you could achive the desired syntax:
struct A;
struct B { int i; };
struct Group {
struct { // note the absense of name here
A *pA;
B b;
}; // note the absense of instance here
};
int main()
{
Group g;
g.b.i = 1;
(&g)->b.i = 2;
}
--
Maxim Yegorushkin
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Thiago R. Adams Guest
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Fri Apr 29, 2005 8:18 am Post subject: Re: layout of two attributes of a class |
|
|
Valery wrote:
| Quote: | Would someone help to approve the solution below or suggest a
better one?
To put it simple I need the following two attributes to sit
one after another in the class object layout:
A *pA;
B b;
|
What do you mean by "sit one after another"? If there is no
intervening access specifier, you are guaranteed that in any
given object, &pA < &b. You are also guaranteed that there will
be no intervening named object. You are not guaranteed that
there will be no padding.
| Quote: | They have to be the attributes of the class Group, which
source code is generated. In reality B is a very small proxy
class and I have a lot of Bs and they all need pA to function
properly. I keep pA outside of B in order to save B's memory
footprint.
|
I'm not sure I understand. If a B needs its own pA to function
correctly, how does keeping it outside of the B reduce memory
use? If the object must be there, then it must be there, and it
will occupy the memory that it will occupy. Putting it inside
of B could even reduce memory use, by requiring less padding.
Also, how does B find the pA. Either you pass its address as a
parameter to the constructor, which saves it (and which requires
added memory), or you pass it as a parameter each time you call
a function in B. In both cases, pA can be anywhere.
| Quote: | To achieve this and knowing that Group itself does not
guarantee that its layout is contiguous, I would like to
achieve it through some POD engagement which has some
conditional layout guarantees.
|
There is no difference in the guarantees. The only guarantee
that you have concerning layout is the order (and only the
order) of named elements without an intervening access
specifier.
| Quote: | Unfortunately, I can't do this:
class Group {
...
struct POD {
A *pA;
B b;
} pod;
...
};
|
Which doesn't change anything, anyway.
| Quote: | because I have to have the following syntax to access B:
Group g;
g.b;
// or
g->b;
I tried the following path:
struct POD {
A *pA;
B b;
};
class Group : public POD {
...
};
but then I needed a POD constructor.
|
??? Again, I don't understand what you mean. Formally, from a
C++ language point of view, all class types, POD or other, have
constructors. Practically, we often speak of a class not having
a constructor if the constructor is trivial. Whether someone
inherits from a class or not does not affect whether that class'
constructor is trivial. For that matter, a class can have base
classes (even multiple base classes) and still have a trivial
constructor.
| Quote: | From what I have learnt from some C++ standard-related
discussions on the net a contiguous layout is guaranteed for
POD types or, more specifically to my case, aggregate classes
(I am using terminology from Walter E. Brown's "C++ Language
Note: POD Types" which is published on the net).
|
Contiguous layout is never guaranteed. Ever. It never has
been, not even in C. A compiler is free to insert padding where
ever it wants.
In practice, if B contains a double or a long long, my compilers
will insert padding between pA and b in the above. Even in the
case of a POD. They have to, because of hardware alignment
constraints.
| Quote: | Apparently any of:
- user-declared constructor or
- a non-static member of any non-POD class type or
- a non-static pointer to any non-POD class type (like A in my
case),
destroys the contiguous layout guarantee,
|
I don't know where you got this, but it is wrong. There is no
contiguous layout guarantee, if by contiguous you mean that the
physical address of one element, plus its size, gives the
physical address of the next element. There never has been, not
even in C, and compilers definitly do insert padding between a
pointer and a class type, if that class type requires stricter
alignment than a pointer.
If, by contiguous, all you mean is that nothing else will be
placed between the two elements, the guarantee is present as
long as there is no access specifier between the two elements.
Regardless of whether the class is a POD or not.
| Quote: | so I may need to have:
class POD {
public:
void *pA;
void *pB;
};
which provides for me a default constructor (and all the other
default member functions).
Questions:
1) Do I have a layout guarantee for the above design?
|
No. Given that pA and pB have the same type, however, it is
highly unlikely that the compiler will insert any padding.
| Quote: | 2) Is there a better option?
|
For what? I still haven't figured out what the problem you're
trying to solve is. If pA is not a part of B, the only way B
can access it is if someone furnishes its address, possibly in
the form of a reference, to B (or, of course, if pA is somehow
otherwise globally accessible). Given that, I don't see what
any layout guarantees buy you; IMHO, even the ordering guarantee
is superfluous, because I cannot think of any possible way to
use it.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Valery Guest
|
Posted: Fri Apr 29, 2005 4:36 pm Post subject: Re: layout of two attributes of a class |
|
|
James,
May I start with the clarification on what I was going to achieve. It
is a bit unusual set up that I have, because the class Group is
generated implicitly by the user of the system. This user also writes
various formulas in C++, where she refers to the attributes of the
Group instance. They are compiled together with the generated class
Group.
We've got potentially thousands of attributes in the Group and
many-many thousands of Group instances, but we can achieve a two-byte
footprint for the attribute proxy B, if we can keep pA out of B. pA and
b interplay allows accessing real attributes, which are located
elsewhere. The memory savings are achieved, because the real attributes
are shared by the instances of the Group, but the User is insulated
from the complexities of sharing. User uses b's in her formulas as if
they are proper attributes.
Now let me explain what the layout guarantee buys me. I can get pA
using pointers arithmetic in B's member function then. Thus I do not
waste space keeping absolutely necessary pA in B, fetching it from the
outside of the class B.
My B class has "unsigned short int" attribute and VC++ 7 does allow me
to reach pA outside an instance of B as if there is no padding. You say
that it is not guaranteed with other compilers/platforms, right? Then
the solution may lose part of its appeal because the padding will
increase effective memory taken by B attributes even I can account for
the padding in the B's method implementation for the new platform.
I've done some additional research and discovered the following:
1) it is apparently OK from layout guarantee perspective to stay with
struct POD {
A *pA;
B b;
};
class Group : public POD {
...
};
but A and B can't have a user-defined constructor, so I have to take an
inconvenience of having B without it and I've got an option to have it
for A or replace
A *pA;
with
void *pA;
and cast it to A* when I use it.
I've got VC++ 7 "approval" for the above design with the POD test,
presented at http://www.devx.com/cplus/10MinuteSolution/24908/0/page/1.
2) The question remains: Do I have the Standard layout guarantee for
the above design?
Thank you so much,
Valery Aronov
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Wed May 04, 2005 11:59 pm Post subject: Re: layout of two attributes of a class |
|
|
Valery wrote:
| Quote: | May I start with the clarification on what I was going to
achieve. It is a bit unusual set up that I have, because the
class Group is generated implicitly by the user of the
system. This user also writes various formulas in C++, where
she refers to the attributes of the Group instance. They are
compiled together with the generated class Group.
We've got potentially thousands of attributes in the Group and
many-many thousands of Group instances, but we can achieve a
two-byte footprint for the attribute proxy B, if we can keep
pA out of B.
|
So what does that buy you? If every time you have a B, you also
have a pA, there is no difference in the amount of memory used
whether pA is in B, or not.
| Quote: | pA and b interplay allows accessing real attributes, which are
located elsewhere. The memory savings are achieved, because
the real attributes are shared by the instances of the Group,
but the User is insulated from the complexities of sharing.
User uses b's in her formulas as if they are proper
attributes.
Now let me explain what the layout guarantee buys me. I can
get pA using pointers arithmetic in B's member function then.
Thus I do not waste space keeping absolutely necessary pA in
B, fetching it from the outside of the class B.
|
I sort of guessed what you were trying to do. But I repeat,
it's not guaranteed by the standard. And it doesn't save any
memory; if every B must be immediately preceded by an A*, then
you might as well put the A* in the B, and be done with it.
| Quote: | My B class has "unsigned short int" attribute and VC++ 7 does
allow me to reach pA outside an instance of B as if there is
no padding.
|
It doesn't "allow" it. It's undefined behavior which just
happens to work. With the current version of the compiler.
With a certain set of compiler options.
Now try the following test:
struct A {} ;
struct B { unsigned short s ; } ;
struct BwithPA { A* pA ; unsigned short s ; } ;
struct POD1
{
A* pA ;
B b ;
} ;
struct POD2
{
BwithPA b ;
} ;
int
main()
{
std::cout << sizeof( POD1 ) << std::endl ;
std::cout << sizeof( POD2 ) << std::endl ;
return 0 ;
}
For VC++, running on a typical Windows box, I'd be very
surprised if you got anything but 8 and 8. So where's the gain?
| Quote: | You say that it is not guaranteed with other
compilers/platforms, right? Then the solution may lose part
of its appeal because the padding will increase effective
memory taken by B attributes even I can account for the
padding in the B's method implementation for the new platform.
|
About the only time you might gain memory is if the B object is
immediately followed by something that doesn't need stricter
alignment. Regardless of the footprint of B (supposing it is at
least as small as a pointer), in a sequence A*;B;A*;B, the first
B object will have padding after it.
| Quote: | I've done some additional research and discovered the
following: 1) it is apparently OK from layout guarantee
perspective to stay with
struct POD {
A *pA;
B b;
};
class Group : public POD {
...
};
but A and B can't have a user-defined constructor,
|
I'd be interested in knowing where you did that research,
because whether B has a user defined constructor or not has
nothing to do with it. If the only data member of B is an
unsigned short, and it has no virtual functions, nor any base
classes, you should get sizeof(B) == 2 on any typical 32 bit
architecture. And sizeof(POD) == 8.
| Quote: | so I have to take an inconvenience of having B without it and
I've got an option to have it for A or replace
A *pA;
with
void *pA;
and cast it to A* when I use it.
|
Which should change absolutely nothing on most common
architectures. On a very few odd machines (Unisys 2200, for
example), using a void* might make the memory footprint bigger,
but it should never make it smaller.
I couldn't find anything at the site which even suggested that
what your are trying to do should work. And even if it did, the
site is not authorized, as far as I can tell, to give "V++ 7
approval"; only Microsoft can do that.
What you are trying to do is undefined behavior, according to
the C++ standard. As such, an implementation is free to define
it; if you have something in writing from Microsoft which
guarantees it, you might start thinking about possibly using
it. But I still don't see what it buys you. The memory
footprint of B is only really relevant if you have large arrays
of B. If you require that the B be preceded by an A*, you can't
have large arrays of B.
| Quote: | 2) The question remains: Do I have the Standard layout
guarantee for the above design?
|
Absolutely not. I though I'd already said as much.
--
James Kanze mailto: [email]james.kanze (AT) free (DOT) fr[/email]
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Valery Guest
|
Posted: Mon May 23, 2005 11:48 am Post subject: Re: layout of two attributes of a class |
|
|
James,
I am sorry to stay silent for so long - I thought that everyone, but
me, lost interest in the topic when I checked the newsgroup on the 4
May. I narrowly missed your response then because of the timezones
shift.
Let me clarify the overall POD structure. I believe it alleviates your
objections. It is:
struct POD {
A *pA;
B b1;
B b2;
...
};
This is how a single pA may be used by every B instance in POD. Each B
instance holds its offset to pA and is very much capable to fetch it.
The test suggested at the site above tests if the struct POD in my code
is a legal POD. At least "legal" from the MS perspective (I put the
word into quotes originally; it is this sort of legality) - their
compiler raises an error if it not, - and the above struct POD passed
the test.
Admittedly since then we found a more promising technique to use some
template values to achieve somewhat better outcomes, but I found the
former approach rather interesting - fetching from B some attributes
which reside outside of B's layout.
Valery
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Mon May 23, 2005 5:01 pm Post subject: Re: layout of two attributes of a class |
|
|
Valery wrote:
| Quote: | I am sorry to stay silent for so long - I thought that
everyone, but me, lost interest in the topic when I checked
the newsgroup on the 4 May. I narrowly missed your response
then because of the timezones shift.
Let me clarify the overall POD structure. I believe it
alleviates your objections. It is:
struct POD {
A *pA;
B b1;
B b2;
...
};
This is how a single pA may be used by every B instance in
POD. Each B instance holds its offset to pA and is very much
capable to fetch it.
|
Where does it get this offset from, and how does it hold it? A
priori, the only place it could get it from is a parameter to
the constructor, whatever you hold it in is going to take space
as well, and unless B is very, very small, alignment
considerations will cause whatever you hold it in to effectively
take up the same space as a pointer.
The possible exception would be if B otherwise only contains two
char's or a short, and you keep the offset in a short. In that
particular case, on a typical 32 bit machine, the trick will
half the size of a B.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Allan W Guest
|
Posted: Tue May 24, 2005 10:16 am Post subject: Re: layout of two attributes of a class |
|
|
James Kanze wrote:
| Quote: | Valery wrote:
My B class has "unsigned short int" attribute and VC++ 7 does
allow me to reach pA outside an instance of B as if there is
no padding.
It doesn't "allow" it. It's undefined behavior which just
happens to work. With the current version of the compiler.
With a certain set of compiler options.
|
I agree with your assumption: the danger of relying on compiler-
defined behavior cannot be overstated.
However, I think it's relatively safe to say that whatever padding
a structure has today with compiler X version Y, for any X made by
a recognized software vendor and any Y>1, then compiler X version
Y+1, running on that same platform, will have some set of compiler
options that will produce the same padding. No, it's not guaranteed
by the standard -- but it should work in real life -- if only to
protect legacy code, especially third-party libraries that cannot
be re-compiled.
Even when Microsoft went from Visual Studio 6 to Visual Studio .Net --
arguably one of the biggest changes ever, that did not involve a
platform change (*) -- there's still a "native mode" C++ compiler
that's compatible with old library code.
(*) There are arguments that this WAS NOT a platform change (still
runs on 32-bit Microsoft Windows) or it WAS a platform change (most
compilation models now require the .NET runtime to be loaded onto
the host). That's how huge of a change it was! Either way, my
argument about old library code still being useful, holds.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Valery Guest
|
Posted: Wed May 25, 2005 1:31 am Post subject: Re: layout of two attributes of a class |
|
|
Well, as I said we generate POD structure ourselves and then compile
it. We add Bs one-by-one and set the offset for each. Strictly speaking
you can't use constructor to do it, because a user-defined constructor
breaks layout guarabtee, but in reality it is more convinient to use
constructor and VC 7.0 maintains the layout we need. If we ever need
another compiler we may review this implementaion and easily fix it for
another compiler.
Each B holds the offset only (in fact I ended up with B holding just a
byte - the lower byte of the index; the upper byte of the index shared
by 256 Bs was placed infront of them and accessed via the same
mechanism of reaching things out of the instance layout). As I said the
real data are elsewhere. We set alignment to one byte and waste
nothing at all.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|