计算在C语言中使用哪个函数(或某种代码)

时间:2018-11-30 01:31:37

标签: c function dynamic artificial-intelligence

我有一个不寻常的问题。假设我有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或任何其他语言编写?该概念的命名方式是什么?

3 个答案:

答案 0 :(得分:5)

您可能需要了解closurecallback是什么。 Function pointers很有用,但单独使用可能还不够(请阅读更多内容,以了解函数指针如何对实现闭包有用)。

您应该了解有关C的更多信息,因此请阅读有关C编程的好书。我建议下载C11标准n1570并浏览一下。一个非常重要的概念是undefined behavior,您应该scared

  

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

在纯标准C代码中这是不可能的(因为构成程序的translation units的集合是固定的),并且原则上任何有效的函数指针值都应指向某个现有函数(因此 stricto sensu 调用任何其他函数指针值将是未定义的行为)。但是,某些实现能够(以某种实现特定的方式)构造,以某种方式“有效”新的函数指针(但这在C11标准的外部中)。在纯粹的Harvard architecture上,代码位于ROM中,这甚至是不可能的。

生成并动态加载插件

但是,如果您在现代operating system(我建议使用Linux)下运行,则可以使用dynamic loadingplugin工具。我特别关注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编译库

要在运行时动态生成代码,还可以使用一些JIT-compilation库。其中有很多,包括libgccjitLLVM(在C ++中),asmjitlibjit,GNU lightningtinycc及其{ {1}}。它们中的一些能够快速发出机器代码,但是该代码的性能可能不是很好。其他人则在进行优化,例如使用良好的C编译器(尤其是libgccjit,其内部使用GCC)。当然,这些优化需要一些时间,因此机器代码的发出速度很慢,但是其性能却与优化的C代码一样好。


嵌入口译员

在许多情况下,脚本语言会提供某种eval设计了几个解释器,以便轻松地 嵌入到您的应用程序中(注意和警告),特别是LuaGuile(和{{3} }。更多的解释器(Ocaml,Python,Perl,Parrot,Ruby等)也可以以某种方式嵌入到您的应用程序中(您可能需要了解Nim术语)。当然,所有都需要一些编码约定。实际上,解释器比编译的代码慢。


术语

  

任何人都可以告诉我,是否可以用C或任何其他语言编写?如何称呼这个概念?

您可能需要阅读有关garbage collectionmetaprogrammingevalmultistage programminghomoiconicityreflection,{{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]();
  }
}