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
}
у меня есть Data
экземпляр, выделенный в куче, и указатель на его std::string b;
член. Как мне получить базовый адрес Data
Например, является ли строка строкой стандартным образом с учетом смещений и отступов?
Я пытался вычесть sizeof(int)
а также std::offsetof(Data, std::string)
от std::string*
указатель, но я не мог заставить его работать.
использование offsetof
от <cstddef>
, но будьте осторожны, он определен только для стандартных типов макетов (Жить в Колиру):
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
подробно описано в C ++ 11 18.2 / 4:
Макрос
offsetof
(тип, член-указатель) принимает ограниченный набор аргументов типа в этом международном стандарте. Если тип не является классом стандартного макета (раздел 9), результаты не определены.195 Выражениеoffsetof
(тип, член-указатель) никогда не зависит от типа (14.6.2.2) и зависит от значения (14.6.2.3) тогда и только тогда, когда тип зависит. Результат примененияoffsetof
макрос в поле, которое является статическим членом данных или членом функции, не определено. Нет операции, вызваннойoffsetof
макрос должен выбросить исключение иnoexcept(offsetof(type, member-designator))
должен бытьtrue
,
и C99 (N1256) 7,17 / 3:
Макросы
NULL
которая расширяется до определенной в реализации постоянной нулевого указателя; а также
offsetof(type, member-designator)
который расширяется до целочисленного константного выражения, имеющего тип
size_t
, значение которого является смещением в байтах к элементу структуры (обозначается как член-указатель), с начала его структуры (обозначается тип). Тип и обозначение элемента должны быть такими, чтобыstatic type t;
тогда выражение
&(t.
член-указатель)
оценивает адресную константу. (Если указанный член является битовым полем, поведение не определено.)
«Ограниченный набор аргументов типа в этом международном стандарте» в стандарте C ++ призван обратить ваше внимание на тот факт, что offsetof
является более строгим, чем в случае стандарта C.
offsetof
дает смещение в символах, поэтому вам нужно привести к mStringMember
в char *
перед выполнением арифметики указателя.
(Data*)((char*)mStringMember - offsetof(Data, b))
offsetof
работает только на стандартных типах.
Есть хитрость, которую вы можете использовать, и она не требует стандартной компоновки: static_cast
знает, как добраться до более производного объекта по указателю на одну из его баз.
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);
}
Если вы уверены, что некоторые ptr
на самом деле адрес некоторых s->b
(что не всегда верно) вы можете попробовать использовать offsetof:
Data* getBaseDataPtrFromString(std::string* ptr) {
void* ad = (char*)ptr - offsetof(Data,b);
return reinterpret_cast<Data*>(ad);
}
Кстати, GCC имеет builtin_offsetof чтобы помочь в реализации offsetof
макрос (особенно в более общих случаях, чем требуется стандартом). Увидеть этот вопрос.