#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*