Fun with pointers-to-class-data-members
I was reading that thread "how to convert class data member pointer to
non class type?" (http://groups.google.com/group/comp.lang.c+
+.moderated/browse_thread/thread/a5303ae0a317d86a) and it got me
thinking:
If I have:
- a pointer to an object of class X
- a class data member pointer to a member of X
the compiler will let me obtain a pointer (or a reference) to that
data member in that specific object. Here's an illustration:
class X { public: int a; X() : a(0) {}};
int X::* ptr_to_int_in_X = & X::a;
X sample;
std::cout << "sample.a=" << sample.a << '\n';
X * ptr_to_sample = & sample;
int * ptr_to_a_in_sample = & (ptr_to_sample ->* ptr_to_int_in_X);
*ptr_to_a_in_sample = 666;
std::cout << "sample.a=" << sample.a << '\n';
It prints, as expected, this:
sample.a=0
sample.a=666
But suppose I have:
- a pointer to a data member in the object (above, this would be
"ptr_to_a_in_sample")
- a class data member pointer (above, it would be "ptr_to_int_in_X")
How come the language does not let me recover ptr_to_sample from the
above?
Is there an inherent irreversibility arising from some combination of
multiple & virtual inheritance that prevents that recovery?
I can see that bad things would happen if "ptr_to_int_in_X" was
combined with a pointer to some other place in the guts of the object,
but hey, that's nothing worse than what can be done with
reinterpret_cast or any other way of shooting your foot.
Expecting that compiler writers would go for an obvious implementation
of pointers-to-class-data-members as offsets from the object's address
to the member's address, I wrote this experiment:
#include <stdint.h>
#include <string>
#include <iostream>
template <class OWNER_CLASS, typename OWNED_DATA>
OWNER_CLASS *
data_member2owner_object(OWNED_DATA * data, OWNED_DATA OWNER_CLASS::*
const data_member_ptr)
{
union {
OWNED_DATA OWNER_CLASS ::* data_member_ptr;
uintptr_t scratch;
} hackery;
hackery.data_member_ptr = data_member_ptr;
return
reinterpret_cast<OWNER_CLASS*>(reinterpret_cast<uintptr_t>(data) -
hackery.scratch);
}
class A {
public:
std::string n;
int x;
int y;
bool z;
A(const char* name) : n(name) {}
void hello() { std::cout << n << std::endl;}
};
class B : virtual public A { public: B(const char* name) : A(name) {}
B() : A("unnamed") {}};
class C : public B { public: C(const char* name) : A(name) {}};
int main (int argc, const char * argv[])
{
A a("I am a");
B b("And I am B, not a");
C c("Here's C!");
data_member2owner_object(& (a.n), & A::n)->hello(); // Prints "I am
a"
data_member2owner_object(& (a.x), & A::x)->hello(); // Prints "I am
a"
data_member2owner_object(& (a.y), & A::y)->hello(); // Prints "I am
a"
data_member2owner_object(& (a.z), & A::z)->hello(); // Prints "I am
a"
data_member2owner_object(& (b.n), & B::n)->hello(); // Prints "And I
am B, not a"
data_member2owner_object(& (b.x), & B::x)->hello(); // Prints "And I
am B, not a"
data_member2owner_object(& (b.y), & B::y)->hello(); // Prints "And I
am B, not a"
data_member2owner_object(& (b.z), & B::z)->hello(); // Prints "And I
am B, not a"
data_member2owner_object(& (c.n), & C::n)->hello(); // Prints
"Here's C!"
data_member2owner_object(& (c.x), & C::x)->hello(); // Prints
"Here's C!"
data_member2owner_object(& (c.y), & C::y)->hello(); // Prints
"Here's C!"
data_member2owner_object(& (c.z), & C::z)->hello(); // Prints
"Here's C!"
return 0;
}
And the above works perfectly fine compiled with GCC4.2 in Xcode, and
just as well in VisualStudio8.0. The expected output is as indicated
in the comments.
I am curious whether the above breaks in any compiler that can compile
it.
And also curious what y'all in this group think about the general idea
of being able to recover object address from its data member's address
and class-data-member-pointer.
KK
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]