3

我知道我可以创建方法指针,而且我知道它们通常不同于函数指针。不允许在它们之间转换。方法指针可以包含大量关于如何调整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 库启用了 RTTI,甚至不需要知道 myVariant 的存在,但类型 x 仍然完全兼容 myVariant。

编辑2:

我现在看到没有独立于实现的方法来完成这项工作。但是,我认为这是不通常需要的怪癖,因此不存在的功能,而不是不可能创建的功能。我想我仍然没有明确我想要实现什么以及我打算如何做到这一点。所以:

  • 这里的库是指共享库(.dll、.so 等)。

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

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

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

4

3 回答 3

3

C++ FAQ Lite 用一整节来讨论指向成员函数的指针

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

  • 指向成员函数的指针是另一种动物:

C++ 引入了一种新的指针类型,称为指向成员的指针,它只能通过提供对象来调用。不要试图将指向成员函数的指针“转换”为指向函数的指针;结果是不确定的,可能是灾难性的。例如,指向成员函数的指针不需要包含适当函数的机器地址。

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

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

于 2011-11-07T22:41:39.510 回答
1

我想知道如何获取正在执行给定方法的代码的实际地址。此地址不会以任何方式被引用,但将用作给定方法所属类型的标识符 - 有点像 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,则两者都将正确识别提到的类型。

这不能工作有两个原因。首先,请注意我对getTypeIdhas::foo的实现。那是因为没有办法谈论一个类的任意方法。你必须使用一个名字(如果你想确定的话,可能还有一个完整的签名)来谈论一个特定的功能。你不能只说“从这种类型中给我一些功能”。你必须选择一个。

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

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

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

也就是说,有一些(可能是抽象的) class base,它们都libXlibY编译了。libX创建X它派生自base,并且有一些函数libX公开它返回 aX作为 a base *

在这种情况下,专业化没有帮助,因为只能libX访问专业化getTypeId<X>。如果专业化是针对getTypeId<base>. 但是我们又遇到了一个问题,如果用户不覆盖虚函数怎么办?然后我们认为它仍然是一个base.

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

你的想法行不通。

于 2011-11-07T22:04:28.730 回答
0

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

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

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

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

如果您可以编写真正想要“获取执行给定方法的代码的实际地址”的方式,那么您的问题肯定会更清楚。

于 2011-11-07T21:13:29.107 回答