[英]Is there any way to compile additional code at runtime in C or C++?
這是我想要做的:
我希望能夠在類Unix系統(尤其是Linux和Mac OS X)上使用gcc
(最終是Java
)同時使用C
和C++
。 我們的想法是基本上為這些語言實現一個read-eval-print循環,它們在輸入時編譯表達式和語句,並使用它們來修改現有的數據結構(在腳本語言中一直都是這樣)。 我在python
編寫這個工具,它生成C
/ C++
文件,但這不應該是相關的。
我已經探討了使用共享庫執行此操作,但了解到修改共享庫不會影響已在運行的程序。 我也嘗試過使用共享內存但無法找到將函數加載到堆上的方法。 我還考慮過使用匯編代碼但尚未嘗試這樣做。
我寧願不使用除gcc
之外的任何編譯器,除非在gcc
絕對沒有辦法。
如果有人有任何想法或知道如何做到這一點,任何幫助將不勝感激。
有一個簡單的解決方案:
要使用您的結構,您必須包含相同的頭文件,如在宿主應用程序中。
structs.h:
struct S {
int a,b;
};
main.cpp中:
#include <iostream>
#include <fstream>
#include <dlfcn.h>
#include <stdlib.h>
#include "structs.h"
using namespace std;
int main ( int argc, char **argv ) {
// create own program
ofstream f ( "tmp.cpp" );
f << "#include<stdlib.h>\n#include \"structs.h\"\n extern \"C\" void F(S &s) { s.a += s.a; s.b *= s.b; }\n";
f.close();
// create library
system ( "/usr/bin/gcc -shared tmp.cpp -o libtmp.so" );
// load library
void * fLib = dlopen ( "./libtmp.so", RTLD_LAZY );
if ( !fLib ) {
cerr << "Cannot open library: " << dlerror() << '\n';
}
if ( fLib ) {
int ( *fn ) ( S & ) = dlsym ( fLib, "F" );
if ( fn ) {
for(int i=0;i<11;i++) {
S s;
s.a = i;
s.b = i;
// use function
fn(s);
cout << s.a << " " << s.b << endl;
}
}
dlclose ( fLib );
}
return 0;
}
輸出:
0 0
2 1
4 4
6 9
8 16
10 25
12 36
14 49
16 64
18 81
20 100
您還可以創建可更改的程序 (源代碼),重新編譯自己,然后用execv
替換它的實際執行,並使用共享內存保存資源。
我認為您可以使用動態庫並在運行時加載它們(使用dlopen
和朋友)來完成此任務。
void * lib = dlopen("mynewcode.so", RTLD_LAZY);
if(lib) {
void (*fn)(void) = dlsym(lib, "libfunc");
if(fn) fn();
dlclose(lib);
}
您顯然必須mynewcode.so
編譯新代碼,但如果您繼續更換mynewcode.so
我認為這對您mynewcode.so
。
盡管LLVM現在主要用於編譯中的優化和后端角色,但它的核心是低級虛擬機。
LLVM可以JIT代碼,即使返回類型可能非常不透明,所以如果你准備好自己包裝自己的代碼並且不要過多擔心將要發生的轉換,它可能對你有所幫助。
但是C和C ++對這種事情並不友好。
是的 - 您可以使用Runtime Compiled C ++ (或查看RCC ++博客和視頻 )或其中一個替代方案來完成此操作 。
OpenCL是一種廣泛支持的標准,主要用於將計算卸載到專用硬件,如GPU。 但是,它在CPU上工作得很好,並且實際上執行類似C99的代碼的運行時編譯作為其核心功能之一(這就是如何實現硬件可移植性)。 較新的版本(2.1+)也接受大量的C ++ 14子集。
這種運行時編譯和執行的基本示例可能如下所示:
#ifdef __APPLE__
#include<OpenCL/opencl.h>
#else
#include<CL/cl.h>
#endif
#include<stdlib.h>
int main(int argc,char**argv){//assumes source code strings are in argv
cl_int e = 0;//error status indicator
cl_platform_id platform = 0;
cl_device_id device = 0;
e=clGetPlatformIDs(1,&platform,0); if(e)exit(e);
e=clGetDeviceIDs(platform,CL_DEVICE_TYPE_ALL,1,&device,0); if(e)exit(e);
cl_context context = clCreateContext(0,1,&device,0,0,&e); if(e)exit(e);
cl_command_queue queue = clCreateCommandQueue(context,device,0,&e); if(e)exit(e);
//the lines below could be done in a loop, assuming you release each program & kernel
cl_program program = clCreateProgramWithSource(context,argc,(const char**)argv,0,&e);
cl_kernel kernel = 0; if(e)exit(e);
e=clBuildProgram(program,1,&device,0,0,0); if(e)exit(e);
e=clCreateKernelsInProgram(program,1,&kernel,0); if(e)exit(e);
e=clSetKernelArg(kernel,0,sizeof(int),&argc); if(e)exit(e);
e=clEnqueueTask(queue,kernel,0,0,0); if(e)exit(e);
//realistically, you'd also need some buffer operations around here to do useful work
}
如果沒有其他工作 - 特別是,如果卸載共享庫最終在運行時平台上不受支持,那么您可以通過艱難的方式完成。
1)使用system()或其他任何方法來執行gcc或make或其他任何來構建代碼
2)要么將其鏈接為平面二進制文件,要么解析鏈接器自己在平台上輸出的任何格式(elf?)
3)通過mmap()執行可執行文件獲取一些可執行頁面,或者使用執行位設置執行匿名mmap並在那里復制/解壓縮代碼(並非所有平台都關心這一點,但讓我們假設你有一個做)
4)刷新任何數據和指令緩存(因為通常不保證兩者之間的一致性)
5)通過函數指針或其他任何方式調用它
當然還有另一種選擇 - 根據您需要的交互級別,您可以構建一個單獨的程序,然后啟動它並等待結果,或者分叉並啟動它並通過管道或套接字與它通信。 如果這可以滿足您的需求,那就不那么棘手了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.