如何使一个函数接受其他函数作为参数(没有已知参数)

时间:2018-10-15 12:39:16

标签: c function parameters

以前我问过如何用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) );

5 个答案:

答案 0 :(得分:3)

定义要传递的函数,以将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);

答案 1 :(得分:2)

要能够将参数未知的函数传递给函数,请声明将通过该函数的函数,如下所示:

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

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

int f(int x, void *y);

现在的呼叫如下:

g(f);

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

例如f可能需要传递的特定功能。

现在g是通过函数f或任何其他函数调用的。

请注意,g的决定是在调用f时必须传递哪些参数。因此,您需要一个“协议”来告诉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);

答案 2 :(得分:1)

了解有关closuretagged union的更多信息。请注意,C没有它们。您可能想用callback s

来模拟
  

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

您不能简单,便携地做到这一点。请记住,C语言中函数的签名与其calling conventions有关(因此与编译器和代码使用的ABI相关;例如,请查看Linux x86 ABI s;因此浮点数参数可以作为整体参数传递到不同的寄存器中,因此您的编译器需要知道所有函数指针的签名)。您还需要给functionWhichReceiveFunction一些描述签名的东西。

假设平台上具有与数据指针相同大小且在相同地址空间中的函数指针(通常是这种情况),您可能考虑做的是传递给functionWhichReceiveFunction a {{1 }}指针(实际上是转换为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); 。您需要stdarg(3)设施。它会包含类似

的代码
functionWhichRecieveFunction

不久之后,您将需要在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 正文末尾附近插入一些va_end(arglis);

答案 3 :(得分:1)

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

#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);
}


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);
    }
    }

}

int main() {

    caller1(type1, &f3, 3,2,1, "hi");
    caller1(type2, &f4, 3,2,"take care");

    return 0;

答案 4 :(得分:0)

@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;
}