Assuming I have the runtime memory address of a function in an Application and I know the return type of said function, is it possible to call the function, knowing the functions return type, its arguments and calling convention, using a variadic template?
The templated function has to support both void and non-void return types. Due to the fact that we are dealing with function pointers, the compiler shouldn't complain, despite return ptr.
I thought about doing something like this:
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
}
Sadly the compiler throws the error C2440: It can't convert the Address "address
" to 'ReturnType (__cdecl *)(int,int)
'
I would really appreciate your help with this problem. I know i could just split this wrapper into 2 functions: one for void calls and one for non-void calls, but i hope there is a more elegant, template-supported solution.
Thank you and have a nice day!
the answer is yes, but doing it with variadic template is dangerous .
to enforce the compiler to cast the address to a function pointer you need to use reinterpret_cast
or a c cast .
note : you are improperly casting an integral address to a pointer because you are really trying to cast the address of the variable containing the address to the pointer not the address itself !
so this line :
function_caller<void>(&address, 1, 1); // Function with return type void.
should be :
function_caller<void>(address, 1, 1); // Function with return type void.
and always use the address type as uintptr_t
which will fit for any address available for the architecture (64 bit or 32)
but doing so with variadic template isn't safe at all . the reason is that the function have particular arguments type like this :
int fn(std::string& str, const char* ptr, uint64_t& i);
but when you cast with variadic template the compiler will deduce the types from the arguments passed however some conversions may be required !
so in your current version :
int i;
function_caller<int>(0x15216516, "str", "ptr", i);
the compile will assume that the function signature is something like :
int fn(const char*, const char*, int); // wrong types means stack corruptions and undefined behaviors
also see this :
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
so it is really reliable only to specify the whole function type and use that to cast the address like what boost.dll does