通过模板函数从其地址调用函数
Call a function from its address via a templated function
假设我有应用程序中函数的运行时内存地址,并且我知道所述函数的 return 类型,是否可以调用函数,知道函数 return 类型,它的参数和调用约定,使用可变参数模板?
模板函数必须同时支持 void 和非 void return 类型。由于我们正在处理函数指针,编译器不应该抱怨,尽管 return ptr.
我想过这样做:
template<typename ReturnType, typename Address, typename... Args>
ReturnType function_caller(Address address, Args... args)
{
ReturnType(*ptr)(Args...) = address;
return ptr(args...);
}
int main()
{
auto address = 0x100;
auto address2 = 0x200;
function_caller<void>(&address, 1, 1); // Function with return type void.
int result = function_caller<int>(&address2, 1, 2, 3.f, "hello");
// result should contain the int value we received by calling the function at 0x200
}
遗憾的是,编译器抛出错误 C2440:无法将地址“address
”转换为“ReturnType (__cdecl *)(int,int)
”
非常感谢你帮助解决这个问题。我知道我可以将这个包装器拆分为 2 个函数:一个用于 void 调用,一个用于非 void 调用,但我希望有一个更优雅、支持模板的解决方案。
谢谢你,祝你有愉快的一天!
答案是肯定的,但是使用可变参数模板这样做很危险。
要强制编译器将地址转换为函数指针,您需要使用 reinterpret_cast
或 c 转换。
注意:您不正确地将整数地址转换为指针,因为您实际上是在尝试将包含地址的变量地址转换为指针而不是地址本身!
所以这一行:
function_caller<void>(&address, 1, 1); // Function with return type void.
应该是:
function_caller<void>(address, 1, 1); // Function with return type void.
并始终使用地址类型 uintptr_t
,这将适合架构可用的任何地址(64 位或 32 位)
但是使用可变参数模板这样做一点也不安全。原因是该函数具有像这样的特定参数类型:
int fn(std::string& str, const char* ptr, uint64_t& i);
但是当您使用可变参数模板进行转换时,编译器将从传递的参数中推断出类型,但是可能需要进行一些转换!
因此在您当前的版本中:
int i;
function_caller<int>(0x15216516, "str", "ptr", i);
编译将假定函数签名类似于:
int fn(const char*, const char*, int); // wrong types means stack corruptions and undefined behaviors
也看到这个:
std::string to_string(std::string_view v);
function_caller<std::string>(0x15216516, "str"); // wrong the compiler won't convert the string literal for you and the function will end up with a dangling view
function_caller<std::string>(0x15216516, std::string("str")); // wrong again there is no conversion from std::string to std::string_view here
所以只指定整个函数类型并使用它来像 boost.dll 所做的那样转换地址是非常可靠的
假设我有应用程序中函数的运行时内存地址,并且我知道所述函数的 return 类型,是否可以调用函数,知道函数 return 类型,它的参数和调用约定,使用可变参数模板?
模板函数必须同时支持 void 和非 void return 类型。由于我们正在处理函数指针,编译器不应该抱怨,尽管 return ptr.
我想过这样做:
template<typename ReturnType, typename Address, typename... Args>
ReturnType function_caller(Address address, Args... args)
{
ReturnType(*ptr)(Args...) = address;
return ptr(args...);
}
int main()
{
auto address = 0x100;
auto address2 = 0x200;
function_caller<void>(&address, 1, 1); // Function with return type void.
int result = function_caller<int>(&address2, 1, 2, 3.f, "hello");
// result should contain the int value we received by calling the function at 0x200
}
遗憾的是,编译器抛出错误 C2440:无法将地址“address
”转换为“ReturnType (__cdecl *)(int,int)
”
非常感谢你帮助解决这个问题。我知道我可以将这个包装器拆分为 2 个函数:一个用于 void 调用,一个用于非 void 调用,但我希望有一个更优雅、支持模板的解决方案。
谢谢你,祝你有愉快的一天!
答案是肯定的,但是使用可变参数模板这样做很危险。
要强制编译器将地址转换为函数指针,您需要使用 reinterpret_cast
或 c 转换。
注意:您不正确地将整数地址转换为指针,因为您实际上是在尝试将包含地址的变量地址转换为指针而不是地址本身!
所以这一行:
function_caller<void>(&address, 1, 1); // Function with return type void.
应该是:
function_caller<void>(address, 1, 1); // Function with return type void.
并始终使用地址类型 uintptr_t
,这将适合架构可用的任何地址(64 位或 32 位)
但是使用可变参数模板这样做一点也不安全。原因是该函数具有像这样的特定参数类型:
int fn(std::string& str, const char* ptr, uint64_t& i);
但是当您使用可变参数模板进行转换时,编译器将从传递的参数中推断出类型,但是可能需要进行一些转换!
因此在您当前的版本中:
int i;
function_caller<int>(0x15216516, "str", "ptr", i);
编译将假定函数签名类似于:
int fn(const char*, const char*, int); // wrong types means stack corruptions and undefined behaviors
也看到这个:
std::string to_string(std::string_view v);
function_caller<std::string>(0x15216516, "str"); // wrong the compiler won't convert the string literal for you and the function will end up with a dangling view
function_caller<std::string>(0x15216516, std::string("str")); // wrong again there is no conversion from std::string to std::string_view here
所以只指定整个函数类型并使用它来像 boost.dll 所做的那样转换地址是非常可靠的