Получить указатель на объект из указателя на некоторый член

Предположим, что существует структура

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";    
}