对象方法的真实地址

True address of object's method

本文关键字:实地址 真实 方法 对象      更新时间:2023-10-16

我知道我可以创建方法指针,并且我知道它们通常与函数指针不同。不允许在它们之间进行转换。方法指针可以包含有关如何调整指针等this大量数据。

我想知道如何获取正在执行给定方法的代码的实际地址。此地址不会以任何方式被尊重,但将用作给定方法所属类型的标识符 - 有点像 RTTI,具有对库边界不敏感的优点,因为此代码通常仅在一个单元中可用。

编辑:

为什么我不添加一些返回对象类型的getType()方法?

因为我希望能够以这种方式不仅使用我创建的类。我正在尝试做的基本上是我对variant类的实现,它基本上可以接受所有内容,前提是存在给定类型的 void* getVariantId(( 的专用化。

所以我可以写:

template <typename T>
class getTypeId{
};
class foo{
    public:
    void bar();
};
// getTypeId declared but not defined for generic type T earlier...
template <>
class getTypeId<foo>{
    static void* get<foo>(){
        return &foo::bar;
    }
};
void doSomething(myVariant var);
int main(int argc, char* argv[]){
     foo f;
     myVariant var = myVariant::fromType<foo*>(&f);
     doSomething(f);
}
void doSomething(myVariant var){
    foo* f = var.toType<foo*>(); // Returns foo object from main
    int* i = var.toType<int*>(); // returns null, as var is not int* in my example.
}

这个想法是fromType使用getTypeId来获取类型的表示值,并将其与投射到void*的对象指针一起进行缩放。 另一方面,toType比较从'getTypeId::get获得的值和一个存储在对象中的值 - 如果它匹配,那么内部保留的对象指针将被重新解释回原始类型并返回。

这个解决方案的优点是,即使有一些共享库 X,定义了类型 x,那么单独使用库 X 的库 Y 和 Z 也会同意类型 x 是相同的(例如,如果 Y 中的变量创建被传递给 Z(,因为 X::method 上的地址保持不变。作为 Y 或 Z 库(但不是 X!!(的创建者,我不需要确保 X lib 启用了 RTTI,甚至不知道 myVariant 的存在,但 x 类型仍然完全兼容 myVariant 。

编辑2:

我现在看到没有独立于实现的方法来完成这项工作。但是,我认为这是不寻常需要的怪癖,因此不存在的功能,而不是无法创建的功能。我想我还没有明确我想要实现的目标以及我是如何计划的。所以:

  • 我所说的库是指这里的共享库(.dll,.so等(。

  • 每个
  • 非纯虚拟方法(意味着定义的每个方法(都必须具有实现。此实现的行为类似于接受一个附加参数this的函数 - 虚拟函数仅在调用方端不同。让我们以类 X 及其方法 X::foo 为例。此方法仅绑定到 X 类型,即使类型 Y 继承了它,此方法也保持X::foo,因此足以识别类型 X 。覆盖子类Xchild中的虚拟方法或覆盖方法实际上是使用新地址定义新方法 Xchild::foo。然后你可以用Xchild::foo来表示Xchild,但不能用X

  • 如果类型 X(所有准确的方法(都是在库 libX 中定义的,并且
  • libY 和 libZ 都使用 libX(但彼此不知道(,并且为了自己的目的定义 getTypeId 使用 X 中的第一个方法来表示它(请注意,这不会对 libX 开发人员施加任何影响(,那么他们对 X 类型是一致的。如果应用程序开发人员使用 libY 获取变体并将其传递给 libZ,则两者都将正确识别提到的类型。

  • 我不是在尝试开发类型转换变体 - 保存为 X 的变体只能读取为 X,即使传递给 myVariant 的实际指针是 Xchild。我对获取给定实例的最顶层类的类型不感兴趣 - 只是用于创建变体的类。

C++ FAQ Lite 用一整节专门介绍指向成员函数的指针。

有两个答案特别说明了为什么您尝试做的事情无法完成(至少不是以便携式方式(:

  • 指针到成员函数是一种不同的动物:

C++引入了一种新型指针,称为指向成员的指针, 只能通过提供对象来调用。不要尝试 将指向成员的指针"转换为指向函数的指针;这 结果是不确定的,可能是灾难性的。例如,一个 指针到成员函数不需要包含计算机 相应函数的地址。

  • 指针到成员函数可以指向虚函数:

指向成员函数的指针可能是数据结构,而不是 单指针。想一想:如果它指向一个虚拟的 函数,它实际上可能并不指向静态可解析的 一堆代码,所以它甚至可能不是一个普通的地址。

我想知道如何获取正在执行给定方法的代码的实际地址。此地址不会以任何方式被尊重,但将用作给定方法所属类型的标识符 - 有点像 RTTI,具有对库边界不敏感的优点,因为此代码通常仅在一个单元中可用。

RTTI不受翻译单位的限制。RTTI 围绕库的唯一限制是 DLL 边界,其中一个 DLL 将公开返回派生类的函数。而且我不是 100% 确定,但只要 DLL 和可执行文件都是使用相同的编译器构建的(并共享静态库(,我认为它仍然可以工作。

无论如何,你所要求的事情是无法做到的。成员指针不是指针,并且没有可移植的方法来获取指向成员函数的实际函数指针。

即使可以,它也不会为您提供类型的唯一标识符。毕竟,如果一个类不重写函数,或者如果函数不是虚拟的,那么该成员函数的地址对于使用它的每个类都是相同的。仅当函数是虚拟的并且类覆盖它时,它才会更改。

没有办法从中制作一个唯一的类型标识符。

在我看来,你想要的是 Boost.Any,它在内部使用 RTTI 来防止你投射到错误的类型。


您的计划崩溃的地方

您正在提出一个getTypeId函数,该函数将像这样实现:

template<typename T>
SomeIdentifierTypedef getTypeId(const T &x)
{
  void *ptr = GetMemberFuncPtr<T>(x, &x::foo);
  return FindIdFromPtr(ptr);
}

这需要存在 GetMemberFuncPtr ,它接受一个成员指针和一个对象,并获取该成员的实际函数指针。此函数旨在为它收到的每个类型返回一个唯一值。

好的,现在考虑一下你说的话:

让我们以类 X 和它的方法 X::foo 为例。此方法仅绑定到 X 类型,即使类型 Y 继承了它,此方法仍保留为 X::foo,因此足以标识类型 X。

如果Y::fooX::foo是相同的成员函数,那么它们自然具有相同的函数指针。因此,getTypeId(Y()) == getTypeId(X()).这违反了getTypeId的既定目标。如果类型为 Y,则希望它返回的值与类型 X 时的值不同

如果类型 X(所有准确的方法(都是在库 libX 中定义的,并且

libY 和 libZ 都使用 libX(但彼此不知道(,并且为了自己的目的定义 getTypeId 使用 X 中的第一个方法来表示它(请注意,这不会对 libX 开发人员施加任何影响(,那么他们对 X 类型是一致的。如果应用程序开发人员使用 libY 获取变体并将其传递给 libZ,则两者都将正确识别提到的类型。

这行不通,原因有二。首先,请注意,我对getTypeId的实现::foo其中。那是因为没有办法谈论类的任意方法。你必须使用一个名称(如果你想确定的话,可能是一个完整的签名(来谈论一个特定的功能。你不能只是说"从这个类型中给我一些功能"。你必须选择一个。

这意味着您必须为所有可能的模板类型选择相同的getTypeId。因此,使用 getTypeId 的所有类都必须实现相同的函数。

哦,您可以尝试将其专门用于某些类型。但这直接涉及第二个问题。

共享

库不共享函数,除非它们被显式共享。您不能共享X::foolibX传递libY libY没有完整和单独定义的类的唯一方法是该类以某种方式隐藏。所以要么它是一个不透明的指针(void*(,在这种情况下,你甚至无法获得它的成员函数。或者它是从libXlibY都有的定义派生的类。

也就是说,有一些(可能是抽象的(类baselibXlibY都是针对它们编译的。 libX创建从base派生的X,并且libX公开了一些函数,该函数返回X作为base *

在这种情况下,专业化无济于事,因为只有libX才能访问专业化getTypeId<X>。它工作的唯一方法是如果专业化是针对getTypeId<base>的。但是,我们又遇到了一个问题,如果用户不覆盖虚拟函数怎么办?然后我们认为它仍然是一个base.

即使测试函数是纯虚函数,也无济于事。这是因为libX可以有一个派生自X派生自baseYX可能已经实现了该功能,但Y没有覆盖它。因此再次getTypeId(x) == getTypeId(y)

你的想法行不通。

如果你只想从彼此推断类型(所以基本上创建自定义RTTI(,你可以创建一个返回某种类型标识符的方法。

class MyFirst
{
public:
    inline virtual string GetType() { return "Myfirst"; }
};

然后为您创建的每个类重载 GetType(( 函数。

这通常是最简单的方法(当然,除了dynamic_cast(。如果你真的想从虚拟调用中获取对象的类型,你可以设置一个静态标志来表示最后使用的类型,或者从该对象具有的每个函数中返回它。

如果你能写出你真正想要的"获取正在执行给定方法的代码的实际地址",你的问题肯定会更清楚。