Простой способ вызова функции с неизвестным количеством аргументов через указатель - PullRequest
5 голосов
/ 23 августа 2011

У меня есть следующий код:

typedef void * (__stdcall * call_generic)(...);
typedef void * (__stdcall * call_push2)(unsigned long,unsigned long);

void * pfunc;
// assume pfunc is a valid pointer to external function

// this is a logically correct way of calling, however this includes:
// add esp, 8
// after the call, and that breaks my stack.
((call_generic)pfunc)(1,1);

// however, if i use this call:
((call_push2)pfunc)(1,1);
// this does not happen and code works properly.

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

Есть ли решение?Есть ли другой способ создания типа call_generic, чтобы делать такие вещи?

Я не совсем понимаю, почему именно он выполняет эту «очистку», но это сильно ломает мой стек, вызывая потерю ранее определенных переменных.

Ответы [ 3 ]

3 голосов
/ 23 августа 2011

((call_generic)pfunc)(1,1); - это логически правильный способ вызова, только если функция, на которую указывает pfunc, действительно имеет подпись, которую вы разыграли, void *(...). Ваш код говорит компилятору сделать вызов varargs, поэтому он делает вызов varargs. Вызов varargs для функции, которая не является функцией varargs, не работает (в этом случае есть разногласие о том, кто должен очистить стек, и это делается дважды).

Нет способа сделать это бесплатно. Вы должны как-то привести указатель функции к правильной сигнатуре перед ее вызовом, иначе вызывающий код не знает, как передать параметры способом, который может использовать код вызываемого абонента.

Один из вариантов - убедиться, что все вызываемые функции, на которые может указывать pfunc, имеют одинаковую подпись, а затем приводятся к этому типу. Например, вы могли бы сделать их все функции varargs, хотя я не особенно рекомендую это. Было бы более безопасно делать то, что вы не хотите - убедитесь, что все функции, которые могут появиться здесь, принимают два unsigned long и приводят к call_push2.

1 голос
/ 23 августа 2011

Трюк с call_generic не будет работать с функциями, которые должны вызываться с __stdcall соглашением о вызовах. Это связано с тем, что __stdcall подразумевает, что функция должна очищать стек, а функции OTOH (с аргументами ...) могут этого не делать, поскольку они не знают об аргументах.

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

В вашем конкретном случае я бы пошел в направлении написания макросов. Я не вижу тривиального трюка, который бы соответствовал вашим потребностям.

EDIT

Одним из методов может быть использование шаблонных классов. Например:

// any __stdcall function returning void taking 2 arguments
template <typename T1, typename T2>
struct FuncCaller_2
{
    typedef void * (__stdcall * FN)(T1, T2);

    static void Call(PVOID pfn, T1 t1, T2 t2)
    {
        ((FN) pfn)(t1, t2);
    }
};

// call your function
FuncCaller_2<int, long>::Call(pfn, 12, 19);

Вам потребуется создать такой класс для каждого числа аргументов (0, 1, 2, 3, ...).

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

0 голосов
/ 23 августа 2011

мне кажется, вам нужен механизм динамической привязки функций, который будет привязывать указатель к прототипу, выведенному из вызова.Как правило, для этого требуется метапрограммирование, так как этот boost :: bind или что-то еще из библиотеки функций надстройки - ваша лучшая ставка (и если у них ее нет, сомнительно, что я могу это сделать).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...