`offsetof` in a macro-less, modular world

280 views
Skip to first unread message

Nicol Bolas

unread,
Mar 15, 2018, 12:12:36 PM3/15/18
to ISO C++ Standard - Future Proposals
The C standard library defines a large number of things that are macros. In the vast majority of cases, these are constants or functions, which can in C++ code be converted into `constexpr` variables or actual functions. For all intents and purposes, such constructs can be drop-in replacements for the C macros, which allows us to still work with them in a modular standard library that doesn't allow macros.

`offsetof` is a special case because it's not a function. Or at least, it's not a function that C++ at present allows you to write. Without reflection, there is no way to pass a typename or member variable as a function parameter. And while there are ways we can work around it, none of those ways would be drop-in replacements for `offsetof`; the user would have to change their code.

And even ignoring the non-drop-in part, there is currently no C++ replacement for `offsetof`. As modules moves forward, are there plans to resolve this issue and similar ones (beside the "let's get modules to export macros" plan)?

Hyman Rosen

unread,
Mar 15, 2018, 1:08:11 PM3/15/18
to std-pr...@isocpp.org

The Standard requires (in [headers]) that offsetof be a macro, so a library that bans macros perforce cannot use offsetof (or assert, setjmp, va_arg, va_end, and va_start).  If you want to change that, then make offsetof a built-in keyword, just like decltype and sizeof.

Also, what does it mean, "a modular standard library that doesn't use macros"?  A module can (or must, I suppose, for now) have an exported interface that does not provide macros to its users, but that does not prevent the code in the module from including header files and using macros as part of its implementation.

Nicolas Lesser

unread,
Mar 15, 2018, 1:21:19 PM3/15/18
to std-pr...@isocpp.org
Not that I know of. This was mentioned in passing while discussing P0908, where it was suggested to make it an operator.

EWG then went back and forth a bit, as making offsetof an operator would mean incompatibilities with POSIX headers but better for modules.

You might want to write a proposal for this.

There is the Atom proposal which proposes to have a "module header" which can also solve this issue in a relatively clean way (P0947).

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/c5f5ccc4-3267-442d-8cb2-de1ae103a38e%40isocpp.org.

inkwizyt...@gmail.com

unread,
Mar 15, 2018, 2:09:44 PM3/15/18
to ISO C++ Standard - Future Proposals
I think we should leave this macro alone and add new operator with different name that will be available everywhere.
And then optionally define `offsetof ` with use this new operator.

Gašper Ažman

unread,
Mar 15, 2018, 2:11:02 PM3/15/18
to std-pr...@isocpp.org
ooo offsetof that works on members of virtual base classes :)

To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Hyman Rosen

unread,
Mar 15, 2018, 2:38:19 PM3/15/18
to std-pr...@isocpp.org
On Thu, Mar 15, 2018 at 2:09 PM, <inkwizyt...@gmail.com> wrote:
I think we should leave this macro alone and add new operator with different name that will be available everywhere.
And then optionally define `offsetof ` with use this new operator.

Some compilers do that already.
gcc and clang turn offsetof(a,b) into __builtin_offsetof(a, b).
IBM's xlC turns it into __offsetof(a,b).

Dilip Ranganathan

unread,
Mar 15, 2018, 3:26:25 PM3/15/18
to std-pr...@isocpp.org
So does MSVC -- although it seems to protect the __builtin_offsetof(a, b) version behind another 
macro of the form  _CRT_USE_BUILTIN_OFFSETOF that is disabled by default.
Doesn't the regular offsetof do something weird if the passed in type implements operator&
and returns something evil?


Thiago Macieira

unread,
Mar 15, 2018, 3:41:49 PM3/15/18
to std-pr...@isocpp.org
On Thursday, 15 March 2018 10:21:05 PDT Nicolas Lesser wrote:
> Not that I know of. This was mentioned in passing while discussing P0908,
> where it was suggested to make it an operator.

I'd bring back the idea of using pointers to members as a complete replacement
for offsetof.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center



Hyman Rosen

unread,
Mar 15, 2018, 3:52:09 PM3/15/18
to std-pr...@isocpp.org
On Thu, Mar 15, 2018 at 3:26 PM, Dilip Ranganathan <misc....@gmail.com> wrote:
Doesn't the regular offsetof do something weird if the passed in type implements operator&
and returns something evil?

A naive definition of offsetof(Class, Member) is ((std::size_t)(&((Class*)0)->Member)),
which won't work if Member has an operator&.

Andrey Semashev

unread,
Mar 15, 2018, 4:53:59 PM3/15/18
to std-pr...@isocpp.org
On 03/15/18 22:41, Thiago Macieira wrote:
> On Thursday, 15 March 2018 10:21:05 PDT Nicolas Lesser wrote:
>> Not that I know of. This was mentioned in passing while discussing P0908,
>> where it was suggested to make it an operator.
>
> I'd bring back the idea of using pointers to members as a complete replacement
> for offsetof.

P0908 suggested something along these lines and it was rejected.

Thiago Macieira

unread,
Mar 15, 2018, 7:12:48 PM3/15/18
to std-pr...@isocpp.org
That's not what I meant.

I meant not using offsetof at all. All uses of offsetof can be replaced with a
pointer to member or pointer to container.

Andrey Semashev

unread,
Mar 15, 2018, 7:37:09 PM3/15/18
to std-pr...@isocpp.org
On 03/16/18 02:12, Thiago Macieira wrote:
> On Thursday, 15 March 2018 13:53:54 PDT Andrey Semashev wrote:
>> On 03/15/18 22:41, Thiago Macieira wrote:
>>> On Thursday, 15 March 2018 10:21:05 PDT Nicolas Lesser wrote:
>>>> Not that I know of. This was mentioned in passing while discussing P0908,
>>>> where it was suggested to make it an operator.
>>>
>>> I'd bring back the idea of using pointers to members as a complete
>>> replacement for offsetof.
>>
>> P0908 suggested something along these lines and it was rejected.
>
> That's not what I meant.
>
> I meant not using offsetof at all. All uses of offsetof can be replaced with a
> pointer to member or pointer to container.

Not sure what you mean by pointer to container.

You cannot emulate this use case with a pointer to member:

struct A
{
int x[10];
};

offsetof(A, x[5]);

Also, it is much more difficult to obtain an offset to a nested member
without offsetof:

struct B
{
A a;
};

offsetof(B, a.x[5]);

Don't get me wrong, being able to extract offset information from a
pointer to member would be useful, but it wouldn't be an equivalent
replacement of offsetof.

Thiago Macieira

unread,
Mar 15, 2018, 8:05:41 PM3/15/18
to std-pr...@isocpp.org
On Thursday, 15 March 2018 16:37:04 PDT Andrey Semashev wrote:
> > I meant not using offsetof at all. All uses of offsetof can be replaced
> > with a pointer to member or pointer to container.
>
> Not sure what you mean by pointer to container.

The inverse/dual of a pointer to member. Instead of pointing from the top of
the structure to a member, it reverses and points to the top of the structure.

> You cannot emulate this use case with a pointer to member:
>
> struct A
> {
> int x[10];
> };
>
> offsetof(A, x[5]);

Why not? We can if we give ourselves the syntax for it.

> Also, it is much more difficult to obtain an offset to a nested member
> without offsetof:
>
> struct B
> {
> A a;
> };
>
> offsetof(B, a.x[5]);

No, it isn't.

int A:: *int_in_A = &A::y; // just so we don't discuss arrays just now
A B:: *A_in_B = &B::a;
int B:: *int_in_B = int_in_A + A_in_B; // syntax TBD

> Don't get me wrong, being able to extract offset information from a
> pointer to member would be useful, but it wouldn't be an equivalent
> replacement of offsetof.

It would be if we make it.

Andrey Semashev

unread,
Mar 15, 2018, 9:05:56 PM3/15/18
to std-pr...@isocpp.org
On 03/16/18 03:05, Thiago Macieira wrote:
> On Thursday, 15 March 2018 16:37:04 PDT Andrey Semashev wrote:
>>> I meant not using offsetof at all. All uses of offsetof can be replaced
>>> with a pointer to member or pointer to container.
>>
>> Not sure what you mean by pointer to container.
>
> The inverse/dual of a pointer to member. Instead of pointing from the top of
> the structure to a member, it reverses and points to the top of the structure.
>
>> You cannot emulate this use case with a pointer to member:
>>
>> struct A
>> {
>> int x[10];
>> };
>>
>> offsetof(A, x[5]);
>
> Why not? We can if we give ourselves the syntax for it.

Well, we don't have one now, that's why you can't do that. We need at
least a proposal for this new syntax to have a meaningful comparison
with offsetof.

>> Also, it is much more difficult to obtain an offset to a nested member
>> without offsetof:
>>
>> struct B
>> {
>> A a;
>> };
>>
>> offsetof(B, a.x[5]);
>
> No, it isn't.
>
> int A:: *int_in_A = &A::y; // just so we don't discuss arrays just now
> A B:: *A_in_B = &B::a;
> int B:: *int_in_B = int_in_A + A_in_B; // syntax TBD

Right, that illustrates what I called "much more difficult". offsetof is
pretty difficult to beat in code conciseness and clarity.

Thiago Macieira

unread,
Mar 15, 2018, 11:38:32 PM3/15/18
to std-pr...@isocpp.org
On Thursday, 15 March 2018 18:05:52 PDT Andrey Semashev wrote:
> > Why not? We can if we give ourselves the syntax for it.
>
> Well, we don't have one now, that's why you can't do that. We need at
> least a proposal for this new syntax to have a meaningful comparison
> with offsetof.

Right. I'm just saying that we should use pointer-to-members instead, since
they would also give us type safety, something that offsetof can't do.

> > int A:: *int_in_A = &A::y; // just so we don't discuss arrays just now
> > A B:: *A_in_B = &B::a;
> > int B:: *int_in_B = int_in_A + A_in_B; // syntax TBD
>
> Right, that illustrates what I called "much more difficult". offsetof is
> pretty difficult to beat in code conciseness and clarity.

But you get an offset, which breaks a lot of assumptions in the type system.
It's not really easy to use.

Nicol Bolas

unread,
Mar 15, 2018, 11:55:49 PM3/15/18
to ISO C++ Standard - Future Proposals
On Thursday, March 15, 2018 at 2:09:44 PM UTC-4, Marcin Jaczewski wrote:
I think we should leave this macro alone and add new operator with different name that will be available everywhere.
And then optionally define `offsetof ` with use this new operator.

The problem I'm talking about here is that if you have a `std.cstddef` module, without the ability to export macros, it cannot export anything which is compatible with `offsetof` usage. So even if we make an operator for this or empower member pointers or whatever, the existence of such a feature won't allow you to switch from `#include <cstddef>` to `import std.cstddef` without changing your code.

Hyman Rosen

unread,
Mar 16, 2018, 10:54:35 AM3/16/18
to std-pr...@isocpp.org
On Thu, Mar 15, 2018 at 11:55 PM, Nicol Bolas <jmck...@gmail.com> wrote:
The problem I'm talking about here is that if you have a `std.cstddef` module, without the ability to export macros, it cannot export anything which is compatible with `offsetof` usage. So even if we make an operator for this or empower member pointers or whatever, the existence of such a feature won't allow you to switch from `#include <cstddef>` to `import std.cstddef` without changing your code.

You just need to simultaneously modify C++ so that there is a built-in equivalent
to offsetof, and make offsetof a pre-defined macro that expands to that.  Why is
that difficult?

Are you imagining that modular C++ isn't going to have a preprocessor at all?
Users of __FILE__, __LINE__, __PRETTY_FUNCTION__, and __COUNTER__
may want to have a word with you.

Hyman Rosen

unread,
Mar 16, 2018, 11:13:44 AM3/16/18
to std-pr...@isocpp.org
On Thu, Mar 15, 2018 at 8:05 PM, Thiago Macieira <thi...@macieira.org> wrote:
        int A:: *int_in_A = &A::y;      // just so we don't discuss arrays just now
        A B:: *A_in_B = &B::a;
        int B:: *int_in_B = int_in_A + A_in_B;  // syntax TBD

Rather than inventing a new syntax, the obvious solution is to allow the creation of
member pointers using a syntax that allows C designators to be used, instead of
just qualified-ids.  The code above would then just be

    struct A { int x[10]; };
    struct B { A a; };
    int B::*number_5 = &B::a.x[5];

Nicol Bolas

unread,
Mar 16, 2018, 11:16:45 AM3/16/18
to ISO C++ Standard - Future Proposals


On Friday, March 16, 2018 at 10:54:35 AM UTC-4, Hyman Rosen wrote:
On Thu, Mar 15, 2018 at 11:55 PM, Nicol Bolas <jmck...@gmail.com> wrote:
The problem I'm talking about here is that if you have a `std.cstddef` module, without the ability to export macros, it cannot export anything which is compatible with `offsetof` usage. So even if we make an operator for this or empower member pointers or whatever, the existence of such a feature won't allow you to switch from `#include <cstddef>` to `import std.cstddef` without changing your code.

You just need to simultaneously modify C++ so that there is a built-in equivalent
to offsetof, and make offsetof a pre-defined macro that expands to that.

And if modules can't export macros, how will `import std.cstddef` give me access to that module?

Hyman Rosen

unread,
Mar 16, 2018, 11:34:02 AM3/16/18
to std-pr...@isocpp.org
On Fri, Mar 16, 2018 at 11:16 AM, Nicol Bolas <jmck...@gmail.com> wrote:
You just need to simultaneously modify C++ so that there is a built-in equivalent
to offsetof, and make offsetof a pre-defined macro that expands to that.

And if modules can't export macros, how will `import std.cstddef` give me access to that module?

What module?  I don't understand.

Let me try again:
  • Change C++ so that there is a built-in version of offsetof under a different name, say __offsetof.
  • __offsetof is available for use in all C++ code without any header or module needed, just as sizeof is.
  • Also change C++ so that offsetof is a pre-defined macro that expands to __offsetof.
  • As a pre-defined macro, offsetof is available without any header or module needed, just as __LINE__ is.
OK?

Thiago Macieira

unread,
Mar 16, 2018, 11:52:40 AM3/16/18
to std-pr...@isocpp.org
On Friday, 16 March 2018 08:13:21 PDT Hyman Rosen wrote:
> Rather than inventing a new syntax, the obvious solution is to allow the
> creation of
> member pointers using a syntax that allows C designators to be used,
> instead of
> just *qualified-id*s. The code above would then just be
>
> struct A { int x[10]; };
> struct B { A a; };
> int B::*number_5 = &B::a.x[5];

I think I still want the syntax for adding, subtracting and negating pointer
to members.

Gašper Ažman

unread,
Mar 16, 2018, 11:55:15 AM3/16/18
to std-pr...@isocpp.org
Yeah, me too :).

There's all kinds of caveats with that though, so I'm anticipating some resistance.

G

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Nicol Bolas

unread,
Mar 16, 2018, 1:23:12 PM3/16/18
to ISO C++ Standard - Future Proposals
On Friday, March 16, 2018 at 11:34:02 AM UTC-4, Hyman Rosen wrote:
On Fri, Mar 16, 2018 at 11:16 AM, Nicol Bolas <jmck...@gmail.com> wrote:
You just need to simultaneously modify C++ so that there is a built-in equivalent
to offsetof, and make offsetof a pre-defined macro that expands to that.

And if modules can't export macros, how will `import std.cstddef` give me access to that module?

What module?  I don't understand.

Let me try again:
  • Change C++ so that there is a built-in version of offsetof under a different name, say __offsetof.
  • __offsetof is available for use in all C++ code without any header or module needed, just as sizeof is.
  • Also change C++ so that offsetof is a pre-defined macro that expands to __offsetof.

Oh, when you said "built-in" you meant like "supplied by the compiler".

So... what happens with people's code that don't include <cstddef> or <stddef> that happens to use the `offsetof` identifier? Admittedly this is a minor issue, as I rather doubt someone's going to make a class or function that conflicts with a fairly widely known C macro that gets included by quite a few things (even if only because of other useful things).

Hyman Rosen

unread,
Mar 16, 2018, 1:52:45 PM3/16/18
to std-pr...@isocpp.org
On Fri, Mar 16, 2018 at 1:23 PM, Nicol Bolas <jmck...@gmail.com> wrote:
So... what happens with people's code that don't include <cstddef> or <stddef> that happens to use the `offsetof` identifier?

It stops working, just like code that uses concept and requires.

Myriachan

unread,
Mar 16, 2018, 2:56:29 PM3/16/18
to ISO C++ Standard - Future Proposals
On Friday, March 16, 2018 at 8:13:44 AM UTC-7, Hyman Rosen wrote:

    struct A { int x[10]; };
    struct B { A a; };
    int B::*number_5 = &B::a.x[5];

Careful here: this is a breaking change.  Consider this currently-legal code:

struct W { int x[10]; };
struct X { W w; };
struct Y { W w; };
struct Z : X, Y
{
   
void f();
};

void Z::f()
{
   
int *p = &Y::w.x[5];
   
*p = 4;
}

You'd need to use parentheses and define operators . and [] on member operators in order to solve this:

(&B::a).x[5]

I really like Thiago's proposal regarding data member pointers, but it has a major problem: it's an ABI break.  Some proposed constructions would allow data member pointers with a negative offset (pointing to the containing class's members), and both the Microsoft and Itanium ABIs encode null member pointers as -1.

Melissa

Hyman Rosen

unread,
Mar 16, 2018, 3:25:51 PM3/16/18
to std-pr...@isocpp.org
On Fri, Mar 16, 2018 at 2:56 PM, Myriachan <myri...@gmail.com> wrote:
On Friday, March 16, 2018 at 8:13:44 AM UTC-7, Hyman Rosen wrote:

    struct A { int x[10]; };
    struct B { A a; };
    int B::*number_5 = &B::a.x[5];

Careful here: this is a breaking change.  Consider this currently-legal code:

struct W { int x[10]; };
struct X { W w; };
struct Y { W w; };
struct Z : X, Y
{
   
void f();
};

void Z::f()
{
   
int *p = &Y::w.x[5];
   
*p = 4;
}


I swear, C++ drives me more crazy every time I look at it.  Notice the obvious corollary to your code:

    void Z::f()
    {
        int *p = &(Y::w.x[5]);  // OK
        int *q = & Y::w.x[5] ;  // OK

        W   *r = &(Y::w)     ;  // OK
        W   *s = & Y::w      ;  // Error
    }

C++ syntax is a horrible hodgepodge mess caused by too much "it would be nice if this meant that"
without any consideration of making everything globally consistent.

Myriachan

unread,
Mar 16, 2018, 4:02:09 PM3/16/18
to ISO C++ Standard - Future Proposals
On Friday, March 16, 2018 at 12:25:51 PM UTC-7, Hyman Rosen wrote:
On Fri, Mar 16, 2018 at 2:56 PM, Myriachan <myri...@gmail.com> wrote:
On Friday, March 16, 2018 at 8:13:44 AM UTC-7, Hyman Rosen wrote:

    struct A { int x[10]; };
    struct B { A a; };
    int B::*number_5 = &B::a.x[5];

Careful here: this is a breaking change.  Consider this currently-legal code:

struct W { int x[10]; };
struct X { W w; };
struct Y { W w; };
struct Z : X, Y
{
   
void f();
};

void Z::f()
{
   
int *p = &Y::w.x[5];
   
*p = 4;
}


I swear, C++ drives me more crazy every time I look at it.  Notice the obvious corollary to your code:

    void Z::f()
    {
        int *p = &(Y::w.x[5]);  // OK
        int *q = & Y::w.x[5] ;  // OK

        W   *r = &(Y::w)     ;  // OK
        W   *s = & Y::w      ;  // Error
    }


Yes.  It's explicitly stated as such in [expr.unary.op]/4: "A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed in parentheses."
 
C++ syntax is a horrible hodgepodge mess caused by too much "it would be nice if this meant that"
without any consideration of making everything globally consistent.

Regarding the grammar of member pointers, I agree >.<

Thiago Macieira

unread,
Mar 16, 2018, 5:04:13 PM3/16/18
to std-pr...@isocpp.org
On Friday, 16 March 2018 11:56:29 PDT Myriachan wrote:
> I really like Thiago's proposal regarding data member pointers, but it has
> a major problem: it's an ABI break. Some proposed constructions would
> allow data member pointers with a negative offset (pointing to the
> containing class's members), and both the Microsoft and Itanium ABIs encode
> null member pointers as -1.

Not really. Negative offsets are already possible in some pathological
pointer-to-members (don't ask me which ones). The ABI has to have a way to
represent them in a way that is different from a null pointer.

inkwizyt...@gmail.com

unread,
Mar 16, 2018, 6:02:16 PM3/16/18
to ISO C++ Standard - Future Proposals

People who use  `<cstddef>` will still use `<cstddef>` we don not change antything (this why I suggest leaving it alone).
We add new keyword `real_super_offsetof` that will be available everywhere.
Only problem would be migration of heder to modules usage but because offsetof is macro we can safely use text replace to change it to new keyword.

Nicol Bolas

unread,
Mar 16, 2018, 6:12:24 PM3/16/18
to ISO C++ Standard - Future Proposals

If it's going to be a direct, drop-in replacement, such that code which uses `offsetof` can be changed to use `new_offsetof` with no other changes... why make it a new identifier at all? Just have `offsetof` become a C++ keyword, and have the C++ versions of `<cstddef>` and `stddef.h` simply not define them.

I thought the whole point of using a new keyword was to give them a (presumably) better interface.

Thiago Macieira

unread,
Mar 17, 2018, 12:32:36 AM3/17/18
to std-pr...@isocpp.org
On Friday, 16 March 2018 15:12:24 PDT Nicol Bolas wrote:
> If it's going to be a direct, drop-in replacement, such that code which
> uses `offsetof` can be changed to use `new_offsetof` with no other
> changes... why make it a new identifier at all? Just have `offsetof` become
> a C++ keyword, and have the C++ versions of `<cstddef>` and `stddef.h`
> simply not define them.
>
> I thought the whole point of using a new keyword was to give them a
> (presumably) better interface.

Agreed, if we're going to go to the trouble of creating a new keyword, then we
ought to make sure it is better than offsetof. You know I dislike the type-
unsafe aspect of that. Another is the fact that its use could be UB, as it
adds pointers that shouldn't really be added.

The first step would be to make a (quick) survey of C++ uses of offsetof --
that is, things that aren't just regular C code in C++ mode (think of Linux
kernel's struct list[1]).

[1] https://code.woboq.org/linux/linux/tools/firewire/list.h.html

Jakob Riedle

unread,
Mar 17, 2018, 6:11:11 AM3/17/18
to ISO C++ Standard - Future Proposals


Am Samstag, 17. März 2018 05:32:36 UTC+1 schrieb Thiago Macieira:
The first step would be to make a (quick) survey of C++ uses of offsetof --
that is, things that aren't just regular C code in C++ mode (think of Linux
kernel's struct list[1]).

Hi Thiago,

as a first contribution to this survey :-), I recently used it to determine, whether two attributes (used in two different memory layout modes of a class) shaddow each other.
Here is the link to the spot (Line 667):

I found this macro very useful in this spot and I'd be fine to have it replaced by a built-in feature.

Hope this helps,
Jakob

Thiago Macieira

unread,
Mar 17, 2018, 1:34:07 PM3/17/18
to std-pr...@isocpp.org
On Saturday, 17 March 2018 03:11:11 PDT Jakob Riedle wrote:
> as a first contribution to this survey :-), I recently used it to
> determine, whether two attributes (used in two different memory layout
> modes of a class) shaddow each other.
> Here is the link to the spot (Line 667):
> https://github.com/DuffsDevice/tinyutf8/blob/master/include/tinyutf8.h

I think I understand what your optimisation is trying to do here: support the
case where struct NON_SSO has tail padding, in which case the last byte in
struct SSO aliases no members in NON_SSO. Is that it?

If so, the code is dead in all modern platforms, since sizeof(size_t) ==
sizeof(void *) and ditto for alignof. So NON_SSO will never have tail padding
and it will be very hard for you to test this anywhere. Is this an
optimisation you really want to keep in your code?

Though I don't see any other way to test for padding at a specific position
aside from offsetof. That seems to be a legitimate use, though fragile and
dangerous.

I also think that your code in that condition is UB. You can't use t_sso and
t_non_sso at the same time -- only one member of a union can be active at a
time. Since this function is supposed to disable SSO, you must leave the
t_non_sso member of the union active at the end of your function, but the last
one it would have used is t_sso.

Your last_byte<T> type may be suffering from the same problem.

Richard Smith

unread,
Mar 18, 2018, 11:15:27 AM3/18/18
to std-pr...@isocpp.org
Looks like people in this thread have discussed pointers-to-members, but I didn't see anyone bring up what I think is the best option to replace offsetof (apologies if I missed it): a stdlib function that converts a pointer-to-data-member into an offset.

(We'd need two other things for this to work well: the pointer-to-member composition syntax and/or Hyman's generalized memptr formation syntax, plus fixing the bug that &B::x can be of type "pointer to A member" where A is a base class of B. But both of those seem good in their own right.)

On 15 Mar 2018 12:12, "Nicol Bolas" <jmck...@gmail.com> wrote:
The C standard library defines a large number of things that are macros. In the vast majority of cases, these are constants or functions, which can in C++ code be converted into `constexpr` variables or actual functions. For all intents and purposes, such constructs can be drop-in replacements for the C macros, which allows us to still work with them in a modular standard library that doesn't allow macros.

`offsetof` is a special case because it's not a function. Or at least, it's not a function that C++ at present allows you to write. Without reflection, there is no way to pass a typename or member variable as a function parameter. And while there are ways we can work around it, none of those ways would be drop-in replacements for `offsetof`; the user would have to change their code.

And even ignoring the non-drop-in part, there is currently no C++ replacement for `offsetof`. As modules moves forward, are there plans to resolve this issue and similar ones (beside the "let's get modules to export macros" plan)?

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Thiago Macieira

unread,
Mar 18, 2018, 12:19:53 PM3/18/18
to std-pr...@isocpp.org
On Sunday, 18 March 2018 08:15:20 PDT Richard Smith wrote:
> Looks like people in this thread have discussed pointers-to-members, but I
> didn't see anyone bring up what I think is the best option to replace
> offsetof (apologies if I missed it): a stdlib function that converts a
> pointer-to-data-member into an offset.

That's a good idea, but it would lead to no better code than what we have
right now.

I'd still like to see more use-cases supported by the pointer to members,
composing them, inverting them (pointer to container), etc., so that we retain
type safety.

Miguel Ojeda

unread,
Mar 18, 2018, 7:29:34 PM3/18/18
to std-pr...@isocpp.org
On Thu, Mar 15, 2018 at 5:12 PM, Nicol Bolas <jmck...@gmail.com> wrote:
> `offsetof` is a special case because it's not a function. Or at least, it's
> not a function that C++ at present allows you to write. Without reflection,

container_of() would be very nice to get as well.

Cheers,
Miguel

Richard Smith

unread,
Mar 18, 2018, 9:30:37 PM3/18/18
to std-pr...@isocpp.org
On 18 Mar 2018 09:19, "Thiago Macieira" <thi...@macieira.org> wrote:
On Sunday, 18 March 2018 08:15:20 PDT Richard Smith wrote:
> Looks like people in this thread have discussed pointers-to-members, but I
> didn't see anyone bring up what I think is the best option to replace
> offsetof (apologies if I missed it): a stdlib function that converts a
> pointer-to-data-member into an offset.

That's a good idea, but it would lead to no better code than what we have
right now.

I'd still like to see more use-cases supported by the pointer to members,
composing them, inverting them (pointer to container), etc., so that we retain
type safety.

I agree, and I think that's a better place to focus our efforts. But if we need it, I think we do have good solutions for offsetof without macros.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
   Software Architect - Intel Open Source Technology Center



--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Jakob Riedle

unread,
Mar 19, 2018, 6:11:10 PM3/19/18
to ISO C++ Standard - Future Proposals
Hi Thiago,

Am Samstag, 17. März 2018 18:34:07 UTC+1 schrieb Thiago Macieira:
I think I understand what your optimisation is trying to do here: support the
case where struct NON_SSO has tail padding, in which case the last byte in
struct SSO aliases no members in NON_SSO. Is that it?
Yes, exactly.
 
If so, the code is dead in all modern platforms, since sizeof(size_t) ==
sizeof(void *) and ditto for alignof. So NON_SSO will never have tail padding
and it will be very hard for you to test this anywhere. Is this an
optimisation you really want to keep in your code?
I haven't thought about it this way. And you're right, that it is in most cases dead code.
Concerning testing: I've already tested it by inserting a 'char' field in between members.

Though I don't see any other way to test for padding at a specific position
aside from offsetof. That seems to be a legitimate use, though fragile and
dangerous.
I don't even consider it fragile. It's robust and platform independent. Do I misunderstand something?
 
I also think that your code in that condition is UB. You can't use t_sso and
t_non_sso at the same time -- only one member of a union can be active at a
time. Since this function is supposed to disable SSO, you must leave the
t_non_sso member of the union active at the end of your function, but the last
one it would have used is t_sso.
You're right. However, my assumption is that accessing t_non_sso and t_sso is no different than invoking reinterpret_cast<NON_SSO|SSO*>(this),
which I assume to work on every compiler and every platform, even though it is UB as well.
The memory model of C++ is just not (yet) able to guarantee this very obvious behaviour.
 
Your last_byte<T> type may be suffering from the same problem.
Right :)

Cheers,
Jakob
 
Reply all
Reply to author
Forward
0 new messages