Как сделать функцию, которая получает другую функцию в качестве параметра (без известных параметров) - PullRequest
0 голосов
/ 15 октября 2018

Ранее я спрашивал Как создать функцию, которая получает функцию в качестве параметра на языке Си.Я получаю ответ Ссылка на вопрос , но это решение основано на параметрах функции аргумента.Я имею в виду:

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 голосов
/ 15 октября 2018

Другая возможность - использовать 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;
0 голосов
/ 15 октября 2018

Узнайте больше о closure s и tagged union s.Обратите внимание, что у С их нет.Возможно, вы захотите эмулировать это с помощью обратного вызова s

Я хочу добиться передачи любой функции на functionWhichReceiveFunction

Вы не можете этого сделать просто и переносимо.Помните, что сигнатура функции в C связана с ее соглашениями о вызовах (то есть с ABI , используемым вашим компилятором и вашим кодом; для примеров, посмотрите в Linux x86ABI s, поэтому аргументы с плавающей точкой могут передаваться в различных регистрах в качестве интегральных аргументов, поэтому вашему компилятору необходимо знать сигнатуру всех ваших указателей на функции).Вам также нужно дать 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.Вам нужно стандартное (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

гораздо позже, вам понадобится немного va_end(arglis); ближе к концу вашего functionWhichRecieveFunction тела.

0 голосов
/ 15 октября 2018

Чтобы иметь возможность передавать функцию с неизвестными параметрами в функцию, объявите функцию, которая передаст функцию, следующим образом:

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);
0 голосов
/ 15 октября 2018

@ Пол Огилви, это код:

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;
}
0 голосов
/ 15 октября 2018

Определите функцию, которую нужно передать, чтобы принять 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...