|
From: xtrigger303 on 16 Apr 2008 18:58 Hi to all, my problem is the following: let's suppose I have a pointer to an object which I'm sure is enclosed in another object. Is it possible (safely) with some pointer arithmetics to get the address of the enclosing object? Obviously without passing a "back pointer" to the constructor of the inner object... The example below seems to work but I would like some insights from the experts. This is just something I would use for a debugging class, in working code I would certainly pass a pointer from the enclosing class to the inner object. Suppose also that I want to use this trick in the CONSTRUCTOR of the inner object. At that time the outer object would not be constructed already but the address I get should be correct, right? Thanks very much in advance, Francesco P.S. Sorry for cross-posting to c.l.c++, but I got no answers... #include <iostream> #include <cassert> struct CInner {}; struct COuter { char mPad[ 10 ]; CInner mInner; }; int main() { COuter outer; CInner COuter::* membPtr( &COuter::mInner ); std::cout << &outer << std::endl; std::cout << &( outer.*membPtr) << std::endl; // LET'S SUPPOSE I HAVE THE ADDRESS OF THE INNER OBJECT CInner * innerPtr( &( outer.*membPtr ) ); // NOW I WANT TO GET THE ADDRESS OF THE ENCLOSING OBJECT // IS THE FOLLOWING OK? COuter * outerPtr = reinterpret_cast< COuter * > ( innerPtr - &( static_cast< COuter * >( 0 )->*membPtr ) ); std::cout << outerPtr << std::endl; assert( &outer == outerPtr ); std::cin.get(); } -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Lance Diduck on 17 Apr 2008 03:01 On Apr 17, 5:58 am, xtrigger...(a)gmail.com wrote: > my problem is the following: > let's suppose I have a pointer to an object which I'm sure is enclosed > in another object. > Is it possible (safely) with some pointer arithmetics to get the > address of the enclosing object? This is not reliable. This may work for this particular case of a member in a otherwise plain struct (to maintain C compatibility), but not in general. For example, add this complexity struct VOuter:virtual COuter{}; struct VOuter2:virtual VOuter,virtual COuter{}; So given this: VOuter2 outer; VOuter2 VOuter2 ::*membPtr( &VOuter2 ::mInner ); how would you find the address of outer from membPtr? It would be possible to write COuter * outerPtr = reinterpret_cast< COuter * > ( innerPtr - &( static_cast< COuter * >( 0 )->*membPtr ) ); VOuter2 * vouterPtr=dynamic_cast<VOuter2 >(outerPtr ); however there is no way to determine just when you need that dynamic_cast, and if you call dynamic_cast on something that has no virtuals, well that is undefined as well. Debuggers do use knowledge about object layout to do their work, however there is no way in general to determine obbject layout using the syntax of the language only Lance -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Greg Herlihy on 17 Apr 2008 07:22 On Apr 17, 11:01 am, Lance Diduck <lancedid...(a)nyc.rr.com> wrote: > On Apr 17, 5:58 am, xtrigger...(a)gmail.com wrote:> my problem is the following: > > let's suppose I have a pointer to an object which I'm sure is enclosed > > in another object. > > Is it possible (safely) with some pointer arithmetics to get the > > address of the enclosing object? > > This is not reliable. This may work for this particular case of a > member in a otherwise plain struct (to maintain C compatibility), but > not in general. The "offsetof" macro found in <cstddef> can be applied to any C++ POD- class reliably - including the COuter class in the original example. The offsetof macro returns the offset (in bytes) of the member from its base class pointer: struct CInner {}; struct COuter { char mPad[ 10 ]; CInner mInner; }; int main() { COuter o; CInner * inner = &o.mInner; void * v = inner; v = (static_cast<char*>(v)-offsetof( COuter, mInner)); COuter *outer = static_cast<COuter *>(v); assert(outer == &o); } >For example, add this complexity > > struct VOuter:virtual COuter{}; > struct VOuter2:virtual VOuter,virtual COuter{}; > ... Or refrain from doing so. :-) Since the offsetof macro only works with POD types, the programmer has to be ensure that COuter is a POD class before apply offsetof to one of its members. But as long as the class in question is a POD type, then the offsetof macro - although messy and inelegant - is the safe solution to the original poster's problem. Greg -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: courpron on 17 Apr 2008 07:20 On Apr 17, 11:58 am, xtrigger...(a)gmail.com wrote: > Hi to all, > > my problem is the following: > let's suppose I have a pointer to an object which I'm sure is enclosed > in another object. > Is it possible (safely) with some pointer arithmetics to get the > address of the enclosing object? > Obviously without passing a "back pointer" to the constructor of the > inner object... > > The example below seems to work but I would like some insights from > the experts. > > This is just something I would use for a debugging class, in working > code I would certainly > pass a pointer from the enclosing class to the inner object. > Suppose also that I want to use this trick in the CONSTRUCTOR of the > inner object. > At that time the outer object would not be constructed already but the > address I get should be correct, > right? > > Thanks very much in advance, > Francesco > > P.S. > Sorry for cross-posting to c.l.c++, but I got no answers... > > #include <iostream> > #include <cassert> > > struct CInner {}; > struct COuter > { > char mPad[ 10 ]; > CInner mInner; > > }; > > int main() > { > COuter outer; > CInner COuter::* membPtr( &COuter::mInner ); > std::cout << &outer << std::endl; > std::cout << &( outer.*membPtr) << std::endl; > > // LET'S SUPPOSE I HAVE THE ADDRESS OF THE INNER OBJECT > CInner * innerPtr( &( outer.*membPtr ) ); > > // NOW I WANT TO GET THE ADDRESS OF THE ENCLOSING OBJECT > // IS THE FOLLOWING OK? > COuter * outerPtr = reinterpret_cast< COuter * > > ( innerPtr - &( static_cast< COuter * >( 0 )->*membPtr ) ); > std::cout << outerPtr << std::endl; > > assert( &outer == outerPtr ); > std::cin.get(); > If COuter is a POD type, it will work. Otherwise it may not work, for example when multiple and virtual inheritances are involved. By the way you should better use the (rather unknown) offsetof macro defined in the <cstddef> header. With the latter, you can replace this : > COuter * outerPtr = reinterpret_cast< COuter * > > ( innerPtr - &( static_cast< COuter * >( 0 )->*membPtr ) ); by : COuter * outerPtr2 = reinterpret_cast< COuter * > ( innerPtr - offsetof(COuter, mInner) ); which is more portable on different architectures. Of course the C++ standard states that the first argument of the offsetof macro shall accept a POD type. Alexandre Courpron. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: xtrigger303 on 18 Apr 2008 08:16
Thanks a lot to all. I got Lance point. But I just want to get the address of the first enclosing object. I'm not interested in the dynamic type of an object that may have derived from this "first encloser". ( Hope my english is good enough ). After your hints I tried to use offsetof but it uses casts to references instead of pointers, so it gives me some warnings... PLUS, I do not understand why this technique should not work with non POD types. This is my understanding, please correct me if I'm wrong: 1 - First I obtain a "ghost" address of the inner object of a (non- existent) outer object with address zero. CInner COuter::* membPtr = &COuter::mInner; CInner * ghostInnerAddr = &( static_cast< COuter * >( 0 )- >*membPtr ); 2 - I calculate the offset in chars ptrdiff_t offset = reinterpret_cast< char * >( ghostInnerAddr ) - static_cast< char * >( 0 ); 3 - I then subtract from a real inner pointer to obtain the address of the outer char * outerAddr = reinterpret_cast< char * >( realInnerAddr ) - offset; 4 - I cast back to what I'm sure (AM I???) it's the enclosing object COuter * = reinterpret_cast< COuter * >( outerAddr ); What step can actually go wrong? And why a non-POD type should mess this up? Remember I just want to get to the first enclosing object, not to the address of an object that might have derived from it. Please check the code below, maybe it's clearer than the explanation.... I tried with diamond multiple inheritance, non-diamond multiple inheritance, single inheritance. It seems to always work... Thanks again in advance for any comment. Sorry if I insist but it's just to understand better. Francesco #include <cstddef> #include <cassert> #include <set> #include <iostream> #include <algorithm> #include <iterator> #include <boost/lambda/lambda.hpp> #include <boost/lambda/bind.hpp> #define M_DEBUG_TRACKER mDebugTracker template< typename T > class CDebugTracker { public: typedef std::set< CDebugTracker< T > const * > CReg; static CReg sReg; CDebugTracker() { sReg.insert( this ); } CDebugTracker( CDebugTracker const & ) { sReg.insert( this ); } ~CDebugTracker() { sReg.erase( this ); } T const * GetEnclAddr( ) const { CDebugTracker< T > const T::* membPtr = &T::M_DEBUG_TRACKER; ptrdiff_t offset = reinterpret_cast< char const * > ( &( static_cast< T const * >( 0 )- >*membPtr ) ) - static_cast< char const * >( 0 ); return reinterpret_cast< T const * >( reinterpret_cast< char const * >( this ) - offset ); } }; template< typename T > std::set< CDebugTracker< T > const * > CDebugTracker< T >::sReg; #ifndef NDEBUG #define DEBUG_TRACKER( ENCL_TYPE ) \ template< typename T > friend class CDebugTracker; \ CDebugTracker< ENCL_TYPE > M_DEBUG_TRACKER #define DEBUG_TRACK_CHECK assert( this == M_DEBUG_TRACKER.GetEnclAddr() ) #elif #define DEBUG_TRACKER( ENCL_TYPE ) #endif class COuter { public: COuter() { DEBUG_TRACK_CHECK; } private: char mPad1[ 5 ]; DEBUG_TRACKER( COuter ); char mPad2[ 5 ]; }; // class COuter2 : public virtual COuter { public: COuter2() { DEBUG_TRACK_CHECK; } private: char mPad1[ 5 ]; DEBUG_TRACKER( COuter2 ); char mPad2[ 5 ]; }; // class COuter3 : public virtual COuter2, public virtual COuter { public: COuter3() { DEBUG_TRACK_CHECK; } private: char mPad1[ 5 ]; DEBUG_TRACKER( COuter3 ); char mPad2[ 5 ]; }; #define INSERT_ADDR( OBJ ) "- " # OBJ << " - " << &OBJ << "\n" int main() { using namespace boost::lambda; COuter obj1; COuter2 obj2; COuter3 obj3; std::cout << INSERT_ADDR( obj1 ) << std::endl; std::cout << INSERT_ADDR( obj2 ); std::cout << INSERT_ADDR( static_cast< COuter & >( obj2 ) ) << std::endl; std::cout << INSERT_ADDR( obj3 ); std::cout << INSERT_ADDR( static_cast< COuter2 & >( obj3 ) ); std::cout << INSERT_ADDR( static_cast< COuter & >( obj3 ) ) << std::endl; std::cout << "COuter adrresses:\n"; std::for_each( CDebugTracker< COuter >::sReg.begin(), CDebugTracker< COuter >::sReg.end(), std::cout << bind( &CDebugTracker< COuter >::GetEnclAddr, _1 ) << "\n" ); std::cout << "COuter2 adrresses:\n"; std::for_each( CDebugTracker< COuter2 >::sReg.begin(), CDebugTracker< COuter2 >::sReg.end(), std::cout << bind( &CDebugTracker< COuter2 >::GetEnclAddr, _1 ) << "\n" ); std::cout << "COuter3 adrresses:\n"; std::for_each( CDebugTracker< COuter3 >::sReg.begin(), CDebugTracker< COuter3 >::sReg.end(), std::cout << bind( &CDebugTracker< COuter3 >::GetEnclAddr, _1 ) << "\n" ); std::cin.get(); } //end -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |