Я на 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'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
для конкретной ОС, вы, вероятно, могли бы заставить это работать во многих местах. Но я уверен, что есть компьютеры, на которых работает С, где вы не можете этого сделать.
person
Samuel Edwin Ward
schedule
24.05.2014