我有一个不寻常的问题。假设我有N个功能:
void function1(){ //some code here }
void function2(){ //some code here }
...
void functionN(){ //some code here }
有没有什么方法可以动态计算或找出不使用IF语句的函数?并根据函数名称的名称来调用它? 让我向您展示伪代码,它可以更好地描述情况:
for(int I=1;I<=N;I++){
functionI();
}
我的意思是,是否有可能计算(例如,以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
抱歉,如果我不够清楚。谁能告诉我,是否可以用C或任何其他语言编写?该概念的命名方式是什么?
答案 0 :(得分:5)
您可能需要了解closure和callback是什么。 Function pointers很有用,但单独使用可能还不够(请阅读更多内容,以了解函数指针如何对实现闭包有用)。
您应该了解有关C的更多信息,因此请阅读有关C编程的好书。我建议下载C11标准n1570并浏览一下。一个非常重要的概念是undefined behavior,您应该scared。
如果可以某种方式计算(例如,在char数组中,但也可以通过其他任何方式)某种类型的代码,我将在稍后插入并使用。但不是字符串,而是直接像代码。
在纯标准C代码中这是不可能的(因为构成程序的translation units的集合是固定的),并且原则上任何有效的函数指针值都应指向某个现有函数(因此 stricto sensu 调用任何其他函数指针值将是未定义的行为)。但是,某些实现能够(以某种实现特定的方式)构造,以某种方式“有效”新的函数指针(但这在C11标准的外部中)。在纯粹的Harvard architecture上,代码位于ROM中,这甚至是不可能的。
但是,如果您在现代operating system(我建议使用Linux)下运行,则可以使用dynamic loading和plugin工具。我特别关注Linux(对于Windows,细节有很大不同,而细节是罪恶;请阅读Levine的书Linkers and Loaders)。阅读Operating Systems: Three Easy Pieces,以了解有关操作系统的更多信息。
所以(在Linux上)您可以做的是在运行时在某些临时文件(例如/tmp/generated.c
)中生成一些C代码;您需要定义有关该文件的约定(例如,它定义了一个void pluginfun(int)
函数,该函数具有一些其他所需的属性);然后,您将派生一个编译命令以将其编译为一个共享插件(Drepper读为How to write shared libraries),即一个插件。因此,您的程序可能会运行(例如使用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);
}
由于您关心函数指针,因此,如果将其签名声明为类型,则代码将更具可读性:
typedef void pluginfun_type(int);
然后可以轻松地声明指向该签名的函数的函数指针:
pluginfun_type* fptr = NULL;
,您的程序可以使用dlsym(3)插件中的pluginfun
函数地址:
fptr = (pluginfun_type*) dlsym(dlh, "pluginfun");
if (!fptr) {
fprintf(stderr, "dlsym of pluginfun failed: %s\n", dlerror());
exit(EXIT_FAILURE);
}
最后使用(*fptr)(3)
来调用该函数。
您应使用gcc -O -Wall foo.o bar.o -rdynamic -ldl -o foobarprog
链接主程序。您可能会最后调用dlclose(3),但如果插件中的某个函数仍处于活动状态,则不要这样做。
这种生成插件的方法在Linux上非常有效。我的manydl.c是一个玩具程序,生成“随机” C代码,将该生成的C代码的编译分叉到生成的插件中,然后加载该插件(并且可以重复多次)。它表明,在实践中,Linux程序可以生成并加载数十万个(如果您足够耐心的话,甚至可能是数百万个)插件。实际上,code segments泄漏是可以接受的(可以通过谨慎使用dlclose
来避免)
今天计算机非常快。您可能会在每个REPL交互中生成一个小插件(C少于一千行),然后对其进行编译和加载(对于这样的小插件,通常需要不到0.1秒的时间),也就是说几乎与人类互动兼容(我在过时的GCC MELT项目中做到了这一点;我将在bismon报告草稿中描述的bismon-chariot-doc.pdf项目中做到了这一点)。
要在运行时动态生成代码,还可以使用一些JIT-compilation库。其中有很多,包括libgccjit,LLVM(在C ++中),asmjit,libjit,GNU lightning,tinycc及其{ {1}}。它们中的一些能够快速发出机器代码,但是该代码的性能可能不是很好。其他人则在进行优化,例如使用良好的C编译器(尤其是libgccjit,其内部使用GCC)。当然,这些优化需要一些时间,因此机器代码的发出速度很慢,但是其性能却与优化的C代码一样好。
在许多情况下,脚本语言会提供某种eval。 设计了几个解释器,以便轻松地 嵌入到您的应用程序中(注意和警告),特别是Lua或Guile(和{{3} }。更多的解释器(Ocaml,Python,Perl,Parrot,Ruby等)也可以以某种方式嵌入到您的应用程序中(您可能需要了解Nim术语)。当然,所有都需要一些编码约定。实际上,解释器比编译的代码慢。
任何人都可以告诉我,是否可以用C或任何其他语言编写?如何称呼这个概念?
您可能需要阅读有关garbage collection,metaprogramming,eval,multistage programming,homoiconicity,reflection,{{3} },continuations(考虑伊恩·泰勒(Ian Taylor)的type introspection)。
您可能对stack trace类语言感兴趣。首先阅读libbacktrace,然后阅读Queinnec的书Lisp和Scott的书SICP。请注意,Lisp In Small Pieces(Common Lisp实现)在每次REPL交互时都会生成机器代码。
由于您提到了对人工智能的兴趣,因此可以研究Programming Language Pragmatics。他的CAIA系统是SBCL,因此可以生成其C代码的全部内容(将近500KLOC)。
答案 1 :(得分:4)
尝试使用函数指针数组;
#include <stdlib.h>
#include <stdio.h>
void myfunc1(){printf("1\n");};
void myfunc2(){printf("2\n");};
void myfunc3(){printf("3\n");};
void myfunc4(){printf("4\n");};
void myfunc5(){printf("5\n");};
void (*myfuncs[5])() = {myfunc1, myfunc2, myfunc3, myfunc4, myfunc5};
int main(int argc, char *argv[])
{
for(int i=0;i<5;i++) {
(myfuncs[i])();
}
exit(EXIT_SUCCESS);
}
答案 2 :(得分:4)
使用函数指针数组。首先定义格式:
typedef void func_t (void);
然后创建一个函数指针数组:
func_t* func[n] = {function1, function2, function3};
示例:
#include <stdio.h>
void function1 (void) { puts(__func__); }
void function2 (void) { puts(__func__); }
void function3 (void) { puts(__func__); }
typedef void func_t (void);
#define n 3
int main()
{
func_t* func[n] = {function1, function2, function3};
for(size_t i=0; i<n; i++)
{
func[i]();
}
}