在运行时创建一个具有唯一函数指针的函数
Create a function with unique function pointer in runtime
当调用以回调为参数的WinAPI函数时,通常会有一个特殊的参数将一些任意数据传递给回调。如果没有这样的东西(例如SetWinEventHook
),我们可以理解哪一个API调用导致了给定回调的调用的唯一方法是具有不同的回调。当我们知道在编译时调用给定API的所有情况时,我们总是可以使用静态方法创建一个类模板,并在不同的调用方使用不同的模板参数对其进行实例化。这真是一项艰巨的工作,我不喜欢这样做。
如何在运行时创建回调函数,使它们具有不同的函数指针?
我看到了一个使用运行时程序集生成的解决方案(抱歉,用俄语),但它不能在x86/x64体系结构之间移植。
您可以使用libffi的闭包API。它允许您创建具有不同地址的蹦床。我在这里实现了一个包装类,尽管它还没有完成(只支持int
参数和返回类型,您可以专门化detail::type
以支持不仅仅是int
)。一个更重量级的替代方案是LLVM,不过如果您只处理C类型,那么libffi会做得很好。
我已经提出了这个应该是可移植的解决方案(但我还没有测试它):
#define ID_PATTERN 0x11223344
#define SIZE_OF_BLUEPRINT 128 // needs to be adopted if uniqueCallbackBlueprint is complex...
typedef int (__cdecl * UNIQUE_CALLBACK)(int arg);
/* blueprint for unique callback function */
int uniqueCallbackBlueprint(int arg)
{
int id = ID_PATTERN;
printf("%x: Hello unique callback (arg=%d)...n", id, arg);
return (id);
}
/* create a new unique callback */
UNIQUE_CALLBACK createUniqueCallback(int id)
{
UNIQUE_CALLBACK result = NULL;
char *pUniqueCallback;
char *pFunction;
int pattern = ID_PATTERN;
char *pPattern;
char *startOfId;
int i;
int patterns = 0;
pUniqueCallback = malloc(SIZE_OF_BLUEPRINT);
if (pUniqueCallback != NULL)
{
pFunction = (char *)uniqueCallbackBlueprint;
#if defined(_DEBUG)
pFunction += 0x256; // variable offset depending on debug information????
#endif /* _DEBUG */
memcpy(pUniqueCallback, pFunction, SIZE_OF_BLUEPRINT);
result = (UNIQUE_CALLBACK)pUniqueCallback;
/* replace ID_PATTERN with requested id */
pPattern = (char *)&pattern;
startOfId = NULL;
for (i = 0; i < SIZE_OF_BLUEPRINT; i++)
{
if (pUniqueCallback[i] == *pPattern)
{
if (pPattern == (char *)&pattern)
startOfId = &(pUniqueCallback[i]);
if (pPattern == ((char *)&pattern) + sizeof(int) - 1)
{
pPattern = (char *)&id;
for (i = 0; i < sizeof(int); i++)
{
*startOfId++ = *pPattern++;
}
patterns++;
break;
}
pPattern++;
}
else
{
pPattern = (char *)&pattern;
startOfId = NULL;
}
}
printf("%d pattern(s) replacedn", patterns);
if (patterns == 0)
{
free(pUniqueCallback);
result = NULL;
}
}
return (result);
}
用法如下:
int main(void)
{
UNIQUE_CALLBACK callback;
int id;
int i;
id = uniqueCallbackBlueprint(5);
printf(" -> id = %xn", id);
callback = createUniqueCallback(0x4711);
if (callback != NULL)
{
id = callback(25);
printf(" -> id = %xn", id);
}
id = uniqueCallbackBlueprint(15);
printf(" -> id = %xn", id);
getch();
return (0);
}
我注意到,如果使用调试信息(VisualStudio)进行编译,则会出现一种交互测试行为。通过pFunction = (char *)uniqueCallbackBlueprint;
获得的地址被断开了可变数量的字节。可以使用显示正确地址的调试器来获得差异。这个偏移量随构建而变化,我认为它与调试信息有关?这对于发布版本来说没有问题。因此,也许这应该被放入一个库中,该库被构建为"发布"。
另一件需要考虑的事情可能是pUniqueCallback
的字节对齐,这可能是一个问题。但是,将函数的开头与64位边界对齐并不难添加到该代码中。
在pUniqueCallback
中,您可以实现任何您想要的东西(注意更新SIZE_OF_BLUEPRINT,这样您就不会错过函数的尾部)。函数将被编译,生成的代码将在运行时重复使用。在创建唯一函数时,id的初始值会被替换,以便蓝图函数可以处理它。
- 什么..(省略号)作为函数原型中唯一的函数参数,C++?
- 将唯一指针的指针传递给采用双指针的函数
- 对带有唯一指针的 std::thread 使用类成员函数时出现编译错误
- 函数从唯一代码调用正确的子类方法
- 以下代码如何工作以每次为唯一调用堆栈唯一实例化模板函数?
- 使用唯一指针调用函数会使我的程序崩溃
- 字数统计函数在将单词添加到一组唯一单词时遇到问题
- 常量引用函数参数的地址何时唯一?
- 在创建对象向量时,不为每个对象唯一调用默认对象构造函数
- 获取模板函数/泛型 lambda 的唯一返回类型
- 如何在将唯一的_ptr传递给函数后使用
- 实例化 std::vector 的唯一元素,而无需复制构造函数
- 空的唯一指针在离开作用域时调用析构函数
- C (函数)模板将其返回其唯一参数而无需复制某些类型
- 通过成员函数将唯一 ptr 的向量附加到另一个向量
- 如何将唯一指针传递给函数中所需的特定参数
- C++14 不能调用从唯一指针继承的类的复制构造函数或运算符 =
- 析构函数是移动ctor/赋值的RHS上唯一调用过的东西吗
- 重构,其中唯一函数具有不同的参数
- 计算数组中唯一字符的函数