Topic: computing offset of class member


Author: kanze@alex.gabi-soft.fr (James Kanze)
Date: Wed, 12 Mar 2003 20:49:22 +0000 (UTC)
Raw View
db@rtsffm.com (Dirk Bonekaemper) writes:

|>  The reason for my little test program was some legacy code that
|>  uses XtOffset(), a macro that is eventually defined as offsetof(),
|>  which eventually breaks down to the definition I gave in my
|>  program. Thats on Linux, but AIX and Solaris use the same
|>  definition. (The nice thing about headers, they're readable :-)
|>  Looking in the Web, all offsetof definitions I could find used the
|>  null ptr trick.

Are you sure about Linux.  The versions of g++ that I've seen do NOT
use the null ptr trick, but rather a special compiler built-in (which
seems to me the only intelligent solution, but that's just me).

|>  So, in the real world, there's a lot of "old and broken" code.
|>  (Sorry Marco, couldn't resist)

Do you mean in the macro offsetof?  All that the standard requires is
that the macro give the correct results; there is no requirement that
it be written in standard-conformant C.  The header is provided by the
compiler vendors.  If the expression in question gives the correct
results for their compiler, they can use it.  If they haven't
explicitly guaranteed that it will work, you can't.  When they deliver
an upgrade of the compiler, if the code doesn't work with the new
version, they will have changed the header.  If your code doesn't
work, because it contains such tricks, that's your problem, not
theirs.

--=20
James Kanze                           mailto:jkanze@caicheuvreux.com
Conseils en informatique orient=E9e objet/
                    Beratung in objektorientierter Datenverarbeitung
                                              Tel. +33 1 41 89 80 93

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: db@rtsffm.com (Dirk Bonekaemper)
Date: Fri, 28 Feb 2003 21:38:37 +0000 (UTC)
Raw View
Marco Manfredini wrote:

> Dirk Bonekaemper wrote:
>
> >
> > #include <iostream>
> >
> > #define OFFSET(TYPE, MEMBER) ((unsigned) &((TYPE *)0)->MEMBER)
>
> As it was said before, this dereferences "0". There can't be an object
> at 0 (by definition) and the compiler knows that. You can trick the
> compiler into accepting this (if you *have* to save *old and broken*
> code):
>
> #define OFFSET(TYPE, MEMBER) ((unsigned) &((TYPE *)4)->MEMBER+4)
>
> Though there aren't any objects at address 4, your macro will work as
> long you don't feed it with a TYPE which your compiler requires an
> instantiated object to determine its layout.

First of all, thanks to everybody who replied to my question. I will
for sure not use offsetoff with classes from now on.

But I'd like to make a comment:
The reason for my little test program was some legacy code that uses
XtOffset(), a macro that is eventually defined as offsetof(), which
eventually breaks down to the definition I gave in my program. Thats
on Linux, but AIX and Solaris use the same definition. (The nice thing
about headers, they're readable :-) Looking in the Web, all offsetof
definitions I could find used the null ptr trick.

So, in the real world, there's a lot of "old and broken" code.
(Sorry Marco, couldn't resist)

----
Dirk Bonekamper                                 EMail   db@rtsffm.com
RTS Realtime Systems AG                         Phone   +49 (0) 69 61009 330

Rembrandtstr. 13                                Fax     +49 (0) 69 61009 181

D-60596 Frankfurt (Germany)



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: db@rtsffm.com (Dirk Bonekaemper)
Date: Thu, 27 Feb 2003 15:36:41 +0000 (UTC)
Raw View
Hi everybody,

please consider the following simple program, that uses the old member
offset calculation form C times.

------------------------------------------------------------------------

#include <iostream>

#define OFFSET(TYPE, MEMBER) ((unsigned) &((TYPE *)0)->MEMBER)

struct A
{
    int a;
};

struct B
{
    int b;
};

struct C : public A, public B
{
    int c;
};

int main()
{
    std::cout << "a: " << OFFSET(C, a) << std::endl
              << "b: " << OFFSET(C, b) << std::endl
              << "c: " << OFFSET(C, c) << std::endl;
}
------------------------------------------------------------------------

Compiling with gcc 3.2.2 gives the following warnings:

> g++ -Wall offsetof.cxx
offsetof.cxx: In function `int main()':
offsetof.cxx:22: warning: invalid offsetof from non-POD type `struct A';
use
   pointer to member instead
offsetof.cxx:23: warning: invalid offsetof from non-POD type `struct B';
use
   pointer to member instead
offsetof.cxx:24: warning: invalid offsetof from non-POD type `struct C';
use
   pointer to member instead

Compiling with gcc 2.95 fails:

> g++ offsetof.cxx
offsetof.cxx: In function `int main()':
offsetof.cxx:22: invalid reference to NULL ptr, use ptr-to-member
instead
offsetof.cxx:23: invalid reference to NULL ptr, use ptr-to-member
instead

For comparison, I compiled the program on several other compilers
(VisulAge, Forte, VisualC++, Comeau Online). All compiled without
warnings.

So, here's the question:
Is gcc right about warning/rejecting the offset calculation?
Does the presence of virtual tables and/or multiple inheritance lead
  to undefined behaviour?
And, if that is the case, how to do it right?


Thanks for your input,
Dirk

----
Dirk Bonekamper                                 EMail   db@rtsffm.com
RTS Realtime Systems AG                         Phone   +49 (0) 69 61009
330
Rembrandtstr. 13                                Fax     +49 (0) 69 61009
181
D-60596 Frankfurt (Germany)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Hyman Rosen <hyrosen@mail.com>
Date: Thu, 27 Feb 2003 12:06:37 CST
Raw View
Dirk Bonekaemper wrote:
> Is gcc right about warning/rejecting the offset calculation?

Yes. The presence of '0->member' in the code means that you
are dereferencing a null pointer, whether or not you take the
address of the dereference. This makes for undefined behavior,

The solution for PODs is to use 'offsetof'.
There is no portable solution for non-PODs.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: poenitz@gmx.net (=?iso-8859-1?Q?Andr=E9_P=F6nitz?=)
Date: Thu, 27 Feb 2003 21:24:22 +0000 (UTC)
Raw View
Dirk Bonekaemper <db@rtsffm.com> wrote:
> Is gcc right about warning/rejecting the offset calculation?

Yes.

> Does the presence of virtual tables and/or multiple inheritance lead
>   to undefined behaviour?

Not only that, every type that's a non-POD is forbidden.

> And, if that is the case, how to do it right?

Use the offsetof macro that is provided by your implementation.
Works only for PODs. There is no way to get that functionality
portably for non-PODs.

Andre'

--
Those who desire to give up Freedom in order to gain Security,
will not have, nor do they deserve, either one. (T. Jefferson)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: marco@technoboredom.net (Marco Manfredini)
Date: Thu, 27 Feb 2003 21:24:41 +0000 (UTC)
Raw View
Dirk Bonekaemper wrote:

>
> #include <iostream>
>
> #define OFFSET(TYPE, MEMBER) ((unsigned) &((TYPE *)0)->MEMBER)

As it was said before, this dereferences "0". There can't be an object
at 0 (by definition) and the compiler knows that. You can trick the
compiler into accepting this (if you *have* to save *old and broken*
code):

#define OFFSET(TYPE, MEMBER) ((unsigned) &((TYPE *)4)->MEMBER+4)

Though there aren't any objects at address 4, your macro will work as
long you don't feed it with a TYPE which your compiler requires an
instantiated object to determine its layout. For example when compiled
with gcc your program wil segfault if you change the line

> struct C : public A, public B

to:

> struct C : public A, public virtual B

since now an instance of C has a hidden pointer to the real B instance,
a pointer which isn't there at address 4. Of course.

Other compilers (for example with a more secure or advanced object model
implementation) might break for your given example as well.

M.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]