Reading Struct not Located at Four-boundary

A

Alf P. Steinbach /Usenet

* Ian Collins, on 20.08.2010 22:59:
OK, let's see if I got this straight. Since the size of those two
structs is the same, they will both fit correctly aligned in a space
allocated via "new char[sizeof(a)]" (or via "new char[sizeof(b)]", for
that matter).

I think you are still missing the point that size and alignment are
orthogonal.

I'd miss it too, since alignment requirement /is/ a function of size.

That goes down to the hardware level, it's not a matter of formalism.

Regarding the formalism, specific alignment for a given object can be specified,
by language extensions or in C++0x within the standard language; such a specific
alignment must conform to the alignment requirement for the type; the alignment
requirement for the type in turn must conform to the alignment requirement for
the type's size, which is what matter to the HW.

But "new short[sizeof(double) / sizeof(short)]", alone and by itself,
has to satisfy the alignment requirements of "short", regardless of the
size of the array itself.

So in some sense, both the type and the size of that type do play an
important role in the alignment of the address returned by dynamic
allocators - although that role is implementation defined within the
limits mandated by the standard.

No, the size is still irrelevant to the guarantee.

In theory yes the size is irrelevant to the new operator's guarantee, since it
guarantees to return a pointer suitably aligned for any item.

However, in practice there can be hardware support for items of larger size than
standard C++ supports, then with special alignment requirements.

E.g., for SIMD instructions there is a 16-byte alignment requirement, and most
likely operator new will use an 8-byte alignment (at best), not sufficient.

Whether you are
allocating a double or an array of sizeof(double) char or a single char,
the alignment of the pointer will be the same. The pointer returned will
always be appropriately aligned for objects of any type.


Cheers,

- Alf (picking nits)
 
G

Gennaro Prota

Suitably aligned for any object that fits into the allocated space, as I
seem to have understood, so far, from the other branches of this thread.

In any case, I was surely overlooking (once more) the difference between
using plain "new" and calling "the operator new function". How were they
commonly referred to? "new expression" and "operator new"?

Use the terminology of the standard ("new expression" and
"operator new function")!

<asides>
- The reason for the excitement and the consequent exclamation
point is that I do use it, and I feel lonely.

- If you really want to refer to the operator (as opposed to
the expression) you have to think more.
</asides>

I seem to recall someone advocating the usage of "operator new"
for the function(s) and "new operator" for the operator but to
me... Perhaps it's crystal clear to a native speaker? (Anyway,
it may work in some contexts, especially when italics or
different fonts are used properly, and I've seen it used by
Dewhurst with extreme clarity. It's just not something to
advocate in general.)

I think one may also use "new allocation function", which is
unambiguous too.

PS: quite obviously, too, if one says for instance "write
operator delete if you write operator new", like our Scott
Meyers does, it's clear that he's not talking about the
operators :) Yet I'm picky enough to avoid such language anyway
(no, not avoiding C++, just such terminology ;-)).
 
I

Ian Collins

* Ian Collins, on 20.08.2010 22:59:
OK, let's see if I got this straight. Since the size of those two
structs is the same, they will both fit correctly aligned in a space
allocated via "new char[sizeof(a)]" (or via "new char[sizeof(b)]", for
that matter).

I think you are still missing the point that size and alignment are
orthogonal.

I'd miss it too, since alignment requirement /is/ a function of size.

No, it isn't, at least not to the size we are debating, that is the size
passed to the allocation function.
That goes down to the hardware level, it's not a matter of formalism.

Correct. A 32 bit machine may be able to perform efficient byte aligned
operation on any type, so it could have an alignment requirement of one
for any type. Another 32 bit machine may be incapable of byte
addressing and therefore have an alignment requirement of four for any type.

A lot of 32 bit machines have the same alignment requirement (four) for
32 bit float and 64 bit double (or 32 bit long and 64 bit long long).

The alignment used by allocator functions is typically bigger than that
required by the machine. All the hosted environment allocators I've
checked use 16 or 32.
>
> In theory yes the size is irrelevant to the new operator's guarantee,
> since it guarantees to return a pointer suitably aligned for any item.

In practice too. The guarantee is just that, a guarantee.

It might be clearer to say the size /passed/ the the allocation function
is irrelevant. The alignment used is derived from the hardware, not run
time information.
Cheers,

- Alf (picking nits)

Putting some back!

Cheers,
 
G

Gennaro Prota

On 20/08/2010 17.32, Pete Becker wrote:
[...]
The standard says, in [expr.new] 5.3.4p10, that char arrays do have
such restrictions, but it says that related to char arrays created
using "new", that would mean that "new char[sizeof(double)]" should
return an address properly aligned for any object of the same size of a
double.

Are you saying that the requirements are different for automatic and
static char arrays as opposed to dynamically created ones?

Sorry, I made a bit of a muddle here. The key point is that alignment
is defined by the hardware; whatever the standard says about alignment
is pretty much handwaving, to avoid putting unnecessary restrictions on
implementations.

Those words in [expr.new] try to put some logic into what typically
hasn't been explicitly laid out in the past. The underlying principle
is that the size of the requested allocation might eliminate some
types, because they're too large to fit in the requested space; in that
case, the alignment of the returned pointer doesn't have to be
appropriate for those types, because the memory block is too small to
hold them. For example, the result of calling operator new(4) doesn't
have to be aligned appropriately for an 8-bit double, only for types
that occupy four bits or less.

Actually this seems in contradiction with 3.7.3.1/2 and with the
note to 5.3.4/10 itself which says "aligned for objects of any
type". When will the standard writers employ the same principles
that hold for programming, such as DRY?

And I think, at this time of the night, the "difference between
the result of the new-expression and the address returned by the
allocation function" matter is just confusing me.

This stuff concretely affects what the programmer knows about
the pointer returned by an operator new function so, if after
some sleep, I still think that there's a contradiction I'll file
a DR (I don't file DRs for logical inconsistencies and such with
no concrete impact on real software. There are just too many of
those, IMHO, and the result is just wasting time, for the
committee and for language users, who would see other important
things delayed).

I'm particularly worried that someone just reads 3.7.3.1 and
assumes "aligned for everything" (which is correct: why should
he suspect that another section says otherwise?).

In this case, where should the DR go? Core or library? Both?
(I'm inclined to "core"... but this lays astride (well, more on
the core side of the things, as I see it)).
 
I

Ian Collins

Those words in [expr.new] try to put some logic into what typically
hasn't been explicitly laid out in the past. The underlying principle
is that the size of the requested allocation might eliminate some
types, because they're too large to fit in the requested space; in that
case, the alignment of the returned pointer doesn't have to be
appropriate for those types, because the memory block is too small to
hold them. For example, the result of calling operator new(4) doesn't
have to be aligned appropriately for an 8-bit double, only for types
that occupy four bits or less.

Actually this seems in contradiction with 3.7.3.1/2 and with the
note to 5.3.4/10 itself which says "aligned for objects of any
type".

I agree. 3.7.3.1 states that "The pointer returned shall be suitably
aligned so that it can be converted to a pointer of any complete object
type". The note in 5.3.4p10 is based on that.

The examples in 5.3.4p12 appears to contradict what Pete says:

5.3.4p12 [Example:

— new T results in a call of operator new(sizeof(T)),

which follows on form the note in 5.3.4p10

[Note: Because allocation functions are assumed to
return pointers to storage that is appropriately aligned for objects of
any type, this constraint on array allocation overhead permits the
common idiom of allocating character arrays into which objects of other
types will later be placed. ]

Which implies to me that new(4) *does* have to be (or at least will be)
aligned appropriately for an 8-bit double, even though the space is too
small.
This stuff concretely affects what the programmer knows about
the pointer returned by an operator new function so, if after
some sleep, I still think that there's a contradiction I'll file
a DR (I don't file DRs for logical inconsistencies and such with
no concrete impact on real software.

All the programmer has to know is the pointer is suitably aligned.
I'm particularly worried that someone just reads 3.7.3.1 and
assumes "aligned for everything" (which is correct: why should
he suspect that another section says otherwise?).

Because none does?
 
F

Francesco S. Carta

I am replying to nobody in particular, right now. It seems to me that
people more experienced than me have not yet found an agreement in this
very thread, so I'll stay out of the discussion because my intervention
will likely add confusion instead of dissipating it.

This is just an update about what I've worked out till now.

I have tracked down a misconception of mine that was influencing my
thought about all of this.

I've created a struct that occupies 64 bytes of storage, then I
dynamically allocated it and I checked its address to see if, as I was
expecting, it was a multiple of 64. As it turned out, it wasn't, showing
me that my assumption was stupid at best.

I have no idea about where I have taken that misconception from, I could
very well have made it up all by myself.

I still need some study to clarify the concepts used by the standard
about "operator new", "new operator", their array versions, the flow of
these operations and so on, so I won't speak any further about a subject
that isn't clear enough to me yet, I'll come back about this later to
ask further informations if reading the relative parts of the standard
will not suffice.

About the implementation-defined part of this discussion, that is, the
actual value of the alignments, finding a method for determining this
value will at least help me understand the implementation's behavior,
which in turn will eventually help me understanding the abstract
behavior mandated by the standard.

I've found a method in the page linked below, and the explanations given
make sense to me, any further reference or eventually a warning about
wrong informations given there (if any) will be more than welcome:

http://www.monkeyspeak.com/alignment/
 
J

James Kanze

On 2010-08-20 12:47:39 -0400, Francesco S. Carta said:

[...]
If you're really into technicalities, since a class can override
operator new, the actual code that the compiler generates for new T
typically calls T's constructor with a null pointer, and T's
constructor calls operator new.

I know that this is a common implementation, but is it formally
conform? I'm not sure that it is 100% clear, but by my reading,
the following program is conform:

class C
{
void* operator new(size_t);
public:
C();
};

int
main()
{
C aC;
}

With at least some compilers I've used, this (or something
similar---I don't have a compiler installed on this machine to
test) fails to link, because there is no definition of
C::eek:perator new (and the compiler is using the implementation
you suggest).

A similar problem arises with delete.
 
J

James Kanze

* Ian Collins, on 20.08.2010 22:59:
On 08/21/10 04:04 AM, Francesco S. Carta wrote:
OK, let's see if I got this straight. Since the size of those two
structs is the same, they will both fit correctly aligned in a space
allocated via "new char[sizeof(a)]" (or via "new char[sizeof(b)]", for
that matter).
I think you are still missing the point that size and alignment are
orthogonal.
I'd miss it too, since alignment requirement /is/ a function of size.

Not "a function of size", but "constrained by size". The
alignment isn't orthogonal, since the constraint exists
(alignment cannot be greater than size), but it's not a function
of size either, since as long as the constraint is met, there's
no problem.
That goes down to the hardware level, it's not a matter of formalism.

Except that Ian has already posted a contradicting example:
char[sizeof(double)] and double have the same size (required by
the standard), but don't have the same alignment requirements.

[...]
Regarding the formalism, specific alignment for a given object
In theory yes the size is irrelevant to the new operator's
guarantee, since it guarantees to return a pointer suitably
aligned for any item.

I'm not sure about this. I've heard two explinations (and I
can't remember which is right): a non-member allocation function
must return memory sufficiently aligned for any object, or it
must return memory sufficiently aligned for any object which
will fit.
 
A

Alf P. Steinbach /Usenet

* James Kanze, on 21.08.2010 17:14:
* Ian Collins, on 20.08.2010 22:59:
On 08/21/10 04:04 AM, Francesco S. Carta wrote:
OK, let's see if I got this straight. Since the size of those two
structs is the same, they will both fit correctly aligned in a space
allocated via "new char[sizeof(a)]" (or via "new char[sizeof(b)]", for
that matter).
I think you are still missing the point that size and alignment are
orthogonal.
I'd miss it too, since alignment requirement /is/ a function of size.

Not "a function of size", but "constrained by size". The
alignment isn't orthogonal, since the constraint exists
(alignment cannot be greater than size), but it's not a function
of size either, since as long as the constraint is met, there's
no problem.

Well it gets thorny in the details. For the general case the basic HW alignment
on a given system is a function of size: size -> alignment requirement. But then
you have things like special instructions for special kinds of data, with
special alignment requirements -- outside of the general case, and not very
well covered, if at all, by C++ rules. Yeah, I could've been more precise.

That goes down to the hardware level, it's not a matter of formalism.

Except that Ian has already posted a contradicting example:
char[sizeof(double)] and double have the same size (required by
the standard), but don't have the same alignment requirements.

No it's not a contradiction. The data item size for char[N], what you can deal
[...]
Regarding the formalism, specific alignment for a given object
In theory yes the size is irrelevant to the new operator's
guarantee, since it guarantees to return a pointer suitably
aligned for any item.

I'm not sure about this. I've heard two explinations (and I
can't remember which is right): a non-member allocation function
must return memory sufficiently aligned for any object, or it
must return memory sufficiently aligned for any object which
will fit.

C++98 §3.7.3.1/2 "The pointer returned shall be suitably aligned so that it can
be converted to a pointer of any complete object type and then used to access
the object or array in the storage allocated".

At the ultra-formal level I think this doesn't say anything, since "converted"
isn't defined (at least as far as I can see).

But from a mereley practical-formal point of view it says the address must be
suitably aligned for any complete object type.


Cheers,

- Alf
 
L

Larry Evans

On 08/21/10 06:30, Francesco S. Carta wrote:
[snip]
I've created a struct that occupies 64 bytes of storage, then I
dynamically allocated it and I checked its address to see if, as I was
expecting, it was a multiple of 64. As it turned out, it wasn't, showing
me that my assumption was stupid at best.

I have no idea about where I have taken that misconception from, I could
very well have made it up all by myself.
[snip]

Hi Francesco.

I struggled a few yrs (months?) ago about that and think I've
figured it out.

The alignment of your 64 byte long structure is dependent on the
types contained in the structure.

Using variadic template:

http://www.osl.iu.edu/~dgregor/cpp/variadic-templates.html

notation, augmented with:

template
< unsigned Index
, typename... T
struct at_c
{
typedef
//T... the I-th type in T...
type
;
};

assume:

template
< typename... T
struct C
{
typename at_c<1,T...>::type m_1;
typename at_c<2,T...>::type m_2;
...
typename at_c<n,T...>::type m_n;
};

then, again

then alignof(C) is *only* a function of the alignments of
T... .

I'm pretty sure:

eq[1]: alignof(C) == fold(lcm,1,alignof(T)...)

where fold is the haskell foldl or foldr
which is basically the stl::accumulate.
alignof(T)... basically expands to:

alignof(T_1), alignof(T_2), ..., alignof(T_n)

NOTE, sizeof doesn't appear in eq[1]:.

If eq[1] is true, then the
offsets for all the members can
be easily calculated by assuring that
the offset of m_i in C is divisible by the
alignof(T_i). This is because, since the start of C is
at an address that is divisible by alignof(T_i)
(since alignof(C) is a multiple of alignof(T_i),
since alignof(C) was calculated as the lcm of all of
the alignof(T...)) , then m_i will be located at an address divisible by
aligndof(T_i).

All this is implemented with templates here:

http://svn.boost.org/svn/boost/sandbox/variadic_templates/boost/composite_storage/

Now the one thing missing from this explanation is the role
of size. Now in order for all C's in:

C cvec[2];

to be properly aligned, the sizeof(C) must also be a multiple
of the alignof(C). Thus there may be some padding at the
end of C to achieve this. Likewise, in order to calculates
the offset of m_i to put m_i at the proper alignment, some
padding may also be needed. I'm pretty sure:

Sorry if this seems complicated. It is, at least to me :(

HTH.

-Larry
 
F

Francesco S. Carta

On 08/21/10 06:30, Francesco S. Carta wrote:
[snip]
I've created a struct that occupies 64 bytes of storage, then I
dynamically allocated it and I checked its address to see if, as I was
expecting, it was a multiple of 64. As it turned out, it wasn't, showing
me that my assumption was stupid at best.

I have no idea about where I have taken that misconception from, I could
very well have made it up all by myself.
[snip]

Hi Francesco.

I struggled a few yrs (months?) ago about that and think I've
figured it out.

The alignment of your 64 byte long structure is dependent on the
types contained in the structure.

Hi Larry, thank you for your post.

Indeed, I suspected something like that, by now I see that regardless of
what I am allocating, the "largest" alignment amounts to 32 bytes on my
system, so it all should come down to the most stringent alignment
required for fundamental types.
Using variadic template:

I'm snipping all of your post for brevity as...

Sorry if this seems complicated. It is, at least to me :(

....believe me, all of that is not complicated for me, it is just plain
unintelligible: I never really put my hands on such advanced features of
the upcoming new C++ standard, I've only read about some of them in some
article here and there, I'm afraid I'll have to stay on the current
standard for some good time still, as there are too many basic concepts
that still trick me even if I already have acquired some good bits on
all the existing features.

Nonetheless I'll give it a shot and I'll come back here, because sooner
or later I'll have to face such stuff so maybe it's better to start
before my brain goes completely rusty :)
 
G

Gennaro Prota

On 2010-08-20 12:47:39 -0400, Francesco S. Carta said:
[...]
If you're really into technicalities, since a class can override
operator new, the actual code that the compiler generates for new T
typically calls T's constructor with a null pointer, and T's
constructor calls operator new.

I know that this is a common implementation, but is it formally
conform? I'm not sure that it is 100% clear, but by my reading,
the following program is conform:

class C
{
void* operator new(size_t);
public:
C();
};

int
main()
{
C aC;
}

With at least some compilers I've used, this (or something
similar---I don't have a compiler installed on this machine to
test) fails to link, because there is no definition of
C::eek:perator new (and the compiler is using the implementation
you suggest).

And it becomes particularly interesting when you add =delete to
the declaration (in C++0x, of course).
 
L

Larry Evans

On 08/21/10 11:06, Larry Evans wrote:
[snip]
Using variadic template:

http://www.osl.iu.edu/~dgregor/cpp/variadic-templates.html

notation, augmented with:

template
< unsigned Index
, typename... Tstruct at_c
{
typedef
//T... the I-th type in T...
type
;
};

assume:

template
< typename... Tstruct C
{
typename at_c<1,T...>::type m_1;
typename at_c<2,T...>::type m_2;
...
typename at_c<n,T...>::type m_n;
};

then, again

then alignof(C) is *only* a function of the alignments of
T... .

I'm pretty sure:

eq[1]: alignof(C) == fold(lcm,1,alignof(T)...)

where fold is the haskell foldl or foldr
which is basically the stl::accumulate.
alignof(T)... basically expands to:

alignof(T_1), alignof(T_2), ..., alignof(T_n)

NOTE, sizeof doesn't appear in eq[1]:.

If eq[1] is true, then the
offsets for all the members can
be easily calculated by assuring that
the offset of m_i in C is divisible by the
alignof(T_i). This is because, since the start of C is
at an address that is divisible by alignof(T_i)
(since alignof(C) is a multiple of alignof(T_i),
since alignof(C) was calculated as the lcm of all of
the alignof(T...)) , then m_i will be located at an address divisible by
aligndof(T_i).

An alternate implementation might decide on:

alignof(C) = alignof(T_1)

and then adjust the offsets,
(off_2, off_3,...,off_n)
of:

(m_2, m_3,...m_n)

to assure that, for any memory location:

loc_C

such that loc_C%alignof(C) == 0,

(loc_C+off_i)%alignof(T_i) == 0

However, I've not tried figuring out how
to do that.

Maybe someone else could or explain why it's
worse than the first method?

-Larry
 
F

Francesco S. Carta

the "largest" alignment amounts to 32 bytes on my system

Scratch that. I thought it was so, but it was just a weird result of
some kind of template hack I've tried to implement on my own in order to
guess the actual alignment.

I still wonder if the method provided here can be trusted or not:

http://www.monkeyspeak.com/alignment/

Take a look at the following piece of code and its results on my setup
[MinGW 4.4.0, Windows7, AMD Athlon(tm) II Dual-Core M300 2.00 Ghz]

I wonder why "long double" gives a stride of 4 while "double" gives a
stride of 8 - where "stride" should stand for something like
"alignment", in the above linked page:

(maybe "long double" is stored as three floats or three longs in my
implementation?)

//-------
#include <iostream>

template <typename T>
struct Tchar {
T t;
char c;
};

#define strideof(T) \
((sizeof(Tchar<T>) > sizeof(T)) ? \
sizeof(Tchar<T>)-sizeof(T) : sizeof(T))

#define PRINT_DATA_ABOUT(T) \
std::cout << strideof(T) << ", " \
<< sizeof(T) << ", " << #T << std::endl

struct eight_doubles {
double data[8];
};

int main() {
std::cout << "stride, size, type" << std::endl;
PRINT_DATA_ABOUT(char);
PRINT_DATA_ABOUT(short);
PRINT_DATA_ABOUT(int);
PRINT_DATA_ABOUT(long);
PRINT_DATA_ABOUT(float);
PRINT_DATA_ABOUT(double);
PRINT_DATA_ABOUT(long double);
PRINT_DATA_ABOUT(eight_doubles);
}

/*
output:

stride, size, type
1, 1, char
2, 2, short
4, 4, int
4, 4, long
4, 4, float
8, 8, double
4, 12, long double
8, 64, eight_doubles

*/

//-------

Well, never mind. Just implementation details curiosities, I suppose.
 
F

Francesco S. Carta

Use the terminology of the standard ("new expression" and
"operator new function")!

Indeed I should, I just need more study to actually associate some
well-grasped content to those labels ;-)
<asides>
- The reason for the excitement and the consequent exclamation
point is that I do use it, and I feel lonely.

Of course you're not the only one who uses the standard terms. I
actually try to use them as long as I feel on firm ground.
- If you really want to refer to the operator (as opposed to
the expression) you have to think more.

This is not clear to me. Do you mean that I should think more if I
decide to add an "operator new" to some class? I actually tried, in the
past, when I was playing with some kind of memory pool allocator. I
realized, with the help of this group, that it was too an advanced task
for my knowledge back then (and the situation hasn't change much since
then).
</asides>

I seem to recall someone advocating the usage of "operator new"
for the function(s) and "new operator" for the operator but to
me... Perhaps it's crystal clear to a native speaker? (Anyway,
it may work in some contexts, especially when italics or
different fonts are used properly, and I've seen it used by
Dewhurst with extreme clarity. It's just not something to
advocate in general.)

I think one may also use "new allocation function", which is
unambiguous too.

That seems good, yes.

Thanks for your notes Gennaro.
 
F

Francesco S. Carta

Those of us who are into close reading call them the "new operator" and
"operator new", the second one being a library function that allocates
raw memory. The first one is a keyword, and the compiler generates code
to call operator new and construct an object at the returned address.
(We used to have a similar distinction between a "class template" and a
"template class", but that was too confusing; now they're "template
class" and "template instantiation", not necessarily in that order,
because the first two confuse me).

Yes, "template class" and "template instantiation" are quite less
confusing and more explicative, but now that I'm really thinking into
it, calling "new operator" the plain "new" used in the expressions makes
sense, as well calling "operator new" the library function and their
overrides (either the general or the per-class ones) makes sense as they
reflect their declarations... it's quite less confusing now.
If you're really into technicalities

I try to, not so successfully at times, but I try.
since a class can override
operator new, the actual code that the compiler generates for new T
typically calls T's constructor with a null pointer, and T's constructor
calls operator new. For objects that don't have dynamic storage duration
(that is, those with static, thread, or automatic storage duration in
C++0x) the compiler passes the address where the object will be located,
and the constructor simply uses that. Either way, the constructor
actually returns a value: the address of the newly constructed object.
But that's all implementation details.

Well, I like to know them.

Actually I'm not scared by the amount of details about such technical
subjects - the complexity of C++ has been lamented lots of times in lots
of places, but I never felt it as something I could not overcome with
some good effort - the point is just to understand these details in
order to keep them better impressed into my memory - after all, if I
able to speak several different natural languages without getting
confused (most of the time, at least) then C++ should fall within my
capabilities.

Thanks for your notes Pete.
 
L

Larry Evans

On 08/21/10 13:31, Francesco S. Carta wrote:
[snip]
I still wonder if the method provided here can be trusted or not:

http://www.monkeyspeak.com/alignment/

Take a look at the following piece of code and its results on my setup
[MinGW 4.4.0, Windows7, AMD Athlon(tm) II Dual-Core M300 2.00 Ghz]

I wonder why "long double" gives a stride of 4 while "double" gives a
stride of 8 - where "stride" should stand for something like
"alignment", in the above linked page:

(maybe "long double" is stored as three floats or three longs in my
implementation?)

//-------
#include <iostream>

template <typename T>
struct Tchar {
T t;
char c;
};

#define strideof(T) \
((sizeof(Tchar<T>) > sizeof(T)) ? \
sizeof(Tchar<T>)-sizeof(T) : sizeof(T))

I'm puzzled by the strideof(T) macro.
I would have thought sizeof(Tchar<T>) > size(T) would always be
true. OTOH, maybe the compiler knows that T has some padding at
the end where it puts the 'char c'. In that case,
the code I mentioned earlier:


http://svn.boost.org/svn/boost/sandbox/variadic_templates/boost/composite_storage/

might not calculate the same size as the compiler since
it doesn't know what the padding of any type may be :(

[snip]
 
F

Francesco S. Carta

On 08/21/10 13:31, Francesco S. Carta wrote:
[snip]
I still wonder if the method provided here can be trusted or not:

http://www.monkeyspeak.com/alignment/

Take a look at the following piece of code and its results on my setup
[MinGW 4.4.0, Windows7, AMD Athlon(tm) II Dual-Core M300 2.00 Ghz]

I wonder why "long double" gives a stride of 4 while "double" gives a
stride of 8 - where "stride" should stand for something like
"alignment", in the above linked page:

(maybe "long double" is stored as three floats or three longs in my
implementation?)

//-------
#include <iostream>

template <typename T>
struct Tchar {
T t;
char c;
};

#define strideof(T) \
((sizeof(Tchar<T>) > sizeof(T)) ? \
sizeof(Tchar<T>)-sizeof(T) : sizeof(T))

I'm puzzled by the strideof(T) macro.
I would have thought sizeof(Tchar<T>) > size(T) would always be
true. OTOH, maybe the compiler knows that T has some padding at
the end where it puts the 'char c'.

The author motivates the above macro exactly for that "padding reuse"
possibility, see the second paragraph:

[quote from http://www.monkeyspeak.com/alignment/ ]
There are two cases in which we might not get T's stride exactly. The
first is if the compiler (for some reason) chooses to pad the struct too
much. In that case, Tchar's size will be bumped up to some higher
multiple of T's stride. The result we get is not optimal, but it's still
guaranteed to be safe to use. Such excessive padding should probably be
considered a bug in the compiler, but our technique deals safely with
that case anyway.

The second case is if T already has some padding in it, and the compiler
chooses to reuse some of T's padding to store the extra char. In that
case, Tchar will be the same size as T, and our subtraction will yield
zero. When that happens, our macro defaults to sizeof(T), which is not
optimal but is at least known to be a multiple of T's stride. (There is
some dispute as to whether this case can arise--some believe that it is
illegal for a compiler to reuse padding like this. Either way, our
technique deals with it.)
[/quote]

(I reported it here for the sake of eventual further citation)
 
G

Gennaro Prota

Scratch that. I thought it was so, but it was just a weird result of
some kind of template hack I've tried to implement on my own in order to
guess the actual alignment.

I still wonder if the method provided here can be trusted or not:

http://www.monkeyspeak.com/alignment/

The article is not very precise. It doesn't exclude some types,
for instance (non-object types, etc.).

And when T is not a POD, there can be padding before t in

struct Foo { T t ; char c ; } ;

or even before c in

struct Foo { char c ; T t ; } ;

In the latter case, though, that's unlikely.

FWIW, I'm using a similar technique for "computing" a suitable
alignment down in my (experimental) version of fallible<> (the
non-experimental version has a T member and requires
default-construction, just like the Barton & Nackman one; the
new version drops the default-constructible requirement and uses
placement new---using, of course, a "suitably aligned" array of
unsigned chars).

Copy-pasting from there (beware: I have yet to review it---and
it started as a quick experiment):

template< typename T >
struct align_of
{
struct trick { // gps how to name it?
char c ;
T t ;
} ;

enum { v = sizeof( trick ) - sizeof( T ) } ;
enum { has_padding_at_end = v > sizeof( T ) } ; // should name
// this better

static std::size_t const value = has_padding_at_end
? sizeof( T )
: v
;
} ;

template< typename T >
std::size_t const
align_of< T >::value ;


When T is a POD the whole trick structure (which contains only a
T and a char) is a POD as well: thus there can't be padding
before the first member. (And I find it slightly clearer if the
char appears first.)

When T is not a POD, that padding is allowed. I see no reason
for the compiler to exploit that possibility with the struct
above. Anyway, if it is that "capricious" the whole thing breaks
down (numerical "coincidences" apart :)).

Of course, this template is not exposed: it's only used
internally, to try one after another the POD types in a
predefined "list", in order to find the first of them (if any)
that has the same alignment as the type T on which fallible<> is
instantiated (more precisely: for which align_of<>::value is the
same as align_of< T >::value). At the end of the dance, it
doesn't matter what the alignment really is, numerically: it
picks the POD and puts it in the same union that contains the
array of unsigned chars.

Another thing I found perplexing in the article was

template <typename T>
struct Tchar {
T t;
char c;
};

#define strideof(T) \
((sizeof(Tchar<T>) > sizeof(T)) ? \
sizeof(Tchar<T>)-sizeof(T) : sizeof(T))


How can ever be the size of the struct be smaller than, or even
equal to, sizeof( T )? [footnote]

(In theory, instead, the difference sizeof( Struct ) - sizeof(
T) may be greater than sizeof( T ). That's what the second
enumerator takes care of, though as noted in the comments I'm
not sure the name conveys the right thing.)

__

[footnote] I'm aware that in some cases an object may be
"smaller than its type" (virtual base classes etc.) but in this
case...
 
G

Gennaro Prota

On 21/08/2010 20.58, Francesco S. Carta wrote:
[...]
Indeed I should, I just need more study to actually associate some
well-grasped content to those labels ;-)


Of course you're not the only one who uses the standard terms. I
actually try to use them as long as I feel on firm ground.

Oh, but it wasn't addressed at you! And it was meant to be
humorous ("I feel lonely" :)). I just find that for these two
things using the standard terminology isn't very common.
This is not clear to me. Do you mean that I should think more if I
decide to add an "operator new" to some class?

No no, it was one of my "off at a tangent" thoughts. I was
thinking that the standard names work well because one of them
avoids the term "operator" altogether, referring to the
expression, instead. But *if* you need to talk about the
operator (the "lexical thing"), together with the operator
function, then things become more awkward. I meant: you'd have
to think more about how to avoid confusion.

Just to clarify:

3 + 2 additive "expression"
^--------- + operator

Similarly:

new T() new expression
^---------- new operator

An operator and an expression are two different things; it just
happens that in most cases we can "get away" by referring to the
expression (in fact, it usually makes more sense than referring
to the lexical notion, unless the topic of discussion is
compilers, etc.).

I hope I managed to explain what crossed my mind :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,734
Messages
2,569,441
Members
44,832
Latest member
GlennSmall

Latest Threads

Top