Получить указатель на объект из указателя на некоторый член
Предположим, что существует структура
struct Thing {
int a;
bool b;
};
и я получаю указатель на член b
этой структуры, например, как параметр некоторой функции:
void some_function (bool * ptr) {
Thing * thing = /* ?? */;
}
Как получить указатель на содержащий объект? Самое главное: не нарушая какое-либо правило в стандарте, то есть я хочу стандартное поведение, а не undefined или поведение, определенное реализацией.
В качестве побочного примечания: я знаю, что это обходит безопасность типов.
Ответы
Ответ 1
Если вы уверены, что указатель действительно указывает на элемент b
в структуре, например, если кто-то сделал
Thing t;
some_function(&t.b);
Затем вы можете использовать макрос offsetof
, чтобы получить указатель на структуру:
std::size_t offset = offsetof(Thing, b);
Thing* thing = reinterpret_cast<Thing*>(reinterpret_cast<int8_t*>(ptr) - offset);
Обратите внимание, что если указатель ptr
фактически не указывает на член Thing::b
, то приведенный выше код приведет к поведению undefined, если вы используете указатель thing
.
Ответ 2
void some_function (bool * ptr) {
Thing * thing = (Thing*)(((char*)ptr) - offsetof(Thing,b));
}
Я думаю, что нет UB.
Ответ 3
X* get_ptr(bool* b){
static typename std::aligned_storage<sizeof(X),alignof(X)>::type buffer;
X* p=static_cast<X*>(static_cast<void*>(&buffer));
ptrdiff_t const offset=static_cast<char*>(static_cast<void*>(&p->b))-static_cast<char*>(static_cast<void*>(&buffer));
return static_cast<X*>(static_cast<void*>(static_cast<char*>(static_cast<void*>(b))-offset));
}
Сначала мы создаем статическое хранилище, которое может содержать X
. Затем мы получаем адрес объекта X
, который может существовать в буфере, и адрес элемента b
этого объекта.
Возвращаясь к char*
, мы можем, таким образом, получить смещение bool
внутри буфера, которое затем мы можем использовать для настройки указателя на реальный bool
назад на указатель на содержащий X
.
Ответ 4
Мое предложение получено из ответа @Rod в Смещение от указателя участника без временного экземпляра и аналогичного @0xbadf00d в Смещение указателя на элемент.
Я начал воображать форму смещения, управляющую реализацией указателя на элемент данных класса, позже подтвержденный этой почтой и те тесты, которые я сделал.
Я не практикующий С++, поэтому жалею о краткости.
#include <iostream>
#include <cstddef>
using namespace std;
struct Thing {
int a;
bool b;
};
template<class T, typename U>
std::ptrdiff_t member_offset(U T::* mem)
{
return
( &reinterpret_cast<const char&>(
reinterpret_cast<const T*>( 1 )->*mem )
- reinterpret_cast<const char*>( 1 ) );
}
template<class T, typename U>
T* get_T_from_data_member_pointer (U * ptr, U T::*pU) {
return reinterpret_cast<T*> (
reinterpret_cast<char*>(ptr)
- member_offset(pU));
}
int main()
{
Thing thing;
thing.b = false;
bool * ptr = &thing.b;
bool Thing::*pb = &Thing::b;
std::cout << "Thing object address accessed from Thing test object lvalue; value is: "
<< &thing << "!\n";
std::cout << "Thing object address derived from pointer to class member; value is: "
<< get_T_from_data_member_pointer(ptr, &Thing::b) << "!\n";
}