用ISO C99进行curl/装订

假设我想用普通的C实现一个数值积分例程。看起来像这样:

double integrate(double (*f)(double), double lower, double upper, double step)); 

我经常发现实际上依赖于多个变量的函数,我想要整合第一个函数。 说我想整合这个:

 double func(double x, double z); 

关于x 。 我无法通过func进行integrate因为它有错误的签名。 现在我知道了以下变通方法,我们在学习数字课程时使用了这些变通方法:

  • 使用C ++

    我刚刚使用了C ++和ist std::bind来创建一个我可以传递给集成例程的仿函数(函数对象)。 现在我只想使用lambda函数来完成它。

  • 在function中使用GCC扩展function

    使用GCC,您可以在函数中声明函数。 人们可以做到

     // z is set to some value in this function scope here. double inner(double x) { return func(x, z); } 

    并将inner传递给integrate函数。 这是非标准的,感觉不太好。

  • 使用全局变量

    z的值可以存储在全局变量中。 这将要求函数func可编辑以使用全局变量中的z而不是参数。 这可能是不可能的。 然后它也打破了并发性,一般来说都很糟糕。

在没有破坏某些东西的情况下,在普通C中是否存在一种方法?

这个问题的一个常见解决方案是将设计更改为:

 double integrate(double (*f)(double, void*), void*, double lower, double upper, double step); 

在这里你传递一个额外的void *integrate ,这将被传递回f 。 这可用于传递任意数据,在您的情况下,您将指针传递给z ,并在函数f您将指针转回其原始类型并恢复值z 。 这种模式在C库中无处不在,例如这里是pthread_create的原型:

 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); 

不,C无法在C函数/ C函数指针级别执行此操作。 为特定应用程序(如数学函数和集成)执行此操作的最佳方法是使用您自己的结构来表示数学函数,以及已绑定的任何变量槽。 我可能不会使函数采用不同数量的参数,而是指向一组参数的指针; 这使得以不同方式以编程方式调用它们变得容易得多。

或者你可以使用像libffi这样的东西来做这种绑定/闭包,但它绝对不是便携式或高效的。

您的问题也可以通过变量参数函数来解决(并且稍加努力):

 #include  double integrate(double (*f)(double, ...), double lower, double upper, double step) { return (f)(lower, upper); } double func1(double x, ...) { va_list ap; double ret; va_start(ap, x); ret = x * va_arg(ap, double); va_end(ap); return ret; } double func2(double x, ...) { return x; } 

虽然不确定我是否应该以任何方式考虑清洁……

我认为你在function中使用GCC扩展function的例子一个可行的解决方案。 顺便说一下,它不是GCC扩展的一个例子,而只是一个辅助函数。

 double inner(double x) { return func(x, 100); } 

那是完全有效的C89。

我99%肯定使用纯C89是不可能的。 为此,您必须在运行时创建一个新的函数指针。 有两种方法可以获取函数指针:来自函数表达式,或来自返回函数指针的标准库函数。 函数表达式是指在编译时定义的函数,因此不起作用。 返回函数指针的唯一标准库函数是signal ,这没有任何帮助,因为你只是放弃了它所放入的内容。

获取新函数指针的唯一另一种方法是将指向对象类型的指针转​​换为函数指针,这是不可移植的,因为它不在您可以执行的指针转换列表中(但是,它被标记为共同的延伸)。

有一段时间我认为你可能能够通过setjmplongjmp获得某个地方,但这只是替换存储double问题,存储jmp_buf的问题。

我确实得到了今天在我的系统上运行的东西,但由于它不可移植,即使升级我的编译器也可能会破坏它。 一般的想法是创建一个结构,其中包含指向原始函数的指针, double z和一些机器代码来访问该信息并调用原始函数。 我不建议你使用它,但我发现它很有趣。 我在评论中指出了一些不可移植的假设。

 /* platform-specific include */ #include  /* standard includes */ #include  #include  #include  double func(double x, double z) { printf("%g\n", z); return x; } double bar(double x) { printf("y\n"); return x; } double call(double (*f)(double)) { return f(0.0); } struct cDblDblRetDbl { double (*function)(double, double); double a; char code[1]; double pad; }; static double helper(double x) { /* Casting a function pointer to an object pointer is * not provided by the standard. * In addition, this only works because the compiler * happens to use RIP-relative addressing, so "helper" * points to the beginning of the currently executing * function, which is actually a copy of the one in * the object file. * It's worth noting explicitly that nothing in the * C standard says that a pointer to a function will * point to its machine code. */ double *dp = (double *) helper; struct cDblDblRetDbl *data; /* Modify it to point after the structure. * This depends on the alignment and padding of the * structure, which is not portable. */ dp += 2; data = (struct cDblDblRetDbl *) dp; /* back it up to the structure */ --data; /* now call the function with the saved data. */ return data->function(x, data->a); } /* There is no way to get the code size of a function, * so this is very nonportable. * I found it by examining the object file. */ #define CODE_SIZE 0x60 double (*curryDoubleDoubleReturningDouble(double (*function)(double, double), double a))(double) { size_t size = sizeof(struct cDblDblRetDbl) + CODE_SIZE; /* valloc is nonstandard but we need an area aligned to a * page boundary for mprotect. */ struct cDblDblRetDbl *result = valloc(size); result->function = function; result->a = a; /* Copy the code of the helper function into the structure. * Once again, we're casting a function pointer to an * object pointer and the standard doesn't say you can do that. */ memcpy(result->code, (void *) helper, CODE_SIZE); /* Memory protection is out of the scope of the standard, * and in a real program we need to check the return value. */ mprotect(result, CODE_SIZE, PROT_READ | PROT_EXEC | PROT_WRITE); /* Casting an object pointer to a function pointer is also * not provided by the standard. * This example leaks memory. */ return (double(*)(double)) result->code; } int main() { call(bar); call(curryDoubleDoubleReturningDouble(func, 5)); call(curryDoubleDoubleReturningDouble(func, 7)); call(curryDoubleDoubleReturningDouble(func, 42.9)); } 

如果您在汇编中编写了helper程序并创建了特定于操作系统的curryDoubleDoubleReturningDouble版本, curryDoubleDoubleReturningDouble您可能会在很多地方使用它。 但我确信有一些计算机C运行在你无法做到的地方。