我知道我可以创建方法指针,我知道它们通常与函数指针不同。不允许在它们之间进行转换。方法指针可以包含大量有关如何调整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。我对获取给定实例的最顶层类型不感兴趣 - 只是用于创建变体的类。
答案 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::foo
和X::foo
是相同的成员函数,那么它们自然会有相同的函数指针。因此,getTypeId(Y()) == getTypeId(X())
。这违反了getTypeId
的既定目标。如果类型为Y,则希望它返回不同的值,而不是X.
如果在库libX中定义类型X(它的所有方法都是准确的),并且libY和libZ都使用libX(但不知道彼此相互关联),并且为了它们自己的目的定义getTypeId使用X中的第一个方法代表它(请注意,这不会对libX开发人员施加任何限制),然后他们就X是什么类型达成一致。如果应用程序开发人员使用libY获取变量并将其传递给libZ,则两者都将正确识别所提及的类型。
这有两个原因无法解决。首先,请注意我的getTypeId
实现中包含::foo
。那是因为没有办法谈论一个类的任意方法。你必须使用一个名字(如果你想确定,可能是一个完整的签名),以便谈论一个特定的功能。你不能只是说“从这种类型中获取某些函数。”你必须选择一个。
这意味着您必须为getTypeId
的所有可能模板类型选择相同的。因此,您为使用getTypeId
的所有类必须实现相同的功能。
哦,你可以尝试将它专门用于某些类型。但这一直是第二个问题。
共享库不会共享功能,除非它们是明确共享的。您无法分享X::foo
。 libX
传递libY
一个libY
没有完整和单独定义的类的唯一方法是,如果以某种方式隐藏类。所以要么它是一个不透明的指针(void*
),在这种情况下你甚至不能获得成员函数。或者它是来自libX
和libY
都有的定义的派生类。
也就是说,有一些(可能是抽象的)类base
,其中libX
和libY
都是针对编译的。 libX
创建X
,该base
派生自libX
,并且有一些X
公开的函数会将base *
作为libX
返回。< / p>
在这种情况下,专业化没有帮助,因为只有getTypeId<X>
才能访问专业化getTypeId<base>
。它的唯一工作方式是专业化是base
。但是,如果用户没有覆盖虚拟功能,我们会重新讨论该怎么办?然后我们认为它仍然是libX
。
即使测试功能是纯虚函数,也无济于事。这是因为Y
可能有X
,该base
派生自X
,Y
派生自getTypeId(x) == getTypeId(y)
。 {{1}}可能已实现此功能,但{{1}}未覆盖该功能。因此再次{{1}}。
你的想法无效。
答案 2 :(得分:0)
如果您只想相互推断类型(所以基本上创建自定义RTTI),您可以创建一个返回某种类型标识符的方法。
class MyFirst
{
public:
inline virtual string GetType() { return "Myfirst"; }
};
然后为您创建的每个类重载GetType()函数。
这通常是最简单的方法(当然,除了dynamic_cast之外)。 如果你真的想从虚拟调用中获取对象的类型,你可以设置一个表示最后使用类型的静态标志,或者从所述对象所具有的每个函数返回它。
如果您可以写下你真的想“获取正在执行给定方法的代码的实际地址”,那么你的问题肯定会更清楚。