Currying / binding to ISO C99

Let's say I want to implement a numerical integration procedure with a simple C. It will look something like this:

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

I often find functions that actually depend on several variables, and I want to integrate them first. Say I want to integrate this:

double func(double x, double z);

relatively x. I can not pass funcin integrate, because it has the wrong signature. Now I know the following workarounds that we used when we headed for the numerical values:

  • Use c ++

    I just used C ++ and ist std::bindto create a functor (function object) that I could pass into the integration procedure. Now I would just use the lambda functions to do this.

  • Use GCC extension for functions in functions

    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 . . concurrency .

, C, -?

+3
5

:

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);
+4

, C C/C. (, ) - , . , , , ; -.

- libffi, /, .

+3

( ):

#include <stdarg.h>

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

, , - ...

+2

99% , C89. . : , . , , . , , signal, , , .

- , , (, ).

, - setjmp longjmp, double jmp_buf.

-, , , . , , double z . , . .

/* platform-specific include */
#include <sys/mman.h>
/* standard includes */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

If you wrote helperin the assembly and created versions curryDoubleDoubleReturningDoublefor the OS, you could probably get this to work a lot of places. But I'm sure there are several C computers on which you cannot do this.

0
source

I think your example in Use the GCC extension for functions in functions is a viable solution. And by the way, this is not an example of a GCC extension, but just an auxiliary function.

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

This is a perfectly valid C89.

0
source

Source: https://habr.com/ru/post/1693735/


All Articles