有什么办法在C或C ++运行时编译额外的代码?

这是我想要做的:

  1. 运行一个程序并初始化一些数据结构。
  2. 然后编译其他可以访问/修改现有数据结构的代码。
  3. 根据需要重复步骤2。

我希望能够在类Unix系统(特别是Linux和Mac OS X)上使用gcc (最终是Java )在CC++执行此操作。 这个想法基本上是为这些语言实现一个read-eval-print循环,这些语言在input时编译expression式和语句,并使用它们修改现有的数据结构(在脚本语言中始终进行的操作)。 我正在写这个工具在python ,它生成的C / C++文件,但这不应该是相关的。

我已经探讨了如何使用共享库,但了解到修改共享库不会影响已经运行的程序。 我也尝试使用共享内存,但无法find加载到堆的函数的方法。 我也考虑过使用汇编代码,但还没有试图这样做。

我宁愿不使用gcc以外的编译器,除非在gcc绝对没有办法做到这一点。

如果任何人有任何想法或知道如何做到这一点,任何帮助将不胜感激。

我想你可以使用动态库来完成这个工作,并在运行时加载(使用dlopen和朋友)。

 void * lib = dlopen("mynewcode.so", RTLD_LAZY); if(lib) { void (*fn)(void) = dlsym(lib, "libfunc"); if(fn) fn(); dlclose(lib); } 

当你走的时候,你显然必须编译新的代码,但如果你不断更换mynewcode.so我认为这将适用于你。

有一个简单的解决方案:

  1. 创建自己的具有特殊功能的库
  2. 加载创建的库
  3. 从该库执行函数,将结构作为函数变量传递

要使用你的结构,你必须包含相同的头文件,如主机应用程序。

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) { sa += sa; sb *= sb; }\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; sa = i; sb = i; // use function fn(s); cout << sa << " " << sb << 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替换它的实际执行,并用共享内存保存资源。

尽管现在LLVM现在主要用于编译中的优化和后端角色,但它的核心是低级虚拟机。

LLVM可以使用JIT代码,即使返回类型可能是非常不透明的,所以如果你准备把自己的代码包装起来,不用担心将要发生的转换,它可能会帮助你。

但是C和C ++对于这种事情并不是很友善。

是的 – 您可以使用Runtime Compiled C ++ (或查看RCC ++博客和视频 )或其中一个替代方法来实现此目的 。

如果没有其他的工作 – 尤其是,如果卸载一个共享库最终不能在你的运行时平台上得到支持,那么你可以这么做。

1)使用系统()或任何执行海湾合作委员会或作出或任何建立的代码

2)将它作为平面二进制链接,或者自己分析链接器输出在平台上的任何格式(精灵?)

3)通过mmap()创建一个可执行文件,或者做一个匿名mmap,并设置执行位并复制/解压缩代码(不是所有的平台都关心这一点,但是让我们假设你有一个呢)

4)刷新任何数据和指令缓存(因为两者之间的一致性通常不能保证)

5)通过函数指针或者其他来调用它

当然,还有另外一个选择 – 根据你所需要的交互级别,你可以建立一个单独的程序,启动它并等待结果,或者分叉并启动它,并通过管道或套接字与它交谈。 如果这能满足你的需求,那将是一个不那么棘手的问题。

这可以通过OpenCL轻松完成

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=clCreatecoreelsInProgram(program,1,&kernel,0); if(e)exit(e); e=clSetcoreelArg(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 }