C++ 有没有办法在运行时用C或C+编译其他代码+;?

C++ 有没有办法在运行时用C或C+编译其他代码+;?,c++,c,linux,gcc,runtime-compilation,C++,C,Linux,Gcc,Runtime Compilation,以下是我想做的: 运行一个程序并初始化一些数据结构 然后编译可以访问/修改现有数据结构的附加代码 根据需要重复步骤2 我希望能够在类Unix系统(特别是Linux和Mac OS X)上使用gcc(以及最终的Java)使用C和C++实现这一点。其基本思想是为这些语言实现一个read-eval-print循环,在输入表达式和语句时编译它们,并使用它们修改现有的数据结构(在脚本语言中一直都是这样做的)。我正在用python编写这个工具,它生成C/C++文件,但这不应该是相关的 我已经探索过如何使用共享

以下是我想做的:

  • 运行一个程序并初始化一些数据结构
  • 然后编译可以访问/修改现有数据结构的附加代码
  • 根据需要重复步骤2
  • 我希望能够在类Unix系统(特别是Linux和Mac OS X)上使用
    gcc
    (以及最终的
    Java
    )使用
    C
    C++
    实现这一点。其基本思想是为这些语言实现一个read-eval-print循环,在输入表达式和语句时编译它们,并使用它们修改现有的数据结构(在脚本语言中一直都是这样做的)。我正在用
    python
    编写这个工具,它生成
    C
    /
    C++
    文件,但这不应该是相关的

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

    我宁愿不使用除
    gcc
    之外的任何编译器,除非在
    gcc
    中绝对没有办法这样做


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

    我认为您可以使用动态库并在运行时加载它们(使用
    dlopen
    和friends)来实现这一点


    显然,您必须在继续进行的过程中编译新代码,但是如果您继续替换
    mynewcode。因此
    我认为这将对您有效。

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

    LLVM可以JIT代码,即使返回类型可能非常不透明,因此,如果您准备好将自己的代码包装在它周围,并且不太担心将要发生的强制转换,它可能会帮助您


    <>但是,C和C++对这种事情并不友好。

    如果没有其他的工作-特别是,如果不加载共享库,在运行时平台上不支持,那么你可以很难做到。 1) 使用system()或其他命令来执行gcc或make或其他命令来构建代码

    2) 要么将其链接为平面二进制文件,要么自己解析链接器在平台上输出的任何格式(elf?)

    3) 获取一些可执行页面,可以通过mmap()打包一个可执行文件,也可以使用execute位集执行匿名mmap,并在那里复制/解包代码(并非所有平台都关心这个位,但假设您有一个这样做的平台)

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

    5) 通过函数指针或其他方式调用它


    当然还有另一种选择——根据您需要的交互级别,您可以构建一个单独的程序,然后启动它并等待结果,或者分叉并启动它并通过管道或套接字与它通信。如果这能满足您的需要,就不会那么棘手了。

    有一个简单的解决方案:

  • 创建自己的具有特殊功能的库
  • 加载创建的库
  • 执行该库中的函数,将结构作为函数变量传递
  • 要使用您的结构,您必须像在主机应用程序中一样包含相同的头文件

    structs.h:

    struct S {
        int a,b;
    };
    
    #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
    
    main.cpp:

    struct S {
        int a,b;
    };
    
    #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,并使用共享内存保存资源。

    是-您可以使用(或查看),或者它的一个功能。

    这可以通过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
    }
    
    苹果公司__ #包括 #否则 #包括 #恩迪夫 #包括 intmain(intargc,char**argv){//假定源代码字符串位于argv中 cl_int e=0;//错误状态指示器 cl_平台_id平台=0; cl\U设备\U id设备=0; e=clGetPlatformIDs(1,平台,0);如果(e)退出(e); e=CLGetDeviceID(平台,CL设备类型,全部,1和设备,0);如果(e)退出(e); cl_context context=clCreateContext(0,1,&device,0,0,&e);如果(e)退出(e); cl_命令_队列队列=clCreateCommandQueue(上下文、设备、0和e);如果(e)退出(e); //假设您发布每个程序和内核,下面的代码行可以在一个循环中完成 cl_program program=clCreateProgramWithSource(上下文,argc,(常量字符**)argv,0,&e); cl_内核=0;如果(e)退出(e); e=clBuildProgram(程序,1和设备,0,0,0);如果(e)退出(e); e=clCreateKernelsInProgram(程序,1和内核,0);如果(e)退出(e); e=clSetKernelArg(内核,0,sizeof(int),&argc);如果(e)退出(e); e=clEnqueueTask(队列,内核,0,0,0);如果(e)退出(e); //实际上,您还需要一些缓冲区操作来完成有用的工作 }
    你应该看看/clang。但是C或C++的RePL听起来像是一个相当大的任务。有什么关系吗?我宁愿使用真实的脚本语言,除非你能说出所有这些都应该支持什么,我不确定没有加载。