【问题标题】:Is it possible to convert regular pointer to pointer to class field是否可以将常规指针转换为指向类字段的指针
【发布时间】:2012-09-17 20:13:54
【问题描述】:

给定一个类实例和一个指向字段的指针,我们可以获得指向该类实例的字段变量的常规指针 - 就像下面代码中的最后一个赋值一样;

class A
{
public:
    int i, j;
};
int main(){
    A a;
    int A::*p = &A::i;
    int* r = &(a.*p); // r now points to a.i;
}

是否可以反转这种转换:给定类实例A a;int* r 获得int A::* p(如果给定的指针不在给定的实例中,则为NULL ptr),如代码所示:

class A
{
public:
    int i, j;
};
int main(){
    A a;
    int A::*p = &A::i;
    int* r = &(a.*p); // r now points to a.i;
    int A::*s = // a--->r -how to extract r back to member pointer?
}

我能想到的唯一方法是编写一个函数,该函数采用 A 的每个已知字段,计算给定实例的地址并与给定地址进行比较。然而,这需要为每个类编写自定义代码,并且可能难以管理。它的性能也不理想。

我可以想象这种转换可以由编译器在我知道的所有实现下的几个操作中完成 - 这种指针通常只是结构中的一个偏移量,所以它只是一个减法和范围检查,看看给定的指针是否真的在这个类存储。虚拟基类增加了一些复杂性,但我认为没有什么编译器无法处理。然而,似乎因为它不是标准所要求的(是吗?)没有编译器供应商关心。

还是我错了,这种转换存在一些根本问题?

编辑:

我发现对我所询问的内容存在一些误解。简而言之,我问的是:

  • 已经有一些实现(我的意思是在编译器级别),但由于几乎没有人使用它,几乎没有人知道它。
  • 标准中没有提到它,也没有编译器供应商提到它,但原则上它是可以实现的(再一次:由编译器,而不是编译的代码。)
  • 这样的操作存在一些深层次的问题,我错过了。

我的问题是 - 其中哪一个是真的?如果是最后一个 - 潜在的问题是什么?

我不是在寻求解决方法。

【问题讨论】:

    标签: c++ pointers type-conversion field


    【解决方案1】:

    没有跨平台的方式可以做到这一点。指向成员值的指针通常实现为指向对象开始的偏移量。利用这一事实,我做了这个(在 VS 中工作,没有尝试过其他任何东西):

    class A
    {
    public:
        int i, j;
    };
    
    int main()
    {
        A a;
        int A::*p = &A::i;
        int* r = &(a.*p); // r now points to a.i;
    
        union
        {
            std::ptrdiff_t offset;
            int A::*s;
        };
    
        offset = r - reinterpret_cast<int*>(&a);
        a.*s = 7;
        std::cout << a.i << '\n';
    }
    

    【讨论】:

    • 虚拟基类怎么样?
    • 你不能;唯一可以是编译器供应商 - 我的问题是:编译器供应商是否有一些典型(但相当未知)的方式来实现它,还是根本不可能?
    【解决方案2】:

    AFAIK C++ 不提供完全反射,您需要这样做。

    一种解决方案是自己提供反射(您描述的方式是一种方式,它可能不是最好的,但它会起作用)。

    一个完全不可移植的解决方案是找到可执行文件并使用它可能包含的任何调试信息。显然不可移植,并且需要调试信息开始。

    http://www.garret.ru/cppreflection/docs/reflect.html 的介绍部分对反射问题和不同的可能解决方法有很好的描述

    编辑:

    正如我在上面所写的,没有可移植的通用解决方案。但是可能有一种非常不可移植的方法。我没有在这里给你一个实现,因为我目前没有 C++ 编译器来测试它,但我会描述这个想法。

    基础是 Dave 在他的回答中所做的:利用指向成员的指针通常只是一个偏移量这一事实。问题在于基类(尤其是虚拟和多重继承)。您可以使用模板来处理它。您可以使用动态转换来获取指向基类的指针。并最终将该指针与原始指针进行比较以找出基址的偏移量。

    【讨论】:

    • 对反射问题的描述非常好,但在我的情况下,提出的解决方案(除了让程序员自己做)会有点过头了。因为无论如何我都在使用 gcc,我可能会为其添加一些补丁,但它不太可能会变得普遍使用。由于我想发布我的资源,所以我不能使用这样的补丁。
    • 好吧,正如我所说,您需要完全反思,而完全反思不是语言的一部分(该语言仅提供有限的反思,请参阅stackoverflow.com/questions/41453/… 了解这一点)。正如该分析所说,解决方案并不多。要么用户提供元数据,要么您基本上需要在语言级别(解析器、预处理器、编译器等)工作。 LLVM 预处理器可能会有所帮助,但它仍然会很复杂。正如你所说的过度杀戮,可能是唯一的杀戮。
    • 我在答案中添加了一个想法。这可能会也可能不会。我这里没有编译器,所以这只是一个供您探索的想法,而不是真正完整的答案。当然它不会是便携式的。
    • 它不能解决虚拟基类的问题,因为在这种情况下成员指针不能是简单的偏移量。另请参阅我的编辑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-12
    • 2014-07-22
    • 2011-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多