C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

C++ offsetof hack

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Guest






PostPosted: Fri May 11, 2007 4:40 am    Post subject: C++ offsetof hack Reply with quote



Hey all. I would like some comments on another C++ "hack" that I have
employed in some work I have done (http://www.ischo.com/xrtti). I had
to employ this hack to "work around a limitation of the C++ language.
I realize that I am doing something "dangerous" that is not permitted
by the language specification. However, I have a hard time thinking
of how a compiler could make what I am doing *not work*. Here is the
hack:

Using offsetof() in C++ is only defined to work for "POD" types (plain
old data - i.e. simple C structs or C++ classes with no virtual
methods). But I need offsetof to work for C++ classes also, so I came
up with the following hack:

void DummyFunction();

#define KnownAddress ((char *) ::DummyFunction)

#define cpp_offsetof(type, member) \
(((char *) &((type *) KnownAddress)->member) - KnownAddress)

What I'm doing here is taking an address known to be in the addres
space of the program (although I don't think this matters at runtime
but I found that the compiler I was working with - g++ - gave me
errors if I chose arbitrary addresses like 0 or 1) and casting it to a
pointer to the type in question, then addressing the member in
question from this address, then taking the address of *that* (thus
yielding a pointer to the member as it would be addressed within a
class of the given type, if the class of the given type were located
at the same address as KnownAddress), then casting that to char * (so
that the pointer math then works correctly) and subtracting the known
address back out again.

The net result is that I am left with just the difference between a
pointer to the type in question, and a pointer to the member in
question within that type. This is the "offsetof" that I am
interested in.

This is similar I believe to standard C implementations of offsetof,
which might look like this:

#define offsetof(type, member) \
(((char *) &((type *) 0)->member) - 0)

The only difference being, that I am using an address that the
compiler doesn't issue an error for (whereas with g++ I found that
trying to cast 0 to a pointer to a class gave an error, and the same
for other arbitrary integers I chose - 1, 4096, etc).

I find that this works perfectly in g++ but I haven't tried other
compilers.

I realize that I am violating some of the rules of C++ - I have read
that you are specifically disallowed from trying to get the offset of
a class member. Presumably this is because the C++ standard wants to
leave the door open for compiler implementers to do really weird
things with class layout, so that offsetof cannot work at compile time
but could only work at runtime.

The thing is, I have a hard time thinking of when a C++ compiler
writer would do something like make member variable calculations
require runtime code. I would always expect members of a C++ class to
be at a fixed, known offset from a pointer to that class, and that the
compiler would simply issue simple memory address addition
instructions to get the address of member variables of classes.

First - does anyone have any comments on this hack? Is it crazy to
think that it will work on all compilers? Can anyone think of a
reasonable way that C++ compilers might not be able to give a fixed
constant for the offset from a class to a member of the class, at
compile time, that would work for all instances of that class?

Second - if anyone wants to find out if my cpp_offsetof macro works
for their compiler, you can try this program:

------------------------------------------------------------
#include <stdio.h>

void DummyFunction() { }

#define KnownAddress ((char *) ::DummyFunction)

#define cpp_offsetof(type, member) \
(((char *) &((type *) KnownAddress)->member) - KnownAddress)

class Foo
{
public:

virtual ~Foo() { }

int a;
char b;
float c;
};

int main()
{
Foo f;

f.a = 1, f.b = 2, f.c = 3;

printf("Before: %d, %d, %f\n", f.a, f.b, f.c);

int *pA = (int *) ((char *) &f + cpp_offsetof(Foo, a));

char *pB = (char *) ((char *) &f + cpp_offsetof(Foo, b));

float *pC = (float *) ((char *) &f + cpp_offsetof(Foo, c));

*pA = 4, *pB = 5, *pC = 6;

printf("After: %d, %d, %f\n", f.a, f.b, f.c);

return 0;
}
------------------------------------------------------------

If you compile and run this program and it outputs this:

Before: 1, 2, 3.000000
After: 4, 5, 6.000000

Then my macro works on your compiler. I would love to hear if this
works or does not work for your compiler. So far I have only tried it
on g++ 4.1.1.

Thanks!
Bryan


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
John Moeller
Guest





PostPosted: Fri May 11, 2007 9:10 am    Post subject: Re: C++ offsetof hack Reply with quote



On Thu, 10 May 2007 22:40:58 CST, bji-ggcpp (AT) ischo (DOT) com wrote:

Quote:
#define KnownAddress ((char *) ::DummyFunction)

#define cpp_offsetof(type, member) \
(((char *) &((type *) KnownAddress)->member) - KnownAddress)

...

I realize that I am violating some of the rules of C++ - I have read
that you are specifically disallowed from trying to get the offset of
a class member. Presumably this is because the C++ standard wants to
leave the door open for compiler implementers to do really weird
things with class layout, so that offsetof cannot work at compile time
but could only work at runtime.

Again, virtual inheritance. If both B and C virtually inherit from A,
and D inherits from B and C, the B and C parts of D can end up in
"weird" areas of D. A pointer to B or C that is actually a D may not
necessarily point to the same spot as would a plain B or C object.

Quote:
The thing is, I have a hard time thinking of when a C++ compiler
writer would do something like make member variable calculations
require runtime code. I would always expect members of a C++ class to
be at a fixed, known offset from a pointer to that class, and that the
compiler would simply issue simple memory address addition
instructions to get the address of member variables of classes.

I'd advise you to pick up a guide on C++; Sutter's and Meyers' books
both treat this subject, IIRC.

Quote:
First - does anyone have any comments on this hack? Is it crazy to
think that it will work on all compilers? Can anyone think of a
reasonable way that C++ compilers might not be able to give a fixed
constant for the offset from a class to a member of the class, at
compile time, that would work for all instances of that class?

Again, it's inadvisable. The standard is set up the way it is for a
reason. You should probably do some heavy research on polymorphism in
C++ before you try these hacks.

Quote:
Second - if anyone wants to find out if my cpp_offsetof macro works
for their compiler, you can try this program:

------------------------------------------------------------
....
------------------------------------------------------------

If you compile and run this program and it outputs this:

Before: 1, 2, 3.000000
After: 4, 5, 6.000000

It does on MSVC++ 2005 EE, but your test only covers one class, and no
virtual inheritance.

Quote:
Then my macro works on your compiler. I would love to hear if this
works or does not work for your compiler. So far I have only tried it
on g++ 4.1.1.

John Moeller

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Guest






PostPosted: Sat May 12, 2007 4:29 am    Post subject: Re: C++ offsetof hack Reply with quote



On May 12, 2:56 pm, Sebastian Redl <e0226...@stud3.tuwien.ac.at>
wrote:

Quote:
$ g++ -c -ansi -pedantic -Wall -Werror offsethack.cpp
cc1plus: warnings being treated as errors
offsethack.cpp: In function 'int main()':
offsethack.cpp:29: warning: ISO C++ forbids casting between
pointer-to-function and pointer-to-object
offsethack.cpp:29: warning: ISO C++ forbids casting between
pointer-to-function and pointer-to-object
offsethack.cpp:31: warning: ISO C++ forbids casting between
pointer-to-function and pointer-to-object
offsethack.cpp:31: warning: ISO C++ forbids casting between
pointer-to-function and pointer-to-object
offsethack.cpp:33: warning: ISO C++ forbids casting between
pointer-to-function and pointer-to-object
offsethack.cpp:33: warning: ISO C++ forbids casting between
pointer-to-function and pointer-to-object

That would be GCC 4.1.1 too.

Now, the combination of -pedantic and -Werror might be rare (and largely
makes using dlsym() impossible), but it is still valid.

Thank you for that information, it is much appreciated.

I have found that GCC 4.1.1 accepts "0" instead of KnownAddress in my
macro. It has the same effect that I desire; the only reason for the
KnownAddress thing was to defeat some earlier GCC compilers (can't
remember which ones) which were issuing errors when I tried to cast
from a constant integer value to a pointer.

Thanks!
Bryan


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Guest






PostPosted: Mon May 14, 2007 8:12 am    Post subject: Re: C++ offsetof hack Reply with quote

On May 13, 6:58 am, Maciej Sobczak <see.my.homep...@gmail.com> wrote:

Quote:
You haven't read carefully enough. Smile
Even with the "appropriate" pointer sizes, the address spaces for
functions and data can be separated. You macro uses an arithmetic type
only for computing the difference, but Bad Things may have already
happened earlier - after the cast to (type*). The problem is that
KnownAddress might be illegal in the address space where (type*)
operates, so you already get illegal pointer by just casting. Then you
dereference this pointer to get to the member, which you can do only
when the pointer is dereferenceable. Here it isn't and you will choke
gray smoke before you even get to this unsigned long long trick.

All I was trying to say was, if the compiler decides at compile time
what the offsetof is, it doesn't matter what the actual value that I
am using as the base pointer is, since the same value gets subtracted
out again. It doesn't matter to me that the cast could scramble this
value in some way, as long as it is scrambled the same when it is cast
for the purposes of calculating the offset to the member, as it is for
the purposes of subtracting the original value back out.

If the compiler decides at compile time what the offset is, then it
doesn't matter at all what the address is, because it will never be
dereferenced at runtime.

If the compiler has to insert logic into the runtime code to
dereference the address and inspect its contents to decide how to get
to the member (which I guess it does need to do in some virtual
inheritence cases and maybe some others), then this macro will produce
code that crashes miserably at runtime. I think this is the "rarer"
case but it certainly can happen. It would never happen in *my* code
because I never define classes with virtual inheritence. But it could
definitely happen for someone else.

Anyway, you've convinced me not to use my macro. Your arguments are
sound and good, and I appreciate the information you have given me.
I will only use offsetof for POD types, and for C++ classes, I will do
something different (that is language compliant).

Thank you,
Bryan


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Seungbeom Kim
Guest





PostPosted: Mon May 14, 2007 9:10 am    Post subject: Re: C++ offsetof hack Reply with quote

bji-ggcpp (AT) ischo (DOT) com wrote:
Quote:

All I was trying to say was, if the compiler decides at compile time
what the offsetof is, it doesn't matter what the actual value that I
am using as the base pointer is, since the same value gets subtracted
out again. It doesn't matter to me that the cast could scramble this
value in some way, as long as it is scrambled the same when it is cast
for the purposes of calculating the offset to the member, as it is for
the purposes of subtracting the original value back out.

If the compiler decides at compile time what the offset is, then it
doesn't matter at all what the address is, because it will never be
dereferenced at runtime.

Your assumption is not guaranteed by the standard. Just generating a
pointer value that is not valid leads to undefined behaviour.
For example:

int a[2], *p = a + 3;

incurs undefined behaviour even though dereferencing does not happen,
because (a + 3) is an invalid pointer value.

int a, b, *p = &b - &a;

incurs undefined behaviour because a and b do not belong to the same
array, i.e. they are unrelated, and &b - &a is an invalid pointer value.

These may not cause any observable harm, as is the nature of undefined
behaviour, and the effects may cancel out when you do the subtraction
and your method may work just as you described above. However, it's not
guaranteed behaviour, and you're dancing on a thin ice.

--
Seungbeom Kim

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.