在C中动态创建函数

如何在C中动态创建函数?

我尝试总结我的C问题如下:

  • 我有一个矩阵,我希望能够使用一些函数来生成它的元素。

  • 函数没有参数

因此我定义了以下内容:

typedef double(function)(unsigned int,unsigned int); /* writes f(x,y) to each element x,y of the matrix*/ void apply(double ** matrix, function * f); 

现在我需要在代码中生成常量函数。 我想过创建一个嵌套函数并返回它的指针,但是GCC手册(允许嵌套函数)说:

“如果你试图在包含函数退出后通过它的地址调用嵌套函数,那么一切都会崩溃。”

我希望从这段代码中得到…

 function * createConstantFunction(const double value){ double function(unsigned int,unsigned int){ return value; } return &function; } 

那么我怎样才能让它发挥作用?

谢谢!

C是一种编译语言。 您无法在运行时“在C”中创建代码; 没有特定的C支持向内存发出指令等。 您当然可以尝试分配内存,确保它是可执行的,并在那里发出原始机器代码。 然后使用合适的函数指针从C调用它。

你不会从语言本身得到任何帮助,这就像生成代码并在旧的8位机器上用BASIC调用它一样。

你必须熟悉一些支持闭包机制的编程语言,不是吗? 不幸的是,C不支持像它本身那样的闭包。

如果你坚持关闭,你可以找到一些模拟C中闭包的有用库。 但是大多数这些库都是复杂的并且依赖于机器。
或者,如果你可以改变double ()(unsigned,unsigned);的签名double ()(unsigned,unsigned); ,你可以改变主意同意C-style closure double ()(unsigned,unsigned);

在C中,函数本身没有数据(或上下文),除了它的参数和它可以访问的静态变量。
所以上下文必须由你自己传递。 以下是使用额外参数的示例:

 // first, add one extra parameter in the signature of function. typedef double(function)(double extra, unsigned int,unsigned int); // second, add one extra parameter in the signature of apply void apply(double* matrix,unsigned width,unsigned height, function* f, double extra) { for (unsigned y=0; y< height; ++y) for (unsigned x=0; x< width ++x) matrix[ y*width + x ] = f(x, y, extra); // apply will passing extra to f } // third, in constant_function, we could get the context: double extra, and return it double constant_function(double value, unsigned x,unsigned y) { return value; } void test(void) { double* matrix = get_a_matrix(); // fourth, passing the extra parameter to apply apply(matrix, w, h, &constant_function, 1212.0); // the matrix will be filled with 1212.0 } 

double extra吗? 是的,但仅限于此情况。
如果需要更多背景,我们该怎么做?
在C中,通用参数是void* ,我们可以通过传递上下文的地址来通过一个void *参数传递任何上下文。

这是另一个例子:

 typedef double (function)(void* context, int, int ); void apply(double* matrix, int width,int height,function* f,void* context) { for (int y=0; y< height; ++y) for (int x=0; x< width ++x) matrix[ y*width + x ] = f(x, y, context); // passing the context } double constant_function(void* context,int x,int y) { // this function use an extra double parameter \ // and context points to its address double* d = context; return *d; } void test(void) { double* matrix = get_a_matrix(); double context = 326.0; // fill matrix with 326.0 apply( matrix, w, h, &constant_function, &context); } 

(function,context) pair&constant_function,&contextC-style closure
需要闭包的每个函数(F)必须有一个上下文参数,该参数将作为其上下文传递给闭包。 并且F的调用者必须使用正确的(f,c)对。

如果您可以更改函数的签名以适合C风格的闭包,那么您的代码将是简单且与机器无关的。
如果不能(function和申请不是你写的),试着说服他改变他的代码。
如果失败,你别无选择,只能使用一些闭包库。

一种方法是使用您想要的函数集编写标准C文件,通过gcc编译它并将其作为动态库加载以获取指向函数的指针。

最终,如果您能够在不必动态定义函数的情况下指定函数(例如,通过具有定义其特定行为的参数的通用模板函数),则可能会更好。

使用FFCALL ,它处理特定于平台的技巧 ,使其工作:

 #include  #include  #include  static double internalDoubleFunction(const double value, ...) { return value; } double (*constDoubleFunction(const double value))() { return alloc_callback(&internalDoubleFunction, value); } main() { double (*fn)(unsigned int, unsigned int) = constDoubleFunction(5.0); printf("%g\n", (*fn)(3, 4)); free_callback(fn); return 0; } 

(未经测试,因为我当前没有安装FFCALL,但我记得它的工作原理是这样的。)

如果你想动态编写代码来执行, nanojit可能是一个很好的方法。

在上面的代码中,您正在尝试创建一个闭包。 C不支持这一点。 有一些令人发指的方法可以伪造它,但开箱即用,您无法将变量运行时绑定到您的函数中。

正如已经提到的放松一样 ,语言不支持“在运行时创建代码”,并且需要做很多工作。

我自己没有用过它,但是我的一个同事发誓Lua是一种“嵌入式语言”。 有一个Lua C API ,它(理论上至少)允许你执行动态(脚本)操作。

当然,缺点是最终用户可能需要在Lua进行某种培训。

这可能是一个愚蠢的问题,但为什么必须在您的应用程序中生成该函数? 同样,最终用户自己生成函数有什么优势(而不是从您提供的一个或多个预定义函数中选择)?

由于您希望生成一个遵循简单配方的函数,因此对于某些内联汇编和一块可执行/可写内存来说,这不应该太棘手。

这种方法感觉有点hacky所以我不建议在生产代码中。 由于使用内联汇编,此解决方案仅适用于Intel x86-64 / AMD64,并且需要转换为与其他架构一起使用。

您可能更喜欢这种基于JIT的解决方案,因为它不依赖于任何外部库。

如果您想了解下面代码如何工作的更长的解释,请留下评论,我会添加它。

出于安全原因,在生成函数后,应将代码页标记为PROT_READ|PROT_EXEC PROT_EXEC (请参阅mprotect )。

 #include  #include  #include  #include  int snippet_processor(char *buffer, double value, int action); enum snippet_actions { S_CALC_SIZE, S_COPY, }; typedef double (*callback_t) (unsigned int, unsigned int); int main(int argc, char **argv) { unsigned int pagesize = 4096; char *codepage = 0; int snipsz = 0; callback_t f; /* allocate some readable, writable and executable memory */ codepage = mmap(codepage, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); // generate one function at `codepage` and call it snipsz += snippet_processor(codepage, 12.55, S_COPY); f = (callback_t) (codepage); printf("result :: %f\n", f(1, 2)); /* ensure the next code address is byte aligned * - add 7 bits to ensure an overflow to the next byte. * If it doesn't overflow then it was already byte aligned. * - Next, throw away any of the "extra" bit from the overflow, * by using the negative of the alignment value * (see how 2's complement works. */ codepage += (snipsz + 7) & -8; // generate another function at `codepage` and call it snipsz += snippet_processor(codepage, 16.1234, S_COPY); f = (callback_t) (codepage); printf("result :: %f\n", f(1, 2)); } int snippet_processor(char *buffer, double value, int action) { static void *snip_start = NULL; static void *snip_end = NULL; static void *double_start = NULL; static int double_offset_start = 0; static int size; char *i, *j; int sz; char *func_start; func_start = buffer; if (snip_start == NULL) { asm volatile( // Don't actually execute the dynamic code snippet upon entry "jmp .snippet_end\n" /* BEGIN snippet */ ".snippet_begin:\n" "movq .value_start(%%rip), %%rax\n" "movd %%rax, %%xmm0\n" "ret\n" /* this is where we store the value returned by this function */ ".value_start:\n" ".double 1.34\n" ".snippet_end:\n" /* END snippet */ "leaq .snippet_begin(%%rip), %0\n" "leaq .snippet_end(%%rip), %1\n" "leaq .value_start(%%rip), %2\n" : "=r"(snip_start), "=r"(snip_end), "=r"(double_start) ); double_offset_start = (double_start - snip_start); size = (snip_end - snip_start); } if (action == S_COPY) { /* copy the snippet value */ i = snip_start; while (i != snip_end) *(buffer++) = *(i++); /* copy the float value */ sz = sizeof(double); i = func_start + double_offset_start; j = (char *) &value; while (sz--) *(i++) = *(j++); } return size; } 

此机制称为reflection,其中代码在运行时修改其自身的行为。 Java支持reflectionapi来完成这项工作。
但我认为这种支持在C中不可用。

Sun网站上写道:

反思是强大的,但不应滥用。 如果可以在不使用reflection的情况下执行操作,则优选避免使用它。 通过reflection访问代码时,应牢记以下问题。

反思的缺点

性能开销因为reflection涉及动态解析的类型,所以无法执行某些Java虚拟机优化。 因此,reflection操作的性能低于非reflection操作,并且应避免在性能敏感应用程序中频繁调用的代码段中。

安全限制

Reflection需要运行时权限,在安全管理器下运行时可能不存在。 对于必须在受限安全上下文中运行的代码,例如在Applet中,这是一个重要的考虑因素。

内部接触

由于reflection允许代码执行在非reflection代码中非法的操作,例如访问私有字段和方法,因此使用reflection可能导致意外的副作用,这可能导致代码function失常并可能破坏可移植性。 reflection代码打破了抽象,因此可能会通过升级平台来改变行为。 。

看起来你来自另一种通常使用这种代码的语言。 C不支持它,虽然你当然可以做一些事情来动态生成代码,但很可能这不值得付出努力。

你需要做的是在函数中添加一个额外的参数来引用它应该处理的矩阵。 这很可能是支持动态函数的语言无论如何都要在内部完成的。

如果您确实需要动态创建函数,那么嵌入式C解释器可能会有所帮助。 我刚刚搜索了“嵌入式C解释器”并得到了Ch:

http://www.softintegration.com/

从来没有听说过,所以我对此一无所知,但似乎值得一看。