简体   繁体   English

如何将函数转换为C中的结构?

[英]How to cast function into a struct in C?

This is my first post on StackOverflow, so I hope the format will be okay.这是我在 StackOverflow 上的第一篇文章,所以我希望格式没问题。

I want to pass functions as parameter to another function.我想将函数作为参数传递给另一个函数。 To that end, I declare a struct to describe functions.为此,我声明了一个结构来描述函数。 But then, I get an invalid analyser error on compilation.但是,我在编译时得到一个无效的分析器错误。

In functions.h, I have this bit:在functions.h中,我有这个位:

struct double_fun{
    double (*function)(double x);
};
// Test functions.
extern struct double_fun square;
// Other test functions

// Exponential function.
extern double exponential_temp(double x,double temperature);
extern struct double_fun exponential(double temperature);

Then in functions.c:然后在functions.c中:

// Test functions for somme.
double square_fun(double x){
    return x*x;
}
struct double_fun square = square_fun();
// Other test functions

// Exponential function.
double exponential_temp(double x, double temperature){
    return exp(x/temperature); // From math.h
}

struct double_fun exponential(double temperature){
    double aux_fun(double x){return exponential_temp(x, temperature);};
    struct double_fun aux = aux_fun;
    return aux;
}


double somme(double* liste, int length, struct double_fun fun){
    double poids = 0;
    for(int i=0;i<length;++i){
        poids = poids + (fun.function)(liste[i]);
    }
    return poids;
}

My ultimate goal is to use somme(list, length, function) to apply a function to a list then return the sum of the elements (in particular with the exponential function).我的最终目标是使用 somme(list, length, function) 将函数应用于列表,然后返回元素的总和(特别是指数函数)。 But in another file where I call somme, I call it several times for different values of temperature.但是在另一个我称之为somme的文件中,我多次调用它以获得不同的温度值。 This is why I have exponential(temperature) which is supposed to return a function depending on the value of temperature.这就是为什么我有指数(温度),它应该根据温度值返回一个函数。

This is the error I get:这是我得到的错误:

make learning
gcc -Iinclude/ -o source/functions.o -c source/functions.c -Wall -Werror -lm
source/functions.c:83:28: error: invalid initializer
   83 | struct double_fun square = square_fun;
      |                            ^~~~~~~~~~
[[Same error for other test functions]]
source/functions.c: In function ‘exponential’:
source/functions.c:104:29: error: invalid initializer
  104 |     struct double_fun aux = aux_fun;
      |                             ^~~~~~~
make: *** [Makefile:21 : source/functions.o] Erreur 1

I tried to use no struct and return pointers towards functions instead, and it worked for the test functions, but then there seemed to be no valid syntax for exponential(temperature) that would return a function depending on temperature.我尝试不使用结构并返回指向函数的指针,它适用于测试函数,但似乎没有有效的指数(温度)语法会根据温度返回函数。 On StackOverflow, I found several explanations of how to pass functions as parameters, but no example allows me to have temperature as a parameter without being an argument.在 StackOverflow 上,我找到了一些关于如何将函数作为参数传递的解释,但没有一个示例允许我将温度作为参数而不作为参数。

If you could help me either get around this error or find another way to pass a function as parameter to somme, I would be very grateful!如果您能帮助我解决此错误或找到另一种方法将函数作为参数传递给 somme,我将不胜感激!

You don't convert the function to a struct.您不会将函数转换为结构。 The function is a member of the struct, so you use an ordinary initializer list to fill it in:该函数是结构的成员,因此您可以使用普通的初始化列表来填充它:

struct double_fun square = {square_fun};

Also, you don't put () after the function name.此外,您不要将()放在函数名称之后。 That calls the function, it doesn't evaluate to the function pointer itself.调用函数,它不会计算函数指针本身。

There's little point in using a struct for this.为此使用结构没有什么意义。 You might use a struct if it held multiple functions that you want to pass together.如果结构包含多个要一起传递的函数,则可以使用它。 For example, a device is represented in the kernel as a struct containing function pointers for opening, reading, writing, closing, etc. the device.例如,一个设备在内核中表示为一个结构,其中包含用于打开、读取、写入、关闭等设备的函数指针。

You actually try to implement a closure.您实际上尝试实现闭包。

The idiomatic way of doing it in is C is passing a function pointer and a void pointer to the context .这样做的惯用方式是 C 是传递一个函数指针和一个 void 指针到 context

However, some time ago I came up with a different approach.然而,前段时间我想出了一个不同的方法。 Surprisingly, there is a family of builtin types in C that carries both a data and the code itself.令人惊讶的是,C 中有一系列内置类型,它既可以携带数据,也可以携带代码本身。 Those are pointers to a function pointer .这些是指向函数指针的指针

The trick is use this single object to pass both the code by dereferencing a function pointer.诀窍是使用这个单一对象通过取消引用函数指针来传递两个代码。 And next passing the very same double function pointer as the context as a first argument.接下来传递与上下文相同的双函数指针作为第一个参数。 It looks a bit convoluted by actually it results in very flexible and readable machanism for closures.它看起来有点令人费解,实际上它为闭包带来了非常灵活和可读的机制。

See the code:见代码:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// typedefing functions makes usually makes code more readable
typedef double double_fun_t(void*, double);

struct exponential {
   // closure must be placed as the first member to allow safe casting
   // between a pointer to `closure` and `struct exponential`
  double_fun_t *closure;
  double temperature;
};

double exponential(void *ctx_, double x) {
  struct exponential *ctx = ctx_;
  return exp(x / ctx->temperature);
}

// the "constructor" of the closure for exponential
double_fun_t **make_exponential(double temperature) {
  struct exponential *e = malloc(sizeof *e);
  e->closure = exponential;
  e->temperature = temperature;
  return &e->closure;
}

// now simple closure with no context, a pure x -> x*x mapping
double square(void *_unused, double x){
    (void)_unused;
    return x*x;
}

// use compound literal to transform a function to a closure
double_fun_t **square_closure = & (double_fun_t*) { square };

// the worker that process closures, note that `double_fun_t` is not used
// because `double(**)(void*,double)` is builtin type
double somme(double* liste, int length, double (**fun)(void*,double)){
    double poids = 0;
    for(int i=0;i<length;++i)
        // calling a closure, note that `fun` is used for both obtaing
        // the function pointer and for passing the context
        poids = poids + (*fun)(fun, liste[i]);
    return poids;
}

int main(void) {
    double list[3] = { 1, 2, 3 };
    
    printf("%g\n", somme(list, 3, square_closure));

    // a dynamic closure
    double_fun_t **exponential = make_exponential(42);
    printf("%g\n", somme(list, 3, exponential));
    free(exponential);

    return 0;
}

The advantage of this approach is that the closure exports a pure interface for calling double->double functions.这种方法的优点是闭包导出了一个纯接口来调用 double->double 函数。 There is no need to introduce any boxing structures used by all clients of the closure.没有必要介绍闭包的所有客户使用的任何装箱结构。 The only requirement is the "calling convention" which is very natural and does not require sharing any code.唯一的要求是非常自然且不需要共享任何代码的“调用约定”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM