簡體   English   中英

計算在C語言中使用哪個函數(或某種代碼)

[英]Compute which function (or kind of code) to use in C

我有一個不尋常的問題。 假設我有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或任何其他語言編寫?如何稱呼這個概念?

您可能需要了解什么是closurescallbacks 函數指針很有用,但僅靠函數指針可能還不夠(請閱讀更多內容,以了解函數指針如何對實現閉包有用)。

您應該了解有關C的更多信息,因此請閱讀有關C編程的好書。 我建議下載C11標准n1570並瀏覽一下。 一個非常重要的概念是不確定的行為 ,您應該對此感到恐懼

是否可以某種方式計算(例如,在char數組中,但也可以通過其他任何方式)某種類型的代碼,我將在稍后插入並使用。 但不是字符串,而是直接像代碼。

在純標准C代碼中這是不可能的(因為構成程序的轉換單元的集合是固定的),並且原則上任何有效的函數指針值都應指向某個現有函數(因此嚴格來說,調用任何其他函數指針值將是未定義的)行為)。 然而,一些實現是能夠構建 (在一些實施的具體方式),不知何故“有效”的新函數指針(但那是C11的標准之外 )。 在代碼位於ROM的純哈佛架構上 ,這甚至是不可能的。

生成並動態加載插件

但是,如果您在現代操作系統 (我建議使用Linux)下運行,則可以使用動態加載插件功能。 我特別關注Linux(對於Windows,細節有很大不同,而細節是罪惡;請閱讀Levine的書Linkers and Loaders )。 閱讀操作系統:三篇簡單的文章,以了解有關OS的更多信息。

因此,您可以做的(在Linux上)是在運行時在某些臨時文件(例如/tmp/generated.c )中生成一些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);
}

由於您關心函數指針,因此,如果將其簽名聲明為一種類型,則代碼將更具可讀性:

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程序可以生成並加載數十萬個(如果您足夠耐心的話,甚至可能是數百萬個)插件。 代碼段泄漏在實踐中是可以接受的(可以通過謹慎使用dlclose避免)

和計算機今天非常快。 您可能會在每個REPL交互中生成一個小插件(少於一千行C),然后對其進行編譯和加載(對於這樣的小插件,通常只需不到0.1秒的時間),並且實際上與人類兼容交互(我在過時的GCC MELT項目中進行了此操作;我將在bismon-chariot-doc.pdf報告草案中描述的bismon項目中進行了此操作 )。


使用JIT編譯庫

為了在運行時動態生成代碼,您還可以使用一些JIT編譯庫。 有許多人,包括libgccjitLLVM (在C ++), asmjitlibjit ,GNU 閃電tinycclibtcc 它們中的一些能夠快速發出機器代碼,但是該代碼的性能可能不是很好。 其他人則在進行優化,例如使用良好的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)。

嘗試使用函數指針數組;

#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);
}

使用函數指針數組。 首先定義格式:

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]();
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM