Pointer to member-of-member???

S

Steve Rencontre

Rolf said:
An int S2::* can only point to int members of S2. S1 is a different
structure, and S1::a is obviously not a member of S2, so if you want to
point to an int member of S1 (no matter where the actual object you want to
acces lives), you need an int S1::*.

I don't accept that.

Consider this:

struct S2A { struct { int a; }; };
struct S2B { struct { int a; } s; };
struct S2C { struct S1C { int a; }; };

int S2A::*pa = &S2A::a; // works
int S2B::*pb = &S2B::a; // fails
int S2C::*pc = &S2C::a; // fails

The structures are all physically identical; I contend that it is purely
a syntactical issue that there is no way of expressing the obvious
semantic intent in cases B and C.
 
G

Greg

Steve said:
I don't accept that.

Consider this:

struct S2A { struct { int a; }; };
struct S2B { struct { int a; } s; };
struct S2C { struct S1C { int a; }; };

int S2A::*pa = &S2A::a; // works
int S2B::*pb = &S2B::a; // fails
int S2C::*pc = &S2C::a; // fails

The structures are all physically identical; I contend that it is purely
a syntactical issue that there is no way of expressing the obvious
semantic intent in cases B and C.

No, it's a storage issue. The problem is that a pointer to a member of
a member would need to store two offsets: the offset to the immediate
member and then a second offset to the member within that member. In
other words this kind of member pointer would somehow have to be able
to store twice the information in the same amount of space (and each
additional level would of course each add an additional offset that
would also need to be stored in this über member pointer). Since there
is no room to store multiple offsets in a pointer large enough to hold
only one offset, only member pointers to immediate members of a class
are supported in C++.

Greg
 
J

John Carson

Steve Rencontre said:
I don't accept that.

Consider this:

struct S2A { struct { int a; }; };
struct S2B { struct { int a; } s; };
struct S2C { struct S1C { int a; }; };

int S2A::*pa = &S2A::a; // works
int S2B::*pb = &S2B::a; // fails
int S2C::*pc = &S2C::a; // fails

The structures are all physically identical; I contend that it is
purely a syntactical issue that there is no way of expressing the
obvious semantic intent in cases B and C.

Actually, S2A is illegal (though Microsoft allows it as an extension) and
S2C has no members --- it is simply a type definition that contains another
type definition.

However,

struct S {int a;};

and struct S2B are physically identical, so we can use those to represent
your point.

Pointers to data members are rarely used. Pointers to data members of data
members are extremely rarely used. Doing what you want would complicate the
task of compiler writers --- by how much I am in no position to say --- and
I expect that it hasn't been thought worth the trouble --- to the extent
that it has been thought of at all.
 
J

John Carson

Greg said:
No, it's a storage issue. The problem is that a pointer to a member of
a member would need to store two offsets: the offset to the immediate
member and then a second offset to the member within that member. In
other words this kind of member pointer would somehow have to be able
to store twice the information in the same amount of space (and each
additional level would of course each add an additional offset that
would also need to be stored in this über member pointer). Since there
is no room to store multiple offsets in a pointer large enough to hold
only one offset, only member pointers to immediate members of a class
are supported in C++.

Greg


You may be right --- I know close to zero about compilers --- but it is not
obvious that you couldn't simply sum the two offsets and store the sum in a
single location.
 
S

Steve Rencontre

John said:
Actually, S2A is illegal (though Microsoft allows it as an extension)

So does gcc unless you specify -pedantic. When MS and Gnu agree on
something, it's probably fairly uncontroversial :)
S2C has no members --- it is simply a type definition that contains another
type definition.

Whoops, silly me, I do know that really...
Pointers to data members are rarely used.
True...

Pointers to data members of data members are extremely rarely used.

Err, /never/ AFAICT, 'cos there ain't no syntax to create them!
Doing what you want would complicate the
task of compiler writers

OTTOMH, I think the additional complexity would be lost in the noise
given what a C++ compiler has to do overall!
I expect that it hasn't been thought worth the trouble --- to the extent
that it has been thought of at all.

Yes, judging by the number of people (not just here) who at least
initially completely failed to grasp what I was trying to do!
 
R

Rolf Magnus

Steve said:
I don't accept that.

Consider this:

struct S2A { struct { int a; }; };
struct S2B { struct { int a; } s; };
struct S2C { struct S1C { int a; }; };

int S2A::*pa = &S2A::a; // works
int S2B::*pb = &S2B::a; // fails
int S2C::*pc = &S2C::a; // fails

The structures are all physically identical;

S2B doesn't have a member named a. Only the anonymous struct within it has
one. S2C doesn't have any member variables at all. Regarding S2A, I'm
actually surprised that this works. Maybe some obscure C++ rule because
that anonymous struct member wouldn't be useful otherwise.
I contend that it is purely a syntactical issue that there is no way of
expressing the obvious semantic intent in cases B and C.

They aren't obvious.
 
R

Rolf Magnus

John said:
You may be right --- I know close to zero about compilers --- but it is
not obvious that you couldn't simply sum the two offsets and store the sum
in a single location.

Well, things may get a bit more complicated once you start using non-pod
types and multiple and virtual inheritance.
 
S

Steve Rencontre

S2B doesn't have a member named a. Only the anonymous struct within it has
one. S2C doesn't have any member variables at all. Regarding S2A, I'm
actually surprised that this works. Maybe some obscure C++ rule because
that anonymous struct member wouldn't be useful otherwise.

As noted elsewhere, S2C was just me being stupid, but while S2A is not
strict ISO standard C++, it is a common extension.
They aren't obvious.

Ok, let's look at it from a different angle, forgetting non-standard
extensions:

struct S2A {
int x1;
int x2;
int x3;
};

struct S1 { int x1; int x2; }
struct S2B {
S1 s;
int x3;
};

struct S2C {
struct {
int x1;
int x2;
} s;
int x3;
};

Each and every one of these has exactly the same memory representation
(modulo alignment issues).

I trust we all agree that the meaning of

int S2A::*pa = &S2A::x2;

is straightforward and well-defined; given an S2A object, I can pick out
the middle int from it. The pointer-to-member is basically an
encapsulation of "Add offsetof(S2A,x2) to the object base". The same
concept is just as meaningful for S2B and S2C objects, but there's no
way of expressing it.

Finally, what about

struct S2D {
int x [3];
};

Now there's no confusion with nested structures, name scopes or anything
like that at all, but &S2D::x[1] doesn't work either. Yes, the pedantic
argument is that 'x' is a member of S2D and 'x[1]' isn't, but that
doesn't mean it's not a meaningful and reasonable thing to want to do.
 
S

Steve Rencontre

Rolf said:
Well, things may get a bit more complicated once you start using non-pod
types and multiple and virtual inheritance.

There are already lots of things that are restricted to POD types.

However, I don't think pointer-to-member-of-member introduces any new
complexity beyond that of ordinary pointer-to-member in the presence of
multiple/virtual inheritance.
 
R

roberts.noah

Steve said:
At the moment, the only thing I have found is a rather yucky workaround
with offsetof() macros and lots of casts. That is,

size_t offset = offsetof (S2, s.a);

*(int *) ((char *) &s2 + offset) = 123;

That is the only way but you don't need so many casts and you should
use C++ casts.

I also had such a problem. A redesign of what I was doing resulted in
a better solution. The offset thing was a necessity to do OO in C but
it isn't in C++. If you have such an issue where the offset macro is
needed you should be looking for a better solution, not just a better
way to get the offset.
 
B

Ben Pope

Steve said:
Ok, let's look at it from a different angle, forgetting non-standard
extensions:

struct S2A {
int x1;
int x2;
int x3;
};

struct S1 { int x1; int x2; }
struct S2B {
S1 s;
int x3;
};

struct S2C {
struct {
int x1;
int x2;
} s;
int x3;
};

Each and every one of these has exactly the same memory representation
(modulo alignment issues).

Ignoring possible differences, they're the same, yes.
I trust we all agree that the meaning of

int S2A::*pa = &S2A::x2;

is straightforward and well-defined; given an S2A object, I can pick out
the middle int from it. The pointer-to-member is basically an
encapsulation of "Add offsetof(S2A,x2) to the object base". The same
concept is just as meaningful for S2B and S2C objects, but there's no
way of expressing it.

C++ is a type safe language, so I beg to differ. And you can't just
throw away alignment issues, those are not guaranteed. So in a
hypothetical situation, I might be persuaded to agree, but that's not
relevant.

If you don't see the difference between pointer to member and pointer to
member of member as relevant, flatten your hierarchy.
Finally, what about

struct S2D {
int x [3];
};

Now there's no confusion with nested structures, name scopes or anything
like that at all, but &S2D::x[1] doesn't work either. Yes, the pedantic
argument is that 'x' is a member of S2D and 'x[1]' isn't, but that
doesn't mean it's not a meaningful and reasonable thing to want to do.

If you want to leave the realms of defined behaviour, feel free to use
reinterpret_cast or a union.

If it can't be done in the language rules, and you want more of an
explanation why, then may I suggest:

comp.std.c++

Ben Pope
 
S

Steve Rencontre

That is the only way but you don't need so many casts

How do I do it with fewer casts, unless I do something equally horrible
like,

*((int *) &s2 + offset / sizeof (int))
and you should use C++ casts.

Ok,

*reinterpret_cast <int *> (reinterpret_cast <char *> (*s2) +
offset)

Better? I don't really think so :)
 
S

Steve Rencontre

Ben said:
Ignoring possible differences, they're the same, yes.

The differences are not relevant differences. I'm not trying to lay one
structure on top of another, just pointing out that every one of them
contains three ints at knowable (the compiler knows them even if I
don't), fixed offsets from the base of the enclosing struct.
C++ is a type safe language, so I beg to differ.

In what way is it not type-safe to have a thing which purports to point
to an int in an object of class X pointing to an int in an object of
class X?
And you can't just throw away alignment issues, those are not guaranteed.

I don't need any guarantee of alignment. I'm not asking to be able to
use an S2A::* pointer with an S2B object. What you're saying sounds like
you think that two objects of the same type aren't guaranteed to have
the same memory layout!
If you want to leave the realms of defined behaviour, feel free to use
reinterpret_cast or a union.

They won't do the job in this case. There is no syntax which lets me
specify the pointer I want in any form whatsoever unless I figure out
the compiler's internal representation of pointer-to-member and create
it entirely by hand. Since the offsetof() version is legal and portable,
I see no benefit in doing so!
comp.std.c++

I may well do so.

However, what I originally wanted was not to know why it couldn't be
done, but how to do it! More precisely, I was looking for a way to do it
in the spirit of C++, given that I already have a way of doing it in
classic C hacker style :)
 
B

Ben Pope

Steve said:
The differences are not relevant differences. I'm not trying to lay one
structure on top of another, just pointing out that every one of them
contains three ints at knowable (the compiler knows them even if I
don't), fixed offsets from the base of the enclosing struct.


In what way is it not type-safe to have a thing which purports to point
to an int in an object of class X pointing to an int in an object of
class X?

You're asking for a pointer to an int member of S2, if it's a member of
a member of S2, it's not a member of S2.
I don't need any guarantee of alignment. I'm not asking to be able to
use an S2A::* pointer with an S2B object. What you're saying sounds like
you think that two objects of the same type aren't guaranteed to have
the same memory layout!

But they're not the same type! A pointer to an int member of S2 is not
the same as a pointer to an int member of instance of S1 as a member of S2.

I think I may have your original requirements confused:
> how can I get a pointer to the a in an S2?

The a isn't in S2, it's in S1, so:

int S1::*p1 = &S1::a;
s2.s.*p1 = 2;


I've summarised some options below (mostly for my own sanity!)


struct S1 { int a; };
struct S2 { S1 s; int b; };
struct S3 {
struct S1 { int a; } s;
int b;
};

int main() {
int S1::*p1 = &S1::a;
int S2::*p2 = &S2::b;
int S3::S1::*p3 = &S3::S1::a;

S1 s1;
S2 s2;
S3 s3;

s1.*p1 = 1; // s1.a
s2.s.*p1 = 2; // s2.s.a
s2.*p2 = 2; // s2.b
s3.s.*p3 = 3; // s3.s.a
}

Or did you want to be able to point to a or b with the same pointer?

Ben Pope
 
B

Ben Pope

Steve said:
Yes! I'm sorry if I didn't make that clear enough from the start.

That's what I had assumed (along with everybody else I think!).

Wondered if I was going off track.

Can't be done :p

Sorry!

Ben Pope
 
R

Rolf Magnus

Steve said:
The differences are not relevant differences. I'm not trying to lay one
structure on top of another, just pointing out that every one of them
contains three ints at knowable (the compiler knows them even if I
don't), fixed offsets from the base of the enclosing struct.


In what way is it not type-safe to have a thing which purports to point
to an int in an object of class X pointing to an int in an object of
class X?

Considering this example:

struct S1 { int x1; int x2; }
struct S2B {
S1 s;
int x3;
};

What you want is an int S2B::* that points to a member of S1, i.e. of
another struct. That means it would weaken type safety.
What if you only have a pointer or reference to the S1 instance? Still allow
an int S2B::* to point to x1, which would be logical? After all, the object
pointed to could be a member of an S2B object. But what if it's not?
I don't need any guarantee of alignment. I'm not asking to be able to
use an S2A::* pointer with an S2B object.

It looks to me as if that's exactly what you are asking.
 
S

Steve Rencontre

Rolf said:
Considering this example:

struct S1 { int x1; int x2; }
struct S2B {
S1 s;
int x3;
};

What you want is an int S2B::* that points to a member of S1, i.e. of
another struct. That means it would weaken type safety.
What if you only have a pointer or reference to the S1 instance?

Then I need an S1::* pointer, not an S2B::* pointer.
Still allow an int S2B::* to point to x1, which would be logical?

Why is that logical?
After all, the object
pointed to could be a member of an S2B object. But what if it's not?

I don't see why you're trying to make out that I want something I don't.

Look, if I have an S2B object, s2b, we all agree that I can write,

s2b.s.x1 = 1;
s2b.x3 = 2;

On any Earthlike planet, the compiler is going to generate structurally
identical code for both cases - add a fixed offset to the object base
address to get the destination address, load a value into that address.

All I'm after is to be able to have a pointer, p, which can duplicate
the effect of either of those statements via the expression 's2b.*p'.

If I also have,

S1 *ps1 = &s2b.s;

I neither expect nor want to be able to use the expression 'ps1->*p' and
I don't understand why you seem to be insisting I do.
 
G

Greg

Steve said:
Then I need an S1::* pointer, not an S2B::* pointer.


Why is that logical?


I don't see why you're trying to make out that I want something I don't.

Look, if I have an S2B object, s2b, we all agree that I can write,

s2b.s.x1 = 1;
s2b.x3 = 2;

On any Earthlike planet, the compiler is going to generate structurally
identical code for both cases - add a fixed offset to the object base
address to get the destination address, load a value into that address.

All I'm after is to be able to have a pointer, p, which can duplicate
the effect of either of those statements via the expression 's2b.*p'.

If I also have,

S1 *ps1 = &s2b.s;

I neither expect nor want to be able to use the expression 'ps1->*p' and
I don't understand why you seem to be insisting I do.

I would suggest focussing on the capabilities of the pointer you are
asking for - and not on the relatively simple manner with which you
intend to use it. You're essentially asking for a yacht which you
intend to use as a rowboat. The capabililties of a member pointer (and
by extension a member member pointer) have no relation to the actual
definition of the class to which it belongs. So even though there may
be only one data member to which a particular type of member pointer
can legitimately point to, the member pointer has to assume that there
are any number of matching members that it could point to and plan its
storage accordingly.

The pointer which you are asking for is one that must be able to point
to any int member anywhere in an S2B member located anywhere in an S1
struct. Those two "anywhere" requirements are twice the one "anywhere"
requirement for a standard member pointer. And while it is true that
your use of this member member pointer doesn't do justice to its
wideranging addressing capability, there is no way for the compiler to
slim down this type of pointer simply due to that fact. These pointers
can have only one format, and that format must be able to handle the
full requirements for that pointer type.

So the disconnect in this discussion I would say stems from the fact
that your relatively simple use of the pointer does not adequately
demonstrate the more complex cases which the same type of pointer must
(unfortunately) also be able to handle.

Greg
 
S

Steve Rencontre

Greg said:
I would suggest focussing on the capabilities of the pointer you are
asking for - and not on the relatively simple manner with which you
intend to use it.

In an ultimate sense, yes, but my requirement is strictly in the context
of a POD struct, and I can't conceive of any situation in which what I
want doesn't boil down to a simple numerical offset. This may be a
failure of my imagination, but I don't think so.

TBH, I can't think of /any/ situation in which the address of a data
item relative to the base address of its containing object is not a
compile-time constant, and I'd be fascinated if you could give me an
example.

Sure, there are (non-ISO) concepts like MSVC++ properties, which /look/
like simple data items but aren't, but I'm not asking for that. A
pointer-to-data-member that actually referenced a property in that sense
would be a weird and wonderful thing. Sticking within the normal
definition of data members, though, I really don't see where the problem
lies.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,776
Messages
2,569,602
Members
45,182
Latest member
BettinaPol

Latest Threads

Top