Вопрос по linux, c++, gcc, c – Есть ли способ компилировать дополнительный код во время выполнения в C или C ++?

12

Вот что я хочу сделать:

Run a program and initialize some data structures. Then compile additional code that can access/modify the existing data structures. Repeat step 2 as needed.

Я хочу быть в состоянии сделать это с обоимиC а такжеC++ с помощьюgcc (и в конце концовJava) в Unix-подобных системах (особенно в Linux и Mac OS X). Идея состоит в том, чтобы в основном реализовать цикл read-eval-print для этих языков, который компилирует выражения и операторы по мере их ввода и использует их для изменения существующих структур данных (что постоянно делается на языках сценариев). Я пишу этот инструмент вpython, который генерируетC/C++ файлы, но это не должно быть актуальным.

Я пытался сделать это с общими библиотеками, но узнал, что изменение общих библиотек не влияет на программы, которые уже запущены. Я также пытался использовать разделяемую память, но не смог найти способ загрузить функцию в кучу. Я также рассмотрел использование ассемблерного кода, но еще не пытался это сделать.

Я бы предпочел не использовать какие-либо компиляторы, кромеgcc если нет абсолютно никакого способа сделать это вgcc.

Если у кого-то есть идеи или они знают, как это сделать, любая помощь будет принята с благодарностью.

Ты должен посмотреть на LLVM / Лязг. Но REPL для C или C ++ звучит как довольно сложная задача. Mat
Что-то связанное сэт? jperelli
Я бы предпочел использовать настоящий язык сценариев, за исключением того, что ты можешь сказать, для чего все это LeleDumbo

Ваш Ответ

6   ответов
10

что вы можете сделать это, используя динамические библиотеки и загружая их во время выполнения (используяdlopen и друзья)

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

    if(fn) fn();
    dlclose(lib);
}

Очевидно, вам придется компилировать новый код по мере продвижения, но если вы продолжите заменятьmynewcode.so Думаю, это подойдет тебе.

Загрузка должна поддерживаться, я не уверен, чтоОО загрузка поддерживается во всех случаях. Chris Stratton
@ ChrisStratton: признаюсь, я Далеко от эксперта по загрузке во время выполнения, но страница руководства заставляет меня верить, что символы выгружаются при dlclose (в частности,RTLD_NODELETE флаг). Возьмите все это с зерном соли, хотя:). Stephen Newell
12

создать собственную библиотеку, имеющую специальные функцииload созданная библиотека выполнять функции из этой библиотеки, передавать структуры как переменные функции

Чтобы использовать свои структуры, вы должны включить те же заголовочные файлы, что и в главном приложении.

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 и экономьте ресурсы с общей памятью.

очень полезная информация, но как бы вы включили main.cpp в tmp.cpp? dreamer_999
Хорошо, я собирался отредактировать вопрос, чтобы ответить вам, но в этом нет необходимости :) Вы не можете включить main.cpp в tmp. Если вы хотите поделиться некоторыми данными, то вы должны использовать заголовки (или записать их непосредственно в файл) и передавать структуры в динамически создаваемую функцию:) kravemir
thnx! однако при использовании заголовков значения общих переменных не совпадают. так что я в конечном итоге должен передать их функции. мне интересно, есть ли способ обойти передаваемые переменные dreamer_999
Если вы включаете заголовок в cpp, он такой же, как если бы вы записали его содержимое в cpp. Таким образом, вы получите два экземпляра переменных (main.cpp и динамическая библиотека), но если у вас есть заголовок (определяющий переменные), включенный в два объекта (cpp-s) в одной и той же библиотеке, то это приведет к ошибке при компиляции. Вы должны использовать ключевое слово "extern" в заголовке, чтобы сообщить компилятору, эти переменные не создаются в текущем объекте (cpp) и будут связаны компоновщиком. Вы можете сделать переменные «статическими», они будут создаваться для каждого объекта индивидуально, но вы все равно ничего не поделитесь. kravemir
4

что LLVM сегодня используется в основном для его оптимизации и внутренних функций при компиляции, его ядром является низкоуровневая виртуальная машина.

LLVM может JIT-код, хотя типы возвращаемых данных могут быть довольно непрозрачными, поэтому, если вы готовы обернуть свой собственный код вокруг него и не слишком беспокоиться о предстоящих приведениях, это может вам помочь.

Однако C и C ++ не очень дружелюбны для такого рода вещей.

3

Runtime Скомпилировано C ++ (или посмотрите на RCC ++ блог и видео) или один из его Альтернативы.

2

если выгрузка совместно используемой библиотеки не поддерживается на вашей платформе времени выполнения, вы можете сделать это трудным путем.

1) использовать system () или что-то еще для выполнения gcc или make или что-то еще для сборки кода

2) либо связать его как простой двоичный файл, либо проанализировать любой формат (эльф?), Который выводит компоновщик на вашей платформе

3) найдите себе исполняемые страницы, либо запустив mmap () исполняемый файл, либо сделайте анонимный mmap с установленным битом выполнения и скопируйте / распакуйте там свой код (не все платформы заботятся об этом бите, но давайте предположим, что вы есть тот, который делает)

4) очистить любые кэши данных и инструкций (поскольку согласованность между ними обычно не гарантируется)

5) вызывать его через указатель на функцию или как угодно

Конечно, есть и другой вариант - в зависимости от уровня взаимодействия, который вам нужен, вы можете создать отдельную программу и либо запустить ее и дождаться результата, либо отключить и запустить ее и поговорить с ней по каналам или сокетам. Если бы это отвечало вашим потребностям, это было бы намного менее сложно.

2
Это можно сделать с помощью OpenC

OpenCL это широко поддерживается стандарт, в основном используется для выгрузки вычислений на специализированное оборудование, такое как графические процессоры. Тем не менее, он также отлично работает на процессорах и фактически выполняет компиляцию кода, подобного 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
}

Похожие вопросы