【问题标题】:Call a function from its address via a templated function通过模板函数从其地址调用函数
【发布时间】:2020-08-25 19:35:00
【问题描述】:

假设我有应用程序中函数的运行时内存地址并且我知道所述函数的返回类型,是否可以使用可变参数模板调用函数,知道函数的返回类型、参数和调用约定?

模板化函数必须同时支持 void 和非 void 返回类型。由于我们正在处理函数指针,编译器不应该抱怨,尽管返回 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 调用,但我希望有一个更优雅、支持模板的解决方案。

谢谢你,祝你有美好的一天!

【问题讨论】:

  • 是的,我知道,地址不是真实地址,只是为了展示概念。这不是我将在运行时使用的代码。请假设地址是正确的。除此之外,编译器不会介意,因为这是运行时错误,而不是编译时间
  • 自动扣除参数也很危险。第四个参数是const char* 还是const char[6]?我很确定是后者。
  • 如果你愿意为 void 调用使​​用奇怪的语法,你也可以哄它来推断返回类型。 coliru.stacked-crooked.com/a/42c1c60bf0e2f50a

标签: c++ templates variadic-templates


【解决方案1】:

答案是肯定的,但是使用可变参数模板是危险的。

要强制编译器将地址转换为函数指针,您需要使用 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 所做的那样是非常可靠的

【讨论】:

  • 不,这两个参数将是const char[4],而不是const char*
猜你喜欢
  • 1970-01-01
  • 2013-10-10
  • 1970-01-01
  • 2019-03-25
  • 1970-01-01
  • 1970-01-01
  • 2016-05-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多