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 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…