Em quarta-feira, 12 de outubro de 2016, às 21:26:25 CEST, Andrey Semashev
escreveu:
> Could you point me to a proposal or a place in the standard draft about
> this change?
It was discussed this week in the thread "Standard-layout class" on this
mailign list.
Since offsetof is a constant expression I would prefer the program to be
ill-formed if those conditions are violated. The current behavior is
undefined, which I guess is for compatibility with C. If compatibility
is considered too important, I guess, we could leave the behavior undefined.
On 10/12/16 23:46, Andrey Semashev wrote:
On 10/12/16 23:33, Thiago Macieira wrote:
Em quarta-feira, 12 de outubro de 2016, às 23:15:18 CEST, Andrey Semashev
escreveu:
Personally, I think offsetof() should be allowed for any non-reference
members or members' members... that are not within a virtual base
class. Whether you get undefined behavior when *using* that offset
is a
separate issue to me.
In practice, I think so as well. Even in the presence of virtual
functions I don't think any sane implementation would add hidden offest
fields like Thiago suggested. But I can accept this limitation.
Virtual inheritance implies that the class is not trivially copyable,
so we
can't use that criterion.
Although I would expect offsetof(B, b) for your example to give
meaningful results, I can accept that the implementation might do
something fancy just like when virtual functions are present. Having
offsetof support just trivially copyable types would already be a big
step forward.
Although on the other hand, presence of a user-defined copy constructor is not the proper reason to prohibit offsetof - the constructor does not affect binary layout at all.
If compatibility is considered too important, I guess, we could leave the behavior undefined.
--
--- You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+unsubscribe@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
On 10/13/16 02:23, Richard Smith wrote:
On Wed, Oct 12, 2016 at 2:11 PM, Andrey Semashev<andrey....@gmail.com <mailto:andrey.semashev@gmail.com>> wrote:
Although on the other hand, presence of a user-defined copy
constructor is not the proper reason to prohibit offsetof - the
constructor does not affect binary layout at all.
Can you provide standard wording justifying that claim? If a type is
neither standard layout nor trivially copyable, I cannot off-hand think
of any rule in the standard that requires the data members to even be
stored within the object. In some cases it might be reasonable for a C++
implementation to do interesting things with such types, such as putting
(some of) the members in a separate garbage-collected heap, or
automatically applying the pimpl idiom for ABI stability, or adding or
rearranging the members to support some kind of runtime instrumentation.
Off the top of my head I can remember the standard requires non-static data members to have increasing addresses within the class and the same access mode region. Strictly speaking, that does not preclude the compiler from doing things you describe but it surely adds some restrictions. (BTW: garbage collection is not allowed because the members have to be destroyed in a particular order on the object destruction.)
In that case I don't see how "conditionally-supported" differs from "implementation-specific" for non-standard-layout types. Imprecise wording perhaps?Since offsetof is a constant expression I would prefer the program
to be ill-formed if those conditions are violated. The current
behavior is undefined, which I guess is for compatibility with C.
That's not true. Use on non-standard-layout types is
conditionally-supported, which means:
1) An implementation must document which types are supported.
2) If a type is supported, the behavior must be as documented in the
standard -- you get the offset of the member.
3) If a type is not supported, the program is ill-formed.
Anyway, that doesn't really change my point. I think the standard should define offsetof for more types than it currently does.
On 13 Oct 2016 3:14 am, "Andrey Semashev" <andrey....@gmail.com> wrote:On 10/13/16 02:23, Richard Smith wrote:
On Wed, Oct 12, 2016 at 2:11 PM, Andrey Semashev<andrey....@gmail.com <mailto:andrey....@gmail.com>> wrote:
Although on the other hand, presence of a user-defined copy
constructor is not the proper reason to prohibit offsetof - the
constructor does not affect binary layout at all.
Can you provide standard wording justifying that claim? If a type is
neither standard layout nor trivially copyable, I cannot off-hand think
of any rule in the standard that requires the data members to even be
stored within the object. In some cases it might be reasonable for a C++
implementation to do interesting things with such types, such as putting
(some of) the members in a separate garbage-collected heap, or
automatically applying the pimpl idiom for ABI stability, or adding or
rearranging the members to support some kind of runtime instrumentation.
Off the top of my head I can remember the standard requires non-static data members to have increasing addresses within the class and the same access mode region. Strictly speaking, that does not preclude the compiler from doing things you describe but it surely adds some restrictions. (BTW: garbage collection is not allowed because the members have to be destroyed in a particular order on the object destruction.)The point of putting members on a GC heap is to allow them to be found as GC roots, not necessarily to allow them to be collected.
In that case I don't see how "conditionally-supported" differs from "implementation-specific" for non-standard-layout types. Imprecise wording perhaps?Since offsetof is a constant expression I would prefer the program
to be ill-formed if those conditions are violated. The current
behavior is undefined, which I guess is for compatibility with C.
That's not true. Use on non-standard-layout types is
conditionally-supported, which means:
1) An implementation must document which types are supported.
2) If a type is supported, the behavior must be as documented in the
standard -- you get the offset of the member.
3) If a type is not supported, the program is ill-formed.
Then you should look up the definition of "conditionally-supported" in the standard :)
If a program contains a violation of any diagnosable rule or an occurrence of a construct described in
this Standard as “conditionally-supported” when the implementation does not support that construct,
a conforming implementation shall issue at least one diagnostic message.
Anyway, that doesn't really change my point. I think the standard should define offsetof for more types than it currently does.I think it would make sense to also require it to be guaranteed to be available for trivially copyable types.
Relevant Standard quote:If a program contains a violation of any diagnosable rule or an occurrence of a construct described in
this Standard as “conditionally-supported” when the implementation does not support that construct,
a conforming implementation shall issue at least one diagnostic message.
Anyway, that doesn't really change my point. I think the standard should define offsetof for more types than it currently does.I think it would make sense to also require it to be guaranteed to be available for trivially copyable types.
Yes, but because members of reference type do not break being trivially-copyable, a statement about the behavior being undefined for reference types would be needed.
What do you think about this wording?
The macro offsetof(type, member-designator) accepts a restricted set of type arguments in this International Standard. Use of the offsetof macro with a type other than a standard-layout class or trivially copyable class (Clause 9) is conditionally-supported; if unsupported, the result is undefined. The expression offsetof(type, member-designator) is never type-dependent (14.6.2.2) and it is value-dependent (14.6.2.3) if and only if type is dependent. The result of applying the offsetof macro to a static data member or a function member, or a member-designator containing a member of reference type, is undefined.
Changes:
- Allows trivially-copyable types.
- If an implementation doesn't support some use case, the behavior is undefined. Some existing implementations will simply explode if you try offsetof on e.g. members of virtual base classes, rather than throw a compiler error. Others do throw an error.
- Since trivially-copyable types include classes with reference members, this notes that offsetof(type, referencemember) is undefined. Also, offsetof(type, nonreferencemember.arraymember[1].referencemember.otherclassmember).
Melissa
--
Changes:
- Allows trivially-copyable types.
struct A { int x; };
struct B : A { int y; };
struct C : A
{
C(const C&c) {...}
int y;
};
struct Meow
{
int a;
private:
// two different access-specifiers on data members
// means not standard-layout.
int b;
friend void Function();
};
void Function()
{
Meow x;
Meow y;
x.a = 4;
x.b = 0;
y.a = 2;
y.b = 0;
int *pxa = &x.a;
// Even x = y; would be the same
std::memcpy(&x, &y, sizeof(x));
// Why shouldn't this print 2?
std::printf("%d\n", *pxa);
}
Em sábado, 15 de outubro de 2016, às 10:35:21 PDT, Nicol Bolas escreveu:
> On Friday, October 14, 2016 at 4:00:42 PM UTC-4, Myriachan wrote:
> > Changes:
> > - Allows trivially-copyable types.
>
> ... why?
>
> The presence or absence of a user-defined copy constructor has nothing to
> do with the layout of a type. That's why standard layout doesn't care about
> constructors.
Correct, it doesn't define the layout, but the absence of a user-defined copy
constructor implies memcpy must be able to copy and duplicate the information
in the class. Since every byte in the class is addressable and contains useful
information or padding, then we can use offsetof.
Em domingo, 16 de outubro de 2016, às 12:06:41 PDT, Nicol Bolas escreveu:
> So while trivially copyable types do have fixed layout, they are not the
> only types which do. Basically, my point is that there are plenty of
> non-trivially copyable classes (which are not also standard layout) on
> which `offsetof` could still work. The example I gave being one of them.
Right, but we'll take the sufficient condition.
[...] What's interesting is that 5.7/4 originated [...] with DR 1504 [...]
void f(int* pb)
{
auto bAddress = reinterpret_cast<std::intptr_t>(pb); // not portable
int* pa = reinterpret_cast<int*>(bAddress - int(offsetof(X, b))); // not portable
// don't even need std::launder (I think?)
*pa = 333; // mwahaha!
}
No, I meant this is defined by the standard. In particular, the 5.7/4
paragraph you mentioned makes pointer arithmetic defined, when applied
on `unsigned char*` that points at the beginning of the storage used
to store the complete object.
struct S
{
int x, y;
};
S s;
unsigned char* p1 = reinterpret_cast< unsigned char* >(&s);
unsigned char* p2 = p1 + offsetof(S, y); // well-defined
How do you define "accessible through p"?
The reason the above works is because `p` points to an array. By contrast, the other code is not given a pointer to an array; it is given a pointer to an integer.
So where in the standard does it say that the address of `x.a` is "accessible through" `&x.b`?
[reinterpret.cast]
4. A pointer can be explicitly converted to any integral type large enough to hold it. The mapping function is implementation-defined. ...
5. A value of integral type or enumeration type can be explicitly converted to a pointer. A pointer converted to an integer of sufficient size (if any such exists on the implementation) and back to the same pointer type will have its original value; mappings between pointers and integers are otherwise implementation-defined.
Em quinta-feira, 27 de outubro de 2016, às 17:44:03 PDT, Thiago Macieira
escreveu:
> Em sexta-feira, 28 de outubro de 2016, às 02:30:17 PDT, Andrey Semashev
>
> escreveu:
> > I think the standard does not guarantee equivalence between pointer
> > arithmetics and integer arithmetics. The quoted paragraphs only
> > guarantee that you can cast the pointer to integer and back, but
> > nothing beyond that.
>
> Indeed it isn't.
>
> int *ptr = &array[0];
> uintptr_t u = uintptr_t(ptr);
> assert(u + 1 == uintptr_t(ptr + 1)); // fails!
And back to DOS days, if you compiled the following in the huge memory model
(so you have 32-bit-wide pointers and can access all real-mode memory):
char array[131072];
char *ptr = &array[0];
uintptr_t u = uintptr_t(ptr);
assert(u + 65536 == uintptr_t(ptr + 65536)); // fails!
ptr + 65536 is actually equal to u + 16777216.