Стандартный способ найти базовый адрес структуры от члена

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* указатель, но я не мог заставить его работать.

2

Решение

использование 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.

5

Другие решения

offsetof дает смещение в символах, поэтому вам нужно привести к mStringMember в char * перед выполнением арифметики указателя.

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

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

Если вы уверены, что некоторые 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 макрос (особенно в более общих случаях, чем требуется стандартом). Увидеть этот вопрос.

1
По вопросам рекламы [email protected]