从指向某个成员的指针获取指向对象的指针

时间:2022-05-01 21:40:36

Suppose there's a structure

假设有一个结构

struct Thing {
  int a;
  bool b;
};

and I get a pointer to member b of that structure, say as parameter of some function:

我得到一个指向该结构的成员b的指针,作为某个函数的参数:

void some_function (bool * ptr) {
  Thing * thing = /* ?? */;
}

How do I get a pointer to the containing object? Most importantly: Without violating some rule in the standard, that is I want standard defined behaviour, not undefined nor implementation defined behaviour.

如何获取指向包含对象的指针?最重要的是:如果不违反标准中的某些规则,那就是我需要标准定义的行为,而不是未定义的行为,也不是实现定义的行为。

As side note: I know that this circumvents type safety.

作为旁注:我知道这可以避免类型安全。

4 个解决方案

#1


15  

If you are sure that the pointer is really pointing to the member b in the structure, like if someone did

如果你确定指针真的指向结构中的成员b,就像有人做的那样

Thing t;
some_function(&t.b);

Then you should be able to use the offsetof macro to get a pointer to the structure:

然后你应该能够使用offsetof宏来获得指向结构的指针:

std::size_t offset = offsetof(Thing, b);
Thing* thing = reinterpret_cast<Thing*>(reinterpret_cast<int8_t*>(ptr) - offset);

Note that if the pointer ptr doesn't actually point to the Thing::b member, then the above code will lead to undefined behavior if you use the pointer thing.

请注意,如果指针ptr实际上没有指向Thing :: b成员,那么如果使用指针,上面的代码将导致未定义的行为。

#2


4  

void some_function (bool * ptr) {
  Thing * thing = (Thing*)(((char*)ptr) - offsetof(Thing,b));
}

I think there is no UB.

我认为没有UB。

#3


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

First, we create some static storage that could hold an X. Then we get the address of the X object that could exist in the buffer, and the address of the b element of that object.

首先,我们创建一个可以容纳X的静态存储。然后我们得到缓冲区中可能存在的X对象的地址,以及该对象的b元素的地址。

Casting back to char*, we can thus get the offset of the bool within the buffer, which we can then use to adjust a pointer to a real bool back to a pointer to the containing X.

回到char *,我们可以得到缓冲区中bool的偏移量,然后我们可以使用它来调整指向真实bool的指针,返回指向包含X的指针。

#4


1  

My proposal is derived from the @Rod answer in Offset from member pointer without temporary instance, and the similar @0xbadf00d's one in Offset of pointer to member.

我的建议来自Offset中的@Rod答案,没有临时实例的成员指针,以及类似的@ 0xbadf00d在指向成员的指针的偏移量中的一个。

I started imagining a form of offset driving the implementation of a pointer to a class data member, later confirmed by the post in question and the tests i've made.

我开始想象一种偏移形式驱动指向类数据成员的指针的实现,稍后由相关帖子和我所做的测试确认。

I'm not a C++ practitioner so sorry for the brevity.

我不是C ++从业者,为了简洁而感到抱歉。

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

#1


15  

If you are sure that the pointer is really pointing to the member b in the structure, like if someone did

如果你确定指针真的指向结构中的成员b,就像有人做的那样

Thing t;
some_function(&t.b);

Then you should be able to use the offsetof macro to get a pointer to the structure:

然后你应该能够使用offsetof宏来获得指向结构的指针:

std::size_t offset = offsetof(Thing, b);
Thing* thing = reinterpret_cast<Thing*>(reinterpret_cast<int8_t*>(ptr) - offset);

Note that if the pointer ptr doesn't actually point to the Thing::b member, then the above code will lead to undefined behavior if you use the pointer thing.

请注意,如果指针ptr实际上没有指向Thing :: b成员,那么如果使用指针,上面的代码将导致未定义的行为。

#2


4  

void some_function (bool * ptr) {
  Thing * thing = (Thing*)(((char*)ptr) - offsetof(Thing,b));
}

I think there is no UB.

我认为没有UB。

#3


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

First, we create some static storage that could hold an X. Then we get the address of the X object that could exist in the buffer, and the address of the b element of that object.

首先,我们创建一个可以容纳X的静态存储。然后我们得到缓冲区中可能存在的X对象的地址,以及该对象的b元素的地址。

Casting back to char*, we can thus get the offset of the bool within the buffer, which we can then use to adjust a pointer to a real bool back to a pointer to the containing X.

回到char *,我们可以得到缓冲区中bool的偏移量,然后我们可以使用它来调整指向真实bool的指针,返回指向包含X的指针。

#4


1  

My proposal is derived from the @Rod answer in Offset from member pointer without temporary instance, and the similar @0xbadf00d's one in Offset of pointer to member.

我的建议来自Offset中的@Rod答案,没有临时实例的成员指针,以及类似的@ 0xbadf00d在指向成员的指针的偏移量中的一个。

I started imagining a form of offset driving the implementation of a pointer to a class data member, later confirmed by the post in question and the tests i've made.

我开始想象一种偏移形式驱动指向类数据成员的指针的实现,稍后由相关帖子和我所做的测试确认。

I'm not a C++ practitioner so sorry for the brevity.

我不是C ++从业者,为了简洁而感到抱歉。

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