Standard way to find base address of struct from a member

做~自己de王妃 提交于 2019-12-02 02:48:29

Use offsetof from <cstddef>, but beware it is only defined on standard-layout types (Live at Coliru):

Data* getBaseDataPtrFromString(std::string* mStringMember) {
    static_assert(std::is_standard_layout<Data>::value,
                  "offsetof() only works on standard-layout types.");
    return reinterpret_cast<Data*>(
      reinterpret_cast<char*>(mStringMember) - offsetof(Data, b)
    );
}

offsetof is detailed in C++11 18.2/4:

The macro offsetof(type, member-designator) accepts a restricted set of type arguments in this International Standard. If type is not a standard-layout class (Clause 9), the results are undefined.195 The expression offsetof(type, member-designator) is never type-dependent (14.6.2.2) and it is value-dependent (14.6.2.3) if and only if type is dependent. The result of applying the offsetof macro to a field that is a static data member or a function member is undefined. No operation invoked by the offsetof macro shall throw an exception and noexcept(offsetof(type, member-designator)) shall be true.

and C99 (N1256) 7.17/3:

The macros are

NULL

which expands to an implementation-defined null pointer constant; and

offsetof(type, member-designator)

which expands to an integer constant expression that has type size_t, the value of which is the offset in bytes, to the structure member (designated by member-designator), from the beginning of its structure (designated by type). The type and member designator shall be such that given

static type t;

then the expression &(t.member-designator) evaluates to an address constant. (If the specified member is a bit-field, the behavior is undefined.)

The "restricted set of type arguments in this International Standard" in the C++ standard is there to draw your attention to the fact that offsetof is more restrictive than is the case for the C standard.

offsetof gives the offset in chars, so you need to cast to mStringMember to char * before doing the pointer arithmetic.

(Data*)((char*)mStringMember - offsetof(Data, b))

offsetof only works on standard-layout types.

There is a trick you might be able to use and does not require a standard layout: static_cast knows how to get to a more derived object from a pointer to one of its bases.

struct Data : private std::string {
private:
    using b_base = std::string;
public:
    // was int f() const { return b.size(); }
    int f() const { return b_base::size(); }

private:
    int a;
    float c;
    friend Data* getBaseDataPtrFromString(std::string* mStringMember);
};

Data* getBaseDataPtrFromString(std::string* mStringMember) {
    return static_cast<Data*>(mStringMember);
}
Basile Starynkevitch

If you are sure that some ptr is in fact the address of some s->b (which is not always true) you might try to use offsetof:

Data* getBaseDataPtrFromString(std::string* ptr) {
   void* ad = (char*)ptr - offsetof(Data,b);
   return reinterpret_cast<Data*>(ad);
}

BTW, GCC has a builtin_offsetof to help implementing the offsetof macro (notably in more general cases than those required by the standard). See this question.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!