Вызов функций cdecl с различным количеством аргументов - PullRequest
1 голос
/ 11 марта 2010

У меня есть функции, которые я хочу вызвать на основе некоторого ввода. Каждая функция имеет разное количество аргументов. Другими словами,

if (strcmp(str, "funcA") == 0) funcA(a, b, c);
else if (strcmp(str, "funcB") == 0) funcB(d);
else if (strcmp(str, "funcC") == 0) funcC(f, g);

Это немного громоздко и трудно поддерживать. В идеале это функции с переменным числом (например, в стиле printf) и могут использовать переменные. Но это не так. Итак, используя соглашение о вызовах cdecl, я заполняю стек через структуру, полную параметров. Мне интересно, есть ли лучший способ сделать это. Обратите внимание, что это строго для внутреннего использования (например, простых инструментов, модульных тестов и т. Д.) И не будет использоваться для любого производственного кода, который может подвергаться вредоносным атакам.

Пример:

#include <stdio.h>

typedef struct __params
{
    unsigned char* a;
    unsigned char* b;
    unsigned char* c;
} params;

int funcA(int a, int b)
{
    printf("a = %d, b = %d\n", a, b);
    return a;
}

int funcB(int a, int b, const char* c)
{
    printf("a = %d, b = %d, c = %s\n", a, b, c);
    return b;
}

int funcC(int* a)
{
    printf("a = %d\n", *a);
    *a *= 2;
    return 0;
}

typedef int (*f)(params);

int main(int argc, char**argv)
{
    int val;
    int tmp;
    params myParams;
    f myFuncA = (f)funcA;
    f myFuncB = (f)funcB;
    f myFuncC = (f)funcC;

    myParams.a = (unsigned char*)100;
    myParams.b = (unsigned char*)200;

    val = myFuncA(myParams);
    printf("val = %d\n", val);

    myParams.c = (unsigned char*)"This is a test";
    val = myFuncB(myParams);
    printf("val = %d\n", val);

    tmp = 300;
    myParams.a = (unsigned char*)&tmp;
    val = myFuncC(myParams);
    printf("a = %d, val = %d\n", tmp, val);
    return 0;
}

Выход:

gcc -o func func.c
./func
a = 100, b = 200
val = 100
a = 100, b = 200, c = This is a test
val = 200
a = 300
a = 600, val = 0

1 Ответ

0 голосов
/ 16 марта 2010

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

#define WRAP_VARIADIC_2_ARG(FNAME, RETTYPE, ARGTYPE1, ARGTYPE2) \
  (RETTYPE) wrapped_##FNAME(...) {                              \
    va_list args;                                               \
    va_start(args, 2);                                          \
    (ARGTYPE1) arg1 = va_arg(args, (ARGTYPE1));                 \
    (ARGTYPE2) arg2 = va_arg(args, (ARGTYPE2));                 \
    va_end(args);                                               \
    return FNAME(arg1, arg2);                                   \
  }

И подобные макросы для других аргументов. Затем вы звоните:

WRAP_VARIADIC_2_ARG(funcA, int, int, int)
WRAP_VARIADIC_3_ARG(funcB, int, int, int, const char*)
WRAP_VARIADIC_1_ARG(funcC, int, int*)

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

int wrapped_funcA(...)
int wrapped_funcB(...)
int wrapped_funcC(...)

Это должно быть просто оттуда.

...