вызывая функцию, не зная заранее количество параметров - PullRequest
2 голосов
/ 26 марта 2012

Предположим, у меня есть dll с 2 функциями. Name of dll = "dll1" f1 (int a, int b, int c);

f2 (int a);

Моя программа будет принимать имя функции, имя dll и «список» параметров в качестве входных данных. Как бы я вызвать соответствующую функцию с соответствующими параметрами. то есть, если вход Dll1 f1 список (5,8,9)

это потребует от меня вызова f1 с 3 параметрами если вход был Dll1 f2 лист (8) это потребовало бы от меня вызова f2 с одним параметром как бы я вызвал функцию, не зная заранее количество параметров.

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

Ответы [ 4 ]

3 голосов
/ 26 марта 2012

Поскольку сгенерированный код отличается в зависимости от количества параметров, у вас есть два варианта: вы можете написать некоторый код на ассемблере для выполнения работы (в основном, просматривая список параметров и помещая каждый в стек перед вызовом функции) или вы можете создать что-то вроде массива указателей на функции, по одному для каждого количества параметров, которые вас интересуют (например, от 0 до 10). Большинство людей считают, что с последним гораздо проще иметь дело (хотя бы потому, что он вообще не использует язык ассемблера).

2 голосов
/ 26 марта 2012

Для решения проблемы в целом нужно знать:

  1. Соглашения о вызовах (эти stdcall , cdecl , fastcall , thiscall (кстати, последние два можно объединить в MSVC ++ ) и т. д.), которые управляют тем, как функции получают свои параметры (например, в специальных регистрах, в стеке, оба), как они возвращают значения (одинаковые) и что им разрешено удалять (например, некоторые регистры).
  2. Точные прототипы функций.

Вы можете найти все это только в символьной / отладочной информации, создаваемой компилятором, и (вероятно, в меньшей степени) в заголовочном файле, содержащем прототипы для функций в DLL. Есть одна проблема с заголовочным файлом. Если в нем не указано соглашение о вызовах, а функции были скомпилированы с соглашениями о вызовах, отличными от заданных по умолчанию (с помощью параметра компилятора), вам придется иметь дело с неоднозначностью. В любом случае вам нужно что-то проанализировать.

Если у вас нет этой информации, остается только один вариант - обратная инженерия DLL и / или ее пользователей.

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

И последнее, но не менее важное: вам нужен код для генерации значений параметров. Это наиболее тривиально для числовых типов (целые числа, числа с плавающей запятой и т. П.) И их массивов, а наиболее сложно для структур, объединений и классов. Создание последнего на лету может быть по меньшей мере таким же сложным, как и правильное использование функций. Не забывайте, что они могут ссылаться на другие объекты, используя указатели и ссылки.

Общая проблема разрешима, но не дешево. Гораздо проще решить несколько простых конкретных случаев и, возможно, полностью избежать проблемы, переписав функции так, чтобы они содержали менее переменные параметры, и только одно соглашение о вызовах ИЛИ, написав для этого функции-оболочки.

0 голосов
/ 26 марта 2012

Возможно, вы захотите проверить идиому именованного параметра .

Он использует метод цепочки , чтобы в основном выполнить то, что вы хотите.

Это решает проблему, когда вы знаете, как выглядит набор аргументов по умолчанию, но вам нужно настроить только некоторые из них, и не обязательно в порядке их объявления.

0 голосов
/ 26 марта 2012

Если ваши клиенты знают во время компиляции, то можете обернуть его следующим образом:

template<class Args...>
void CallFunctionPointer(void* pf, Args&&... args)
{
    typedef void(*FunctionType)(Args...);
    FunctionType* pf2 = (FunctionType*) pf;
    (*pf2)(forward<Args>(args)...);
}

Обратите внимание, если вы передаете неправильное количество параметров или неправильный тип (типы) параметров, поведение не определено.

Фон:

В C / C ++ вы можете привести указатель функции к любой сигнатуре, которую хотите, однако, если вы ошиблись, поведение не определено.

В вашем случае вы упомянули две подписи:

void (*)(int)

и

void (*)(int, int, int)

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

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

void (*)(int* begin, size_t n);
// begin points to an array of int of n elements

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

...