Search code examples
c++c++11structmemory-addressoffsetof

Standard way to find base address of struct from a member


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.


Solution

  • 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.