Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
480 views
in Technique[技术] by (71.8m points)

c++: why the restriction on offsetof() for non-standard layout objects, or how to recover parent from pointer-to-member

I have a situation where I have a pointer to a data member, and from that, wish to recover the address of the containing object (implementing 'properties' class for an object).

I have a fully functional implementation https://onlinegdb.com/ByQJurll_

  • convert the ptr-to-member to an offset
  • subtract back to get the parent
  • be careful to static_cast<> appropriately to handle multiple inheritance

But as near as I can tell, this code is not portable, well defined C++, when applied to a container object that uses public/private.

This is because from what I understand, the offsetof() trick is only guaranteed to work for standard_layout<> objects, and if you use public/private, it is no longer considered standard_layout.

I cannot understand the motivation behind this restriction (especially since what I have already works on many compilers - all tested). Is there some portable, legal way in C++ to recover the containing object given a pointer to member, and the address of that member?

(code is all in that link above, but quick summary here)

    template < typename OUTER_OBJECT, typename DATA_MEMBER_TYPE >
      inline size_t
    ConvertPointerToDataMemberToOffset (DATA_MEMBER_TYPE
                        (OUTER_OBJECT::*dataMember))
    {
instance
      return reinterpret_cast <
        char *>(&(((OUTER_OBJECT *) 0)->*dataMember)) - reinterpret_cast <
        char *>(0);
    }
    
    template < typename APPARENT_MEMBER_TYPE, typename OUTER_OBJECT,
      typename AGGREGATED_OBJECT_TYPE >
      inline OUTER_OBJECT * GetObjectOwningField (APPARENT_MEMBER_TYPE *
                              aggregatedMember,
                              AGGREGATED_OBJECT_TYPE
                              (OUTER_OBJECT::
                               *aggregatedPtrToMember))
    {
      std::byte * adjustedAggregatedMember =
        reinterpret_cast < std::byte * >(static_cast <
                         AGGREGATED_OBJECT_TYPE *
                         >(aggregatedMember));
      ptrdiff_t adjustment =
        static_cast < ptrdiff_t >
        (ConvertPointerToDataMemberToOffset (aggregatedPtrToMember));
      return reinterpret_cast <
        OUTER_OBJECT * >(adjustedAggregatedMember - adjustment);
    }
    void DoTest ();
    struct X1
    {
      int a;
      int b;
    };
    struct X2
    {
    public:
      int a;
    private:
      int b;
    private:
        friend void DoTest ();
    };
    void
    DoTest ()
    {
      {
        X1 t;
        static_assert (is_standard_layout_v < X1 >);
        void *aAddr = &t.a;
        void *bAddr = &t.b;
        assert (GetObjectOwningField (aAddr, &X1::a) == &t);
        assert (GetObjectOwningField (bAddr, &X1::b) == &t);
      }
      {
        // Check and warning but since X2 is not standard layout, this isn't guaranteed to work
        X2 t;
        static_assert (not is_standard_layout_v < X2 >);
        void *aAddr = &t.a;
        void *bAddr = &t.b;
        assert (GetObjectOwningField (aAddr, &X2::a) == &t);
        assert (GetObjectOwningField (bAddr, &X2::b) == &t);
      }
    }
question from:https://stackoverflow.com/questions/65940393/c-why-the-restriction-on-offsetof-for-non-standard-layout-objects-or-how-t

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...