我正在寻找一种动态调用虚函数的解决方案。这意味着在运行时支持具有偏移和动态类型的函数调用。
我正在制作游戏插件(Counter-Strike:Source,如果你知道的话)。插件界面不太可能,所以你不能像大多数人想要的那样扩展。为了实现更好的沟通和操纵游戏的方式,我支持调用虚拟功能。例如,CCSPlayer类具有以下方法:
Offset Name
...
201 CCSPlayer::Ignite()
...
205 CCSPlayer::CommitSuicide(bool Blah)
...
250 CCSPlayer::SomeFunctionWith2Params(int A1, float A2)
将这些偏移量和指向该类实例的指针传递给我当前的函数(见下文),我可以调用这个函数:
CBasePlayer *pPlayer = Players.Find("mike");
bool bResult = Call2<bool, int, float>(210 /* offset */, pPlayer, 20, 50.0f);
我这样做是为了调用我无法通过正常例程调用的虚函数,因为编译器不知道派生的CCSPlayer类的结构。
现在我想添加一个带有动态虚函数调用的脚本语言。这意味着,脚本编写者可以设置参数的数量,以及它们的类型。然后将this-pointer和offset传递给函数,最后执行它。这是我真正的问题。
目前我只能通过使用模板并为每个参数量创建一个函数来对它们进行硬编码。例如:
template<typename T, typename A, typename B> T Call2(int iOffset, void *pThis, A arg1, B arg2)
{
void **pVTable = *(void***)pThis;
void *pPointer = pVTable[_iOffset];
union
{
T (CVCallEmpty::*pCall)(A, B);
void *pAddress;
} Data;
Data.pAddress = pPointer;
return (reinterpret_cast<CVCallEmpty*>(*(void***)&pThis)->*Data.pCall)(arg1, arg2);
}
现在,是否有可能支持动态调用?
答案 0 :(得分:3)
这是如此破碎,很难考虑扩展它。
您不处理虚拟继承。你甚至不处理多重继承。
虚函数不能表示为单个“偏移”整数。您需要接受指向成员的指针参数。
答案 1 :(得分:1)
答案 2 :(得分:0)
没有内置的方法来生成排列。我想,您可以使用一些库,但我建议您编写一个小程序来为您生成所有模板。这使您可以完全控制生成的源代码,那里没有无关的垃圾,并且很容易单步执行。
所提出的方法的一个问题是指向函数的参数类型是从提供的参数推导出来的;应该真正独立地指定参数类型。就目前而言,您可以(比方说)将浮动传递给期望int的函数,并获得无效结果(最多)。来电者必须非常精确地了解他们的价值观!如果您有源代码生成程序,您可以稍微调整它以生成转发函数,类似于:
inline void call_vii(int vi,void *obj,int a0,int a1) {
Call2<void,int,int>(vi,obj,a0,a1);
}
inline float call_viff(int vi,void *obj,int a0,float a1,float a2) {
Call2<void,int,float,float>(vi,obj,a0,a1,a2);
}
等等,无限期(或不远处)。
当然,这并不是非常方便。它会产生很多功能。但是如果你要将它暴露给脚本语言,那么无论如何你都需要为每个参数类型组合提供一个函数......