【问题标题】:How to make a function which receive other function as param (with no known parameters)如何制作一个接收其他函数作为参数的函数(没有已知参数)
【发布时间】:2019-03-19 21:32:44
【问题描述】:

之前我问过如何用C语言制作一个接收函数作为参数的函数。我得到了答案Link to the question,但这个解决方案是基于参数函数的参数。我的意思是:

int functionToPassAsParameter (int arg1, int arg2){
    // do something
}

int functionWhichReceiveFunction (int (*f)(), int arg1, int arg2){
    // do something
    f(arg1, arg2);
    // do something
}

// How to call the function
functionWhichReceiveFunction (&functionToPassAsParameter, 1, 2);

我想要类似的东西:

int functionToPassAsParameter (int arg1, int arg2){
    // do something
}

int functionWhichReceiveFunction ( f() ){
    // do something
    f();
    // do something
}

// How to call the function
functionWhichReceiveFunction ( functionToPassAsParameter(arg1, arg2) );

所以,当我调用函数时,我传递了正确的参数,但是当我定义接收另一个函数的函数时,我没有指定要发送给它的参数。有可能吗?


编辑 1: 我想实现将任何功能传递给functionWhichReceiveFunction。就像是:
int function_a (int param1, int param2) { /* Do something */ };
void function_b (char *arg1) { /* Do something */ };

// Call the function with two differents functions regardless return type nor params
int x = functionWhichReceiveFunction ( function_a(param1, param2) );
functionWhichReceiveFunction ( function_b(arg1) );

【问题讨论】:

  • 你想用你建议的方法解决什么问题,第一种方法还没有解决?
  • 第一种方法需要定义要发送的参数。第二种方法不接收任何参数,所以它是 dinamyc,我可以使用它发送任何函数。在第一种方法中,只能始终发送相同的功能
  • 函数指针呢?
  • 在这种情况下你会如何调用函数?听起来您几乎是在尝试重新创建多态性。
  • 如果你不知道函数的签名,你将如何调用它?您可以使用不透明指针作为参数并使用不透明指针作为void func(void*(*f)(void *), void *arg) { f(arg) } 进行调用,但在某些地方需要处理不透明指针并且需要知道底层类型,以便您可以使用它来做一些有用的事情。软件工程的主要问题在哪里。

标签: c function parameters


【解决方案1】:

定义要传递的函数以将void * 作为参数。这样调用给定函数的函数就不需要知道任何关于参数的具体信息:

struct params1 {
   int arg1;
   int arg2;
};

struct params2 {
   char *arg1;
   char *arg2;
};

int functionToPassAsParameter (void *param){
    struct params1 *args = params;
    // do something
}

int otherFunctionToPassAsParameter (void *param){
    struct params2 *args = params;
    // do something
}

int functionWhichReceiveFunction (int (*f)(void *), void *args) {
    // do something
    f(args);
    // do something
}

struct params1 p1 = { 1, 2 };
functionWhichReceiveFunction (&functionToPassAsParameter, &p1);
struct params2 p2 = { "abc", "def" };
functionWhichReceiveFunction (&otherFunctionToPassAsParameter, &p2);

【讨论】:

  • 谢谢,我会试试这个解决方案。而不是structs,参数不能一对一传递?我的意思是:function(param1, param2) 而不是 function(struct_params) ?或者类似main(int argc, char *argv[]) 的东西?
  • 在传递函数的时候不要加上&;编译器将传递 oits 地址。 void * 仅适用于指针,不适用于其他类型的参数。
【解决方案2】:

为了能够将带有未知参数的函数传递给函数,请按如下方式声明将传递该函数的函数:

int g(int (*f)());

实际传递的函数可以有任意数量的参数,例如:

int f(int x, void *y);

现在调用如下:

g(f);

上面的意思是g传递f,可以有零个或多个任意类型的参数。这由空参数列表表示。

可能需要传递的特定函数是例如f

现在g 被函数f 或任何其他函数调用。

请注意,在调用f 时必须由g 知道必须传递哪些参数。所以你需要一个“协议”来告诉g 传递了哪个函数/类型。例如,除了传递函数外,还要传递一个标识符(int),说明传递的是什么类型的函数,例如:

#define fS_I_I  1   // f needs String, int, Int
#define fD_I    2   // f needs Double, Int
#define fI_I    3   // f needs Int, Int

int g(int ID, int (*f)());

g(fI_I, f);

【讨论】:

  • int f(int x, void *y) { return x; }; int g(int (*f)()) { int x = f(); printf("X 是: %d", x); }; int main() { g( f(1, 2) );返回0; }
  • 我将根据您的示例使用代码回答我的问题。你能说我哪里错了吗?我的代码不工作
【解决方案3】:

@Paul Ogilvie,这是代码:

int f(int x, void *y) {
    return x;
};


int g(int (*f)()) {
    int x = f(1, NULL);     // call f with parameters
    printf("X is: %d", x);
    return(x);              // return result
};

int main()
{
    //g( f(1, 2) );   // this passes the result of a call to f, not f
    g( f );           // this passes f
    return 0;
}

【讨论】:

  • f in g 的调用中必须传递参数。
  • 好的,谢谢。这行得通。但是没有办法在主调用中传递参数?我的意思是:g( f(1, 2) );(你评论的那一行)。这正是我正在寻找的。希望有可能
  • 不,你不能告诉g 将哪个prms 传递给f,因为该符号表示f调用。您当然可以将 prms 添加到 g,例如g(f,1,2);(并调整 g 以获取更多 prms)
  • 您可以为g 使用可变参数:int g(int (*f)(), ...)
【解决方案4】:

详细了解closures 和tagged unions。请注意,C 没有它们。你可能想用callbacks 来模仿它

我想实现将任何功能传递给functionWhichReceiveFunction

你不能这样做简单便携。请记住,C 中函数的签名与其calling conventions 相关(因此与编译器和代码使用的ABI 相关;例如,查看Linux x86 ABIs;因此浮点参数可以在不同的寄存器作为整数参数,因此您的编译器需要知道所有函数指针的签名)。您还需要向functionWhichReceiveFunction 提供描述签名的内容。

假设您的平台具有与数据指针大小相同且地址空间相同的函数指针(这种情况很常见),您可能会考虑做的事情是传递给functionWhichReceiveFunction void* 指针(实际上,一个转换为 void*) 的函数指针和一个描述它的枚举。

例如

enum funsig_en {
  funsig_void_to_void,
  funsig_int_to_void,
  funsig_int_to_double,
  funsig_int_double_to_void,
};

然后,您将拥有相应的函数签名(类型)

typedef void fun_void_to_void(void);
typedef void fun_int_to_void(int);
typedef double fun_int_to_double(int);
typedef void fun_int_double_to_void(int, double);

假设你有这些静态函数

static void statf_void_to_void(void);
static void statf_int_to_void(int);
static double statf_int_to_double(int);
static void statf_int_double_to_void(int, double);

你可以声明

void 
functionWhichReceiveFunction (void*res, enum funsig_en sigkind, void*fun, ...);

你可以把它当作

functionWhichRecieveFunction(NULL, funsig_void_to_void
                             (void*)statf_void_to_void);

functionWhichRecieveFunction(NULL, funsig_int_to_void, 
                             (void*)statf_int_to_void, 123);

double r = 0;
functionWhichRecieveFunction(&r, funsig_int_to_double, 
                             (void*)statf_int_to_double, 2345);

我让你编写可变参数 functionWhichRecieveFunction。您需要stdarg(3) 设施。它将包含类似

的代码
va_args arglist;
va_start (arglist, fun);
switch(sigkind) {
case funsig_int_to_void: {
  int a = va_arg(arglis, int);
  fun_int_to_void* fptr = (fun_int_to_void*)fun;
  (*fptr)(a);
  return;
} // end case funsig_int_to_void

很久以后,您需要在 functionWhichRecieveFunction 正文末尾附近添加一些 va_end(arglis);

【讨论】:

    【解决方案5】:

    另一种可能性是使用可变参数。在下面的示例中,每个被调用的函数都会对参数进行自己的解释。

    #include <stdio.h>
    #include <stdarg.h>
    
    // expects 4 arguments (int, int, int, char*)
    void f1(va_list args) {
        int a, b, c;
        char *d;
    
        a = va_arg(args, int);
        b = va_arg(args, int);
        c = va_arg(args, int);
        d = va_arg(args, char *);
    
        printf("%d, %d, %d: %s\n", a, b, c, d);
    }
    
    // expects 3 ars (int, int, char*);
    void f2(va_list args) {
        int a, b;
        char *c; 
    
        a = va_arg(args, int);
        b = va_arg(args, int);
        c = va_arg(args, char *);
    
        printf("%d, %d: %s\n", a, b, c);
    }
    
    
    
    void caller(void (*f)(va_list), ...) {
        va_list args;
    
        va_start(args, f);
        f(args);
        va_end(args);
    }
    
    
    int main() {
        caller(&f1, 0, 1, 3, "hello");
        caller(&f2, 1, 2, "bye");
        return 0;
    }
    

    另一种可能性是让caller 根据某些类型信息解释参数并调用正确的函数调用。如果您的参数模式数量有限并且只有要调用的常规函数​​,这可能会很有用:

    void f3(int a, int b, int c, char *d) {
        printf("%d, %d, %d: %s\n", a, b, c, d);
    }
    
    void f4(int a, int b, char *c) {
        printf("%d, %d: %s\n", a, b, c);
    }
    
    typedef enum  {
        type1, type2
    } Types;
    
    void caller1(Types t, void (*f)(), ...) {
        va_list args;
    
        va_start(args, f);
        switch (t) {
        case type1: {
            int a, b, c;
            char *d;
    
            a = va_arg(args, int);
            b = va_arg(args, int);
            c = va_arg(args, int);
            d = va_arg(args, char *);
    
            f(a,b,c,d);
            break;
        }
        case type2: {
            int a, b;
            char *c;
    
            a = va_arg(args, int);
            b = va_arg(args, int);
            c = va_arg(args, char *);
    
            f(a,b,c);
        }
        }
        va_end(args);
    
    }
    
    int main() {
    
        caller1(type1, &f3, 3,2,1, "hi");
        caller1(type2, &f4, 3,2,"take care");
    
        return 0;
    

    【讨论】:

    • 来自 C++ 文档:“调用 va_start 的函数也应在返回之前调用 va_end”。 va_end(args) 需要在caller函数的末尾添加。
    猜你喜欢
    • 2019-02-19
    • 2023-01-30
    • 1970-01-01
    • 2019-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-26
    相关资源
    最近更新 更多