Вызов функции по ее адресу через шаблонную функцию

#c #templates #variadic-templates

#c #шаблоны #переменные-шаблоны

Вопрос:

Предполагая, что у меня есть адрес памяти во время выполнения функции в приложении, и я знаю тип возвращаемой указанной функции, возможно ли вызвать функцию, зная тип возвращаемой функции, ее аргументы и соглашение о вызове, используя вариационный шаблон?

Шаблонная функция должна поддерживать как пустые, так и не пустые возвращаемые типы. Из-за того, что мы имеем дело с указателями на функции, компилятор не должен жаловаться, несмотря на возврат 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>(amp;address, 1, 1); // Function with return type void.
    int result = function_caller<int>(amp;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 функции: одну для недействительных вызовов и одну для недействительных вызовов, но я надеюсь, что есть более элегантное решение, поддерживаемое шаблоном.

Спасибо и хорошего дня!

Комментарии:

1. Да, я знаю, адреса не являются реальными адресами и предназначены только для демонстрации концепции. Это не тот код, который я бы использовал во время выполнения. Пожалуйста, просто предположите, что адрес правильный. Кроме того, компилятор не будет возражать, поскольку это ошибка времени выполнения, а не время компиляции

2. Автоматический вывод параметров также опасен. Это четвертый параметр const char* или нет const char[6] ? Я почти уверен, что это последнее.

3. Если вы хотите использовать странный синтаксис для вызовов void, вы также можете заставить его определять возвращаемые типы. coliru.stacked-crooked.com/a/42c1c60bf0e2f50a

Ответ №1:

ответ — да, но делать это с помощью шаблона variadic опасно.

чтобы заставить компилятор преобразовать адрес в указатель на функцию, вам нужно использовать reinterpret_cast или приведение c.

примечание: вы неправильно преобразуете интегральный адрес в указатель, потому что на самом деле пытаетесь преобразовать адрес переменной, содержащей адрес, в указатель, а не в сам адрес!

итак, эта строка :

 function_caller<void>(amp;address, 1, 1); // Function with return type void.
  

должно быть :

 function_caller<void>(address, 1, 1); // Function with return type void.
  

и всегда используйте тип адреса as uintptr_t , который подойдет для любого адреса, доступного для архитектуры (64-разрядный или 32)

но делать это с переменным шаблоном совсем небезопасно. причина в том, что функция имеет определенный тип аргументов, подобный этому :

 int fn(std::stringamp; str, const char* ptr, uint64_tamp; i);
  

но когда вы приводите с помощью шаблона variadic, компилятор выводит типы из переданных аргументов, однако могут потребоваться некоторые преобразования!

итак, в вашей текущей версии :

 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

  

таким образом, действительно надежно только указать весь тип функции и использовать его для приведения адреса, например, what boost.dll выполняет

Комментарии:

1. Нет, два параметра будут const char[4] , не const char*