Standard way to find base address of struct from a member

五迷三道 提交于 2019-12-20 02:57:27

问题


struct Data {
    int a;
    std::string b;
    float c;
};

std::string* allocateDataAndGetString() {
    Data* dataPtr(someAllocator.allocate<Data>());
    return &dataPtr.b;
}

Data* getBaseDataPtrFromString(std::string* mStringMember) {
    // ???
}

int main() {
    std::string* stringPtr(allocateDataAndGetString());
    Data* dataPtr(getBaseDataPtrFromString
}

I have a Data instance allocated on the heap, and a pointer to its std::string b; member. How do I get the base address of the Data instance the string is a member of, taking into account offsets and padding, in a standard way?

I've tried subtracting sizeof(int) and std::offsetof(Data, std::string) from the std::string* pointer, but I couldn't get it to work.


回答1:


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.




回答2:


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))



回答3:


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);
}



回答4:


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.



来源:https://stackoverflow.com/questions/21909837/standard-way-to-find-base-address-of-struct-from-a-member

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