Topic: Fwd: pointer to member conversion to a pointer to member that is a member's base class


Author: wade@stoner.com (Bill Wade)
Date: Thu, 26 Aug 2004 00:29:16 GMT
Raw View
v.Abazarov@comAcast.net (Victor Bazarov) wrote in message news:<Qp2Vc.94$Ae.89@newsread1.dllstx09.us.to.verio.net>...

> The basic idea is: if for two classes B and D exists an implicit
> conversion from { D* to B* } (plain vanilla ptr-to-derived to ptr-to-base
> conversion, see 4.10), then the conversion { D T::* to B T::* } for an
> arbitrary class T should exist as well.  I there anything I am missing?

If B is a virtual base, then D* to B* is still plain-vanilla, but it
requires an actual D object (or NULL), because the conversion depends
on the dynamic type of the object.  When doing pointer to member
conversions, you don't know the eventual dynamic type of the D object.
 Supporting this feature (for virtual base B) has a run time cost,
even for programs that don't use it.

---
[ 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: rjpeters@klab.caltech.edu (Robert J. Peters)
Date: Fri, 27 Aug 2004 04:14:45 GMT
Raw View
invalid@bigfoot.com (Bob Hairgrove) writes:

> It's a very interesting problem ... does anyone else have some
> real-world examples using pointer to member data which can't be
> re-written with normal pointers?

I can point out a couple past discussions of this issue:

  [beware of line-wrapping, there should be three URLS here]

  http://groups.google.com/groups?threadm=6gy9yj4usc.fsf%40goethe.klab.caltech.edu
  http://groups.google.com/groups?threadm=6gzokwnmrz.fsf%40goethe.klab.caltech.edu
  http://groups.google.com/groups?threadm=6gpu98uw17.fsf%40hume.klab.caltech.edu

The issue came up for me in writing a persistence system that needed an
enhanced run-time type information about the data members of different
classes.

And to quote myself (from
http://groups.google.com/groups?selm=6gpu98uw17.fsf%40hume.klab.caltech.edu):

> While I'm thinking of it, let's just make clear again that we aren't
> talking about conversion from "T Derived::*" to "T Base::*", which is
> definitely and rightly prohibited by the standard, but rather about
> "Derived C::*" to "Base C::*", which is also prohibited, but is a
> horse of a different color...
>
> I felt (and still feel) that it would be useful to allow this
> conversion, since it could help to build up some meta-information
> about classes in some sort of std::container<Base T::*>, for exposing
> to a GUI or persistence system, for example. Moreover, while there are
> some complexities in implementing polymorphic ptr-to-mem assignment
> (making proper adjustments for when the pointed-to-types involve
> multiple and/or virtual inheritance), making the proper address
> offsets are no different than what is required for normal "Derived*"
> to "Base*" conversion when multiple or virtual inheritance is involved
> (some of the details of this were spelled out in those previous
> threads).
>
> As it stands now, you can't do polymorphic ptr-to-mem assignment.

[...]

> On a more practical note, the workaround that I used was to set up a
> template class hierarchy that acted as a polymorphic ptr-to-mem (not
> as efficient as if it were implemented in the compiler, but at least
> it doesn't rely on non-standard behavior):
>
> template <class C, class Base>
> class PtrToMem
> {
> public:
>   virtual ~PtrToMem() {}
>   virtual       Base& dereference(      C& obj) = 0;
>   virtual const Base& dereference(const C& obj) = 0;
> };
>
> template <class C, class Base, class Derived>
> class PtrToMemImpl : public PtrToMem<C, Base>
> {
>   Derived C::* itsPtr;
> public:
>   PtrToMemImpl(Derived C::* ptr) : itsPtr(ptr) {}
>   virtual ~PtrToMemImpl() {}
>
>   virtual       Base& dereference(      C& obj)
>   {
>     return obj.*itsPtr;
>   }
>
>   virtual const Base& dereference(const C& obj)
>   {
>     return obj.*itsPtr;
>   }
> };
>
> Then you can use a std::container<boost::shared_ptr<PtrToMem<C, Base> >.
>
> If you want to allow polymorphism in the "class C" hierarchy as well,
> you can do this instead:
>
> template <class C2, class C, class Base, class Derived>
> class PtrToMemImpl : public PtrToMem<C, Base>
> {
>   Derived C2::* itsPtr;
> public:
>   // ...
>
>   virtual       Base& dereference(      C& obj)
>   {
>     C2& obj2 = dynamic_cast<C2&>(obj); // or something else appropriate
>     return obj2.*itsPtr;
>   } // ...
> };

Cheers,
Rob

--
****************************************************************
Robert J. Peters, Ph.D.           Computation and Neural Systems
rjpeters@klab.caltech.edu                                Caltech
www.klab.caltech.edu/rjpeters/                  Mail Code 139-74
(626) 395-2882                                Pasadena, CA 91125

---
[ 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: invalid@bigfoot.com (Bob Hairgrove)
Date: Tue, 24 Aug 2004 02:51:40 GMT
Raw View
On Mon, 23 Aug 2004 15:55:41 GMT, v.Abazarov@comAcast.net (Victor
Bazarov) wrote:

>Bob Hairgrove wrote:
>> On Fri, 20 Aug 2004 23:27:25 GMT, v.Abazarov@comAcast.net (Victor
>> Bazarov) wrote:
>>
>>
>>>I don't think that I asked for this type of conversion.  I was talking
>>>member objects, not member functions.  Here is a simple[r] example of
>>>what is needed:
>>>
>>>  struct B { virtual int foo() const = 0; };
>>>  struct D : B { virtual int foo() const { return 42; } };
>>>  struct DD : B { virtual int foo() const { return 43; } };
>>>  struct X { B X::* pb; D d; DD dd;
>>>      int dofoo() const { (this->*pb).foo(); } };
>>>
>>>  int main()
>>>  {
>>>      X x;
>>>      x.pb = &X::d;  // ***
>>>      int fortytwo = x.dofoo(); // makes fortytwo == 42
>>>      x.pb = &X::dd; // ***
>>>      int fortythree = x.dofoo(); // makes fortythree == 43
>>>  }
>>>
>>>The 'X::dofoo' function is of interest.  It takes the pointer to member
>>>of X of class B and calls the virtual function on it.  What should happen
>>>is this: whatever this pointer points to should get its final overrider
>>>called.  If I make pb point to a 'd', the D::foo should be called but for
>>>the object 'd' in the 'x'.  Same if pb points to 'dd'.
>>>
>>>Right now it "works" if a reinterpret_cast is used on the right sides of
>>>the assignments on lines marked with '***'.  But reinterpret_cast does not
>>>allow that type of conversion either.  I submit an implicit conversion of
>>>that sort should exist.  Is there anything that would make it difficult?
>>
>>
>> Actually, as John Harrison pointed out to me in another thread on
>> comp.lang.c++, this can also be done much more easily using normal
>> pointers, i.e.:
>>
>> #include <iostream>
>> #include <ostream>
>>
>> struct B { virtual int foo() const = 0; };
>> struct D : B { virtual int foo() const { return 42; } };
>> struct DD : B { virtual int foo() const { return 43; } };
>> struct X { B * pb; D d; DD dd;  // pb is now simply a B*
>>    int dofoo() const { return pb->foo(); } };
>>
>> int main()
>> {
>>    X x;
>>    x.pb = &(x.d);  // ***
>>    int fortytwo = x.dofoo(); // makes fortytwo == 42
>>    x.pb = &(x.dd); // ***
>>    int fortythree = x.dofoo(); // makes fortythree == 43
>>
>>    std::cout << "fortytwo:\t" << fortytwo << std::endl;
>>    std::cout << "fortythree:\t" << fortythree << std::endl;
>>    return 0;
>> }
>
>Right.  This works fine.  Now imagine this situation:
>--------------------------------
>struct B { virtual int foo() const = 0; };
>struct D : B { virtual int foo() const { return 42; } };
>struct DD : B { virtual int foo() const { return 43; } };
>struct X { static B X::* pb; D d; DD dd; // ******* pb is now static!
>     int dofoo() const { (this->*pb).foo(); } };

dofoo() should return a value, i.e.:
     int dofoo() const { return (this->*pb).foo(); } };
>
>int main()
>{
>     X x[5];
>     X::pb = &X::d;  // ***
>     for (int i = 0; i < 5; ++i)
>         int fortytwo = x[i].dofoo(); // makes fortytwo == 42
>     X::pb = &X::dd; // ***
>     for (int i = 0; i < 5; ++i)
>         int fortythree = x[i].dofoo(); // makes fortythree == 43
>}
>--------------------------------
>
>In this example, the 'pb' member is static.  Yes, I have to admit this
>example is somewhat artificial, but imagine that there is some kind of
>global dispatch mechanism that should change the behaviour of _all_ X
>objects at some point without knowing how many there are.  It could be
>done with a flag and then every object has to check the flag and do as
>prescribed (which is a bit less efficient, probably).
>
>Anyway, in this case, we cannot make 'pb' a pointer to an object, simply
>because that has to be done for every instance of 'X'.  What to do in
>this case?

Well, you could do it with a static pointer to a normal B object ...
or am I missing something?

As I see it, although the language allows pointer to member data, it
is of limited use because you always need an instance of an object in
order to use it, and if you have a concrete instance, you can always
replace the pointer to member data with a normal pointer to the data.
It's different with pointer to member functions which don't have an
actual address, but only the offset given by PTMF.

>Thank you for your interest in this problem.  I wish I could get a bit
>more participation from other frequents here.
>
>Victor

It's a very interesting problem ... does anyone else have some
real-world examples using pointer to member data which can't be
re-written with normal pointers?

--
Bob Hairgrove
NoSpamPlease@Home.com

---
[ 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: v.Abazarov@comAcast.net ("Victor Bazarov")
Date: Tue, 24 Aug 2004 06:40:57 GMT
Raw View
"Bob Hairgrove" <invalid@bigfoot.com> wrote...
> On Mon, 23 Aug 2004 15:55:41 GMT, v.Abazarov@comAcast.net (Victor
> Bazarov) wrote:
> [..]
> >Right.  This works fine.  Now imagine this situation:
> >--------------------------------
> >struct B { virtual int foo() const = 0; };
> >struct D : B { virtual int foo() const { return 42; } };
> >struct DD : B { virtual int foo() const { return 43; } };
> >struct X { static B X::* pb; D d; DD dd; // ******* pb is now static!
> >     int dofoo() const { (this->*pb).foo(); } };
>
> dofoo() should return a value, i.e.:
>      int dofoo() const { return (this->*pb).foo(); } };

Thank you for this correction.

> >
> >int main()
> >{
> >     X x[5];
> >     X::pb = &X::d;  // ***
> >     for (int i = 0; i < 5; ++i)
> >         int fortytwo = x[i].dofoo(); // makes fortytwo == 42
> >     X::pb = &X::dd; // ***
> >     for (int i = 0; i < 5; ++i)
> >         int fortythree = x[i].dofoo(); // makes fortythree == 43
> >}
> >--------------------------------
> >
> >In this example, the 'pb' member is static.  Yes, I have to admit this
> >example is somewhat artificial, but imagine that there is some kind of
> >global dispatch mechanism that should change the behaviour of _all_ X
> >objects at some point without knowing how many there are.  It could be
> >done with a flag and then every object has to check the flag and do as
> >prescribed (which is a bit less efficient, probably).
> >
> >Anyway, in this case, we cannot make 'pb' a pointer to an object, simply
> >because that has to be done for every instance of 'X'.  What to do in
> >this case?
>
> Well, you could do it with a static pointer to a normal B object ...

But 'd' and 'dd' are not static.

> or am I missing something?

Yes.  In the case of a pointer to normal B object, every time an instance
is to use the object to 'dofoo', it would have to reset the pointer to
the address of _its_own_ 'd' or 'dd'.  With an unknown number of objects
running around, the control _can_ be done with a state-wide value and
the necessity for each instance to check the value and behave accordingly
(didn't I already mention that?...  no matter)

> As I see it, although the language allows pointer to member data, it
> is of limited use because you always need an instance of an object in
> order to use it, and if you have a concrete instance, you can always
> replace the pointer to member data with a normal pointer to the data.

Using 'dofoo' on every object's own 'd' or 'dd' is the point.  Making
'pb' a member will unnecessarily increase the size of each 'X' object by
sizeof(pb), just like replacing the pointer-to-member mechanism with some
sort of value to check would unnecessarily increase the time each 'X'
object would have to spend checking that value.

Victor

> [..]
> It's a very interesting problem ... does anyone else have some
> real-world examples using pointer to member data which can't be
> re-written with normal pointers?
>
> --
> Bob Hairgrove

---
[ 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: invalid@bigfoot.com (Bob Hairgrove)
Date: Tue, 24 Aug 2004 17:30:21 GMT
Raw View
On Tue, 24 Aug 2004 06:40:57 GMT, v.Abazarov@comAcast.net ("Victor
Bazarov") wrote:

[big snip...]

After giving this much thought, I think I can finally see where there
would be some advantages to being able to do this. It wasn't easy,
because I still don't have a real-life example for this which I would
do exactly this way. The example you have given (polymorphic dispatch)
can be -- and usually is -- implemented somehow with a container of
base class pointers. There are design issues associated with having
objects as member data of the derived types: Clients need to "know
about" D, DD etc., and they shouldn't have to know these details.

>I submit an implicit conversion of that sort should exist.
>Is there anything that would make it difficult?

The mechanism looks "do-able". Although I wouldn't necessarily want to
do it this way, I can see that it might provide a more flexible way of
doing things for some developers. The only implementation issues I can
see would be the usual problems we already have with PTM when multiple
inheritance is involved.

>Thank you for your interest in this problem.  I wish I could get a bit
>more participation from other frequents here.

Yes, it would be nice to have more "gurus" get involved in the
discussion here.

Cheers!

--
Bob Hairgrove
NoSpamPlease@Home.com

---
[ 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: invalid@bigfoot.com (Bob Hairgrove)
Date: Sat, 21 Aug 2004 11:46:52 GMT
Raw View
On Fri, 20 Aug 2004 23:27:25 GMT, v.Abazarov@comAcast.net (Victor
Bazarov) wrote:

>I don't think that I asked for this type of conversion.  I was talking
>member objects, not member functions.  Here is a simple[r] example of
>what is needed:
>
>   struct B { virtual int foo() const = 0; };
>   struct D : B { virtual int foo() const { return 42; } };
>   struct DD : B { virtual int foo() const { return 43; } };
>   struct X { B X::* pb; D d; DD dd;
>       int dofoo() const { (this->*pb).foo(); } };
>
>   int main()
>   {
>       X x;
>       x.pb = &X::d;  // ***
>       int fortytwo = x.dofoo(); // makes fortytwo == 42
>       x.pb = &X::dd; // ***
>       int fortythree = x.dofoo(); // makes fortythree == 43
>   }
>
>The 'X::dofoo' function is of interest.  It takes the pointer to member
>of X of class B and calls the virtual function on it.  What should happen
>is this: whatever this pointer points to should get its final overrider
>called.  If I make pb point to a 'd', the D::foo should be called but for
>the object 'd' in the 'x'.  Same if pb points to 'dd'.
>
>Right now it "works" if a reinterpret_cast is used on the right sides of
>the assignments on lines marked with '***'.  But reinterpret_cast does not
>allow that type of conversion either.  I submit an implicit conversion of
>that sort should exist.  Is there anything that would make it difficult?

Actually, as John Harrison pointed out to me in another thread on
comp.lang.c++, this can also be done much more easily using normal
pointers, i.e.:

#include <iostream>
#include <ostream>

struct B { virtual int foo() const = 0; };
struct D : B { virtual int foo() const { return 42; } };
struct DD : B { virtual int foo() const { return 43; } };
struct X { B * pb; D d; DD dd;  // pb is now simply a B*
   int dofoo() const { return pb->foo(); } };

int main()
{
   X x;
   x.pb = &(x.d);  // ***
   int fortytwo = x.dofoo(); // makes fortytwo == 42
   x.pb = &(x.dd); // ***
   int fortythree = x.dofoo(); // makes fortythree == 43

   std::cout << "fortytwo:\t" << fortytwo << std::endl;
   std::cout << "fortythree:\t" << fortythree << std::endl;
   return 0;
}

--
Bob Hairgrove
NoSpamPlease@Home.com

---
[ 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                       ]