c - 如何在c中推迟函数调用

标签 c

我试图通过将其参数保存在空指针列表中来推迟函数调用(使用函数包装器):

void *args[]
int argt[]

argt 用于记住存储在 void * 位置的数据类型。

稍后,我需要调用延迟函数:

function(args[0], args[1])

但问题是我必须正确指定它们的类型。

我使用了一个宏,像这样:

#define ARGTYPE(arg, type) type == CHARP ? (char *) arg : (type == LONGLONG ? *((long long *) arg) : NULL)

函数调用变为:

function(ARGTYPE(args[0], argt[0]), ARGTYPE(args[1], argt[1]))

我有两个问题:

1) 警告:条件表达式中的指针/整数类型不匹配,由宏定义生成(请注意我可以忍受它,参见 2))

2) 真正的问题:long long 参数没有正确传递(我每次都得到 0)

我显然遗漏了一些东西,所以任何人都可以解释(详细地)为什么宏不能正常工作,或者建议另一种方法吗?

编辑:我在此处添加了存储参数部分(相关细节,我解析了一个 va_list),它根据格式说明符获取它们的类型:

while (*format)
{
    switch(*format)
    {
        case 's':
            saved_arguments[i] = strdup(arg);
            break;
        case 'l':
            saved_arguments[i] = malloc(sizeof(long long));
            *((long long *) saved_arguments[i]) = arg;
            break;
    }
    i++;
    format++;
}

最佳答案

您的警告是由三元运算符在其子表达式中具有多种类型引起的,即:-

cond ? expression of type 1 : expression of type 2

这两个表达式需要评估为相同的类型,这对您的帮助不大。

为了解决你的问题,我可以想到两种方案,这两种方案都有点恶心。

  1. 使用 VARARGS/可变参数函数

    使用“...”参数定义您的函数,并使用给定的宏将参数存储在某处,并将目标函数定义为接受 va_list。您确实失去了类型安全、编译器检查和函数需要额外元数据以及重写目标函数以使用 va_list 的每一点。

  2. 使用汇编程序并破解堆栈

告诉过你这很讨厌。给定一个函数来调用:-

void FuncToCall (type1 arg1, type2 arg2);

创建一个函数:-

void *FuncToCallDelayed (void (*fn) (type1 arg1, type2 arg2), type1 arg1, type2 arg2);

它将堆栈上的参数复制到一个动态分配的内存块中并返回。然后,当你想调用函数时:-

void CallDelayedFunction (void *params); 

使用指针调用返回的 FuncToCallDelayed。然后将参数压入堆栈,并调用该函数。参数和函数指针在params参数中。

此方法确实将您绑定(bind)到特定的处理器类型,但至少在参数列表上保留了某种形式的类型检查。

更新

这是方法 2 的一个版本,为 Visual Studio 2012、IA32 构建,在 Win7 上运行:-

#include <iostream>
using namespace std;

__declspec (naked) void *CreateDelayedFunction ()
{
    __asm
    {
        mov esi,ebp
        mov eax,[esi]
        sub eax,esi
        add eax,4
        push eax
        call malloc
        pop ecx
        or eax,eax
        jz error
        mov edi,eax
        sub ecx,4
        mov [edi],ecx
        add edi,4
        add esi,8
        rep movsb
      error:
        ret
    }
}

void CallDelayedFunction (void *params)
{
    __asm
    {
        mov esi,params
        lodsd
        sub esp,eax
        mov edi,esp
        shr eax,2
        mov ecx,eax
        lodsd
        rep movsd
        call eax
        mov esi,params
        lodsd
        add esp,eax
    }
}

void __cdecl TestFunction1 (int a, long long b, char *c)
{
    cout << "Test Function1: a = " << a << ", b = " << b << ", c = '" << c << "'" << endl;
}

void __cdecl TestFunction2 (char *a, float b)
{
    cout << "Test Function2: a = '" << a << "', b = " << b << endl;
}

#pragma optimize ("", off)

void *__cdecl TestFunction1Delayed (void (*fn) (int, long long, char *), int a, long long b, char *c)
{
    return CreateDelayedFunction ();
}

void *__cdecl TestFunction2Delayed (void (*fn) (char *, float), char *a, float b)
{
    return CreateDelayedFunction ();
}

#pragma optimize ("", on)

int main ()
{
    cout << "Calling delayed function1" << endl;
    void *params1 = TestFunction1Delayed (TestFunction1, 1, 2, "Hello");
    cout << "Calling delayed function2" << endl;
    void *params2 = TestFunction2Delayed (TestFunction2, "World", 3.14192654f);
    cout << "Delaying a bit..." << endl;
    cout << "Doing Function2..." << endl;
    CallDelayedFunction (params2);
    cout << "Doing Function1..." << endl;
    CallDelayedFunction (params1);
    cout << "Done" << endl;
}

** 另一个更新 **

正如我在评论中提到的,还有第三种选择,那就是使用消息系统。创建一个消息对象,而不是调用一个函数:-

struct MessageObject
{
   int message_id;
   int message_size;
};

struct Function1Message
{
   MessageObject base;
   // additional data
};

然后在 message_id 和实际函数之间进行查找,函数和查找定义如下:-

void Function1 (Function1Object *message)
{
}

struct Lookup
{
  int message_id;
  void (*fn) (void *data);
};

Lookup lookups [] = { {Message1ID, (void (*) (void *data)) Function1}, etc };

关于c - 如何在c中推迟函数调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18082335/

相关文章:

c - eBPF:默认 bpf 程序/映射?

c - 在 C 中编辑文件中的一行

c++ - "#define FOO(template)"是做什么的?

c - 当我将指针初始化为NULL时,运行结果很奇怪

c - 预期 ‘=’ , ‘,’ , ‘;’ , ‘asm’ 或 ‘__attribute__’ token 之前的 ‘{’

c - fopen 返回 NULL,但 perror 打印成功?

c - 从缓冲区绘制 bmp 时出现意外结果

C token 解析

c - 需要有关将代码转换为 Matlab_extension 2 的建议

c - 有没有办法省略 C 预处理器输出顶部的定义(行标记)?