计算在C中使用哪个函数(或代码类型)

vic*_*ory 1 c artificial-intelligence function dynamic

我有一个不寻常的问题.让我们想象一下我有N个功能:

void function1(){ //some code here }
void function2(){ //some code here }
...
void functionN(){ //some code here }
Run Code Online (Sandbox Code Playgroud)

有没有办法,我可以动态地计算或找出哪个函数,没有IF语句?并根据功能名称的名称,调用它?让我告诉你伪代码,可以更好地描述情况:

 for(int I=1;I<=N;I++){
    functionI();
 }
Run Code Online (Sandbox Code Playgroud)

我的意思是,如果它在某种程度上可以计算(例如在char数组中,但也可以通过任何其他方式)代码,我将在稍后插入和使用.但不是字符串,而是直接像代码.让我演示一下不同的例子:

int num=3;
char functionInString[]="printf(num);
//Some code, that would for example change letters in 
functionInString, for example to different fuction 
consisting of letters

//And here, do whatever is written in functionToString
Run Code Online (Sandbox Code Playgroud)

对不起,如果我不够清楚.任何人都可以告诉我,如果有可能用C语言,或任何不同的语言,这个概念怎么称呼?

Bas*_*tch 5

您可能需要了解闭包回调是什么.函数指针很有用,但可能不够单独阅读(了解函数指针如何用于实现闭包).

你应该学习更多关于C的知识,所以阅读有关C编程的好书.我建议下载C11标准n1570瞥一眼.一个非常重要的概念是未定义的行为,你应该害怕它.

如果它以某种方式可以计算(例如,在char数组中,但也可以通过任何其他方式)代码,我将插入并稍后使用.但不是字符串,而是直接像代码.

这在纯粹的标准C代码中是不可能的(因为包含你的程序的翻译单元的集合是固定的),并且原则上任何有效的函数指针值都应该指向一些现有的函数(因此stricto sensu调用任何其他函数指针值将是未定义的行为).然而,一些实现能够构建(在一些实施的具体方式),不知何故"有效"的新函数指针(但是这是外面的C11标准).在纯粹的哈佛架构中,代码位于ROM中,这甚至是不可能的.

生成并动态加载插件

但是,如果您在现代操作系统运行(我建议使用Linux),您可以使用动态加载插件工具.我专注于Linux(对于Windows来说,细节是非常不同的,而且细节是邪恶的;阅读Levine的书籍" Linkers and Loaders").阅读操作系统:三个简单的部分,以了解有关操作系统的更多信息.

所以你可以做的(在Linux上)是在运行时生成一些临时文件中的一些C代码,例如/tmp/generated.c; 你需要定义你对该文件的约定(例如,它定义了一个void pluginfun(int)具有一些额外的理想属性的函数); 然后你派一个编译命令把它编译成一个共享对象(读取如何通过Drepper 编写共享库),这是一个插件.所以你的程序可能运行(可能使用system(3),或者更低级别的系统调用,如fork(2),execve(2),waitpid(2)等......)编译过程,例如gcc -Wall -O -fPIC /tmp/generated.c -shared -o /tmp/generatedplugin.so; 之后,您的主程序将使用dlopen(3)加载该插件:

void* dlh = dlopen("/tmp/generatedplugin.so", RTLD_NOW);
if (!dlh) { 
  fprintf(stderr, "dlopen /tmp/generatedplugin.so failed: %s\n",
          dlerror());
  exit(EXIT_FAILURE);
}
Run Code Online (Sandbox Code Playgroud)

由于您关心函数指针,如果您将其签名声明为类型,则代码将更具可读性:

typedef void pluginfun_type(int);
Run Code Online (Sandbox Code Playgroud)

然后很容易声明一个指向该签名函数的函数指针:

pluginfun_type* fptr = NULL; 
Run Code Online (Sandbox Code Playgroud)

并且您的程序可以使用dlsym(3)pluginfun插件中的函数地址:

fptr = (pluginfun_type*) dlsym(dlh, "pluginfun");
if (!fptr) {
   fprintf(stderr, "dlsym of pluginfun failed: %s\n", dlerror());
   exit(EXIT_FAILURE);
}
Run Code Online (Sandbox Code Playgroud)

并最后用于(*fptr)(3)调用该函数.

您应该gcc -O -Wall foo.o bar.o -rdynamic -ldl -o foobarprog用来链接您的主程序.您最后可能会调用dlclose(3),但如果您的插件中的某个函数仍有一些活动框架,则不应该这样做.

这种生成插件方法在Linux上运行得非常好.我的manydl.c是一个玩具程序,生成"随机"C代码,将生成的C代码编译成生成的插件,然后加载该插件(并且可以重复多次).它表明,在实践中,Linux程序可以生成和加载数十万(甚至数百万,如果你足够耐心)插件.所述代码段泄漏在实践中是可以接受的(并且可能通过避免小心使用的dlclose)

今天计算机速度非常快.您可以在每次REPL交互中生成一个小插件(少于一千行C),并编译并加载它(对于这么小的插件,它通常需要不到0.1秒),这实际上与人类兼容互动(我在我过时的GCC MELT项目中这样做了;我将在bismon-chariot-doc.pdf草案报告中描述的bismon项目中这样做).


使用JIT编译库

要在运行时动态生成代码,您还可以使用一些JIT编译库.其中有很多,包括libgccjit,LLVM(在C++中),asmjit,libjit,GNU 闪电,tinycc及其libtcc.其中一些能够快速发出机器代码,但那时代码的性能可能不是很好.其他正在进行优化,比如一个好的C编译器(特别是里面使用GCC的libgccjit).当然,这些优化需要一些时间,因此机器代码的发送速度要慢得多,但其性能与优化的C代码一样好.


嵌入口译员

在许多情况下,脚本语言提供某种评估.一些口译员的设计容易嵌入您的应用程序中(注意事项和注意事项),尤其是LuaGuile(和Nim).许多解释器(Ocaml,Python,Perl,Parrot,Ruby,...)也可以以某种方式嵌入到您的应用程序中(您可能需要了解垃圾收集术语).当然,所有都需要一些编码约定.实际上,解释器比编译代码慢.


术语

任何人都可以告诉我,如果有可能用C语言,或任何不同的语言,这个概念怎么称呼?

您可能需要阅读有关元编程,评估,多级编程,同性,反射,延续,类型内省,堆栈跟踪的更多信息(请考虑Ian Taylor的libbacktrace).

您可能对类似Lisp的语言感兴趣.首先阅读SICP,然后阅读Queinnec的书" Lisp In Small Pieces"和Scott的书" Programming Language Pragmatics".请注意,SBCL(Common Lisp实现)在每次REPL交互时都会生成机器代码.

既然你提到人工智能的兴趣,你可以看看J.Pitrat的博客.他的CAIA系统是自引导的,因此产生了其C代码的总体(接近500KLOC).