对象方法的真实地址

时间:2011-11-07 20:34:14

标签: c++ methods object-address

我知道我可以创建方法指针,我知道它们通常与函数指针不同。不允许在它们之间进行转换。方法指针可以包含大量有关如何调整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 ::方法上的地址保持不变。作为Y或Z库的创建者(但不是X !!!),我不需要确保X lib已启用RTTI,甚至不知道myVariant存在,但x类仍然完全兼容myVariant。

EDIT2:

我现在看到,没有与实现无关的方法来完成这项工作。但是我认为这是不常见的,因此不存在的功能,而不是不可能创建的功能的怪癖。 我想我还没有说清楚我想要实现什么,以及我是如何计划这样做的。所以:

  • 库我的意思是共享库(.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。我对获取给定实例的最顶层类型不感兴趣 - 只是用于创建变体的类。

3 个答案:

答案 0 :(得分:3)

C ++ FAQ Lite将整个部分用于Pointers to Member Functions

两个答案特别说明了为什么你要做的事情无法完成(至少,不是以便携方式):

  • 指向成员的功能是一种不同的动物:
  

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

  • 指向成员的功能可以指向虚拟功能:
  

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

答案 1 :(得分:1)

  

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

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

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

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

没有办法制作一个独特的类型标识符。

在我看来,你想要的是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.

  

如果在库libX中定义类型X(它的所有方法都是准确的),并且libY和libZ都使用libX(但不知道彼此相互关联),并且为了它们自己的目的定义getTypeId使用X中的第一个方法代表它(请注意,这不会对libX开发人员施加任何限制),然后他们就X是什么类型达成一致。如果应用程序开发人员使用libY获取变量并将其传递给libZ,则两者都将正确识别所提及的类型。

这有两个原因无法解决。首先,请注意我的getTypeId实现中包含::foo。那是因为没有办法谈论一个类的任意方法。你必须使用一个名字(如果你想确定,可能是一个完整的签名),以便谈论一个特定的功能。你不能只是说“从这种类型中获取某些函数。”你必须选择一个。

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

哦,你可以尝试将它专门用于某些类型。但这一直是第二个问题。

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

也就是说,有一些(可能是抽象的)类base,其中libXlibY都是针对编译的。 libX创建X,该base派生自libX,并且有一些X公开的函数会将base *作为libX返回。< / p>

在这种情况下,专业化没有帮助,因为只有getTypeId<X>才能访问专业化getTypeId<base>。它的唯一工作方式是专业化是base。但是,如果用户没有覆盖虚拟功能,我们会重新讨论该怎么办?然后我们认为它仍然是libX

即使测试功能是纯虚函数,也无济于事。这是因为Y可能有X,该base派生自XY派生自getTypeId(x) == getTypeId(y)。 {{1}}可能已实现此功能,但{{1}}未覆盖该功能。因此再次{{1}}。

你的想法无效。

答案 2 :(得分:0)

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

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

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

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

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