Boost logo

Boost :

From: scleary_at_[hidden]
Date: 2000-01-20 09:16:09


> >Coincidentally, I have been trying to persuade Intel's
> >compiler's to pad only contextually, violating your first assumption.
>
> How would that be possible? Surely the standard mandates that sizeof(T)
is
> a compile time constant for all T, so context sensitive padding is not
> really possible.... unless maybe the compiler defined two sizes: a
minimum
> size and a padded size (for arrays), the latter would be sizeof(T) in all
> cases, but in situations like:
>
> struct A{
> B b;
> char c;
> };
>
> the character c could be stored "within" the trailing padding of instance
b
> (if there is any). Is this what you had in mind? I can imagine that this
> would break some existing code, but probably not any code that conformed
to
> the standard exactly (if there is any such beast :-) ).

> >So, because the assumption
> >that (char*)(&aa[i]) == (char*)(aa) + i * sizeof(a) is unfortunately
widespread,
> >we have to pad *all* occurrences of aa.

It's not just a widespread assumption. It's a guarantee of the language.
My reasons follow.

sizeof(T) is ". . .the number of bytes in an object of that class including
any padding required for placing objects of that type in an array. . ."
[5.3.3/2], and sizeof(char) is 1 [5.3.3/1].

Since arrays require a contiguous allocation of storage [8.3.4/1], and ". .
.the size of an array of n elements is n times the size of an element. . ."
[5.3.3/2], then
        T array[..]
        (char *)(&array[i]) == ((char *) aa) + i * sizeof(a)
should be a valid assumption for any type T and integral i.

John's got the right idea -- compilers only _have_ to pad objects if they
are part of an array. However, they might wish to always pad POD types to
allow memcpy, etc. [3.9/2].
        struct POD { char c; };
        void func()
        {
          assert(sizeof(POD) == 4); // let's say 4-byte alignment here
          char buf[sizeof(POD)]; // 4-byte buffer
          POD x; // x doesn't _have_ to be padded
          int y;
          memcpy(buf, &x, sizeof(POD)); // copy x into buffer -- OK
          memcpy(&x, buf, sizeof(POD)); // whoa -- unless the compiler is really
careful,
              // y just got (partially) overwritten, too!
        }

Now take the average end user, who takes a POD type and adds a simple
destructor. Via [9/4], it is no longer a POD type, and the above copying is
not valid, so the compiler is free to mess up however it wants. However,
the conservative approach for compilers is to always pad, so that user code
memcpy'ing non-POD types still functions as expected.

> >template <typename T>
> >struct empty_helper_t1 : public T { char i[sizeof(empty_known)]; };
> >struct empty_helper_t2 { char i[sizeof(empty_known)]; };
>
> I think that there may be situations when that would give the
> incorrect answer:
>
> Imagine that sizeof(empty_known) is one byte and one byte aligned (there's
> at least one platform - spark/gcc - that does this, I know this 'cos it
bit
> me!).
> Imagine that sizeof(empty_helper_t2 ) is 4 (ie padded out to alignment).
> In this case empty_helper_t1 could place the extra sizeof(T) bytes it
needs
> within the "padding": not expanding its size in relation to
empty_helper_t2
> even when T is not empty.
>
> Or then again maybe not :-)

I was assuming that alignment of a type is a function of the size of that
type; therefore, sizeof(empty_known) must equal sizeof(empty_helper_t2).

>
> Whatever I think that the assumption that the current version version
makes
> (that the platform alignment is a power of 2) is reasonable and likely to
> work in most cases, but by all means let's experiment with other
> implementations and see what breaks on what platforms, whatever we do, its
> likely to be susceptible to platform specifics.
>

> >And then there's the usual cache line alignment, at 16 and 32 bytes,
> >and soon to become 64 bytes.

But our current version will cut out at 256 bytes. That's the only issue I
have with it. I would like to, if possible, write self-maintaining code :),
so we don't have to re-visit this way down the line after we all forgot
about it :).

> However: for compressed pair do we need is_empty at all? If either of
the
> template parameters are a class, then why not derive from them regardless?
> It can't do any harm and it may just do some good.

No, we do not need is_empty for compressed_pair. Your logic is correct.

        -Steve


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk