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.
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 expressionoffsetof
(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 theoffsetof
macro to a field that is a static data member or a function member is undefined. No operation invoked by theoffsetof
macro shall throw an exception andnoexcept(offsetof(type, member-designator))
shall betrue
.
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 givenstatic 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);
}
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