Подтвердить что ты не робот

Можно ли динамически создавать функцию во время выполнения на С++?

С++ - это статический, скомпилированный язык, шаблоны разрешаются во время компиляции и т.д.

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

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

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

Любая информация по этому предмету, а также указатели на онлайн-материалы приветствуются. Спасибо!

EDIT: я знаю, что это возможно, это больше похоже на интерес к деталям реализации:)

4b9b3361

Ответ 1

Да, конечно, без каких-либо инструментов, упомянутых в других ответах, но просто с помощью компилятора С++.

просто следуйте этим шагам из своей программы на С++ (в Linux, но должен быть похож на другую ОС)

  • запишите программу С++ в файл (например, в /tmp/prog.cc), используя ofstream
  • скомпилируйте программу через system("c++ /tmp/prog.cc -o /tmp/prog.so -shared -fPIC");
  • загружать программу динамически, например. используя dlopen()

Ответ 2

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

например.

byte[3] func = { 0x90, 0x0f, 0x1 }
*reinterpret_cast<void**>(&func)()

Ответ 3

Да, JIT-компиляторы делают это все время. Они выделяют часть памяти, которой ОС предоставляет особые права выполнения, затем заполняют ее кодом и накладывают указатель на указатель на функцию и выполняют ее. Довольно просто.

EDIT: Вот пример того, как это сделать в Linux: http://burnttoys.blogspot.de/2011/04/how-to-allocate-executable-memory-on.html

Ответ 4

В дополнение к простому использованию встроенного языка сценариев (Lua отлично подходит для встраивания) или написания собственного компилятора для С++ для использования в runtime, если вы действительно хотите использовать С++, вы можете просто использовать существующий компилятор.

Например Clang - это компилятор С++, созданный как библиотеки, которые могут быть легко внедрены в другую программу. Он был разработан для использования с такими программами, как IDE, которые должны анализировать и управлять источником С++ различными способами, но используя инфраструктуру компилятора LLVM как бэкэнд, он также имеет возможность генерировать код во время выполнения и передает вам указатель на функцию, который вы можете вызвать для запуска сгенерированного кода.

Ответ 5

Ниже приведен пример компиляции С++ во время выполнения на основе упомянутого выше метода (напишите код в выходной файл, скомпилируйте через system(), загрузите через dlopen() и dlsym()). См. Также пример в Связанный вопрос. Разница здесь заключается в том, что она динамически компилирует класс, а не функцию. Это достигается добавлением функции C-style maker() к динамически компилируемому коду. Ссылки:

Пример работает только под Linux (вместо этого у Windows есть функции LoadLibrary и GetProcAddress) и требуется, чтобы идентичный компилятор был доступен на целевой машине.

baseclass.h

#ifndef BASECLASS_H
#define BASECLASS_H
class A
{
protected:
    double m_input;     // or use a pointer to a larger input object
public:
    virtual double f(double x) const = 0;
    void init(double input) { m_input=input; }
    virtual ~A() {};
};
#endif /* BASECLASS_H */

main.cpp

#include "baseclass.h"
#include <cstdlib>      // EXIT_FAILURE, etc
#include <string>
#include <iostream>
#include <fstream>
#include <dlfcn.h>      // dynamic library loading, dlopen() etc
#include <memory>       // std::shared_ptr

// compile code, instantiate class and return pointer to base class
// https://www.linuxjournal.com/article/3687
// http://www.tldp.org/HOWTO/C++-dlopen/thesolution.html
// https://stackoverflow.com/questions/11016078/
// https://stackoverflow.com/questions/10564670/
std::shared_ptr<A> compile(const std::string& code)
{
    // temporary cpp/library output files
    std::string outpath="/tmp";
    std::string headerfile="baseclass.h";
    std::string cppfile=outpath+"/runtimecode.cpp";
    std::string libfile=outpath+"/runtimecode.so";
    std::string logfile=outpath+"/runtimecode.log";
    std::ofstream out(cppfile.c_str(), std::ofstream::out);

    // copy required header file to outpath
    std::string cp_cmd="cp " + headerfile + " " + outpath;
    system(cp_cmd.c_str());

    // add necessary header to the code
    std::string newcode =   "#include \"" + headerfile + "\"\n\n"
                            + code + "\n\n"
                            "extern \"C\" {\n"
                            "A* maker()\n"
                            "{\n"
                            "    return (A*) new B(); \n"
                            "}\n"
                            "} // extern C\n";

    // output code to file
    if(out.bad()) {
        std::cout << "cannot open " << cppfile << std::endl;
        exit(EXIT_FAILURE);
    }
    out << newcode;
    out.flush();
    out.close();

    // compile the code
    std::string cmd = "g++ -Wall -Wextra " + cppfile + " -o " + libfile
                      + " -O2 -shared -fPIC &> " + logfile;
    int ret = system(cmd.c_str());
    if(WEXITSTATUS(ret) != EXIT_SUCCESS) {
        std::cout << "compilation failed, see " << logfile << std::endl;
        exit(EXIT_FAILURE);
    }

    // load dynamic library
    void* dynlib = dlopen (libfile.c_str(), RTLD_LAZY);
    if(!dynlib) {
        std::cerr << "error loading library:\n" << dlerror() << std::endl;
        exit(EXIT_FAILURE);
    }

    // loading symbol from library and assign to pointer
    // (to be cast to function pointer later)
    void* create = dlsym(dynlib, "maker");
    const char* dlsym_error=dlerror();
    if(dlsym_error != NULL)  {
        std::cerr << "error loading symbol:\n" << dlsym_error << std::endl;
        exit(EXIT_FAILURE);
    }

    // execute "create" function
    // (casting to function pointer first)
    // https://stackoverflow.com/questions/8245880/
    A* a = reinterpret_cast<A*(*)()> (create)();

    // cannot close dynamic lib here, because all functions of the class
    // object will still refer to the library code
    // dlclose(dynlib);

    return std::shared_ptr<A>(a);
}


int main(int argc, char** argv)
{
    double input=2.0;
    double x=5.1;
    // code to be compiled at run-time
    // class needs to be called B and derived from A
    std::string code =  "class B : public A {\n"
                        "    double f(double x) const \n"
                        "    {\n"
                        "        return m_input*x;\n"
                        "    }\n"
                        "};";

    std::cout << "compiling.." << std::endl;
    std::shared_ptr<A> a = compile(code);
    a->init(input);
    std::cout << "f(" << x << ") = " << a->f(x) << std::endl;

    return EXIT_SUCCESS;
}

Выход

$ g++ -Wall -std=c++11 -O2 -c main.cpp -o main.o   # c++11 required for std::shared_ptr
$ g++ -ldl main.o -o main
$ ./main
compiling..
f(5.1) = 10.2

Ответ 6

По существу вам нужно будет написать компилятор С++ в вашей программе (не тривиальная задача) и сделать то же самое, что и компиляторы JIT для запуска кода. Фактически вы были на 90% от этого пункта:

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

Точно - эти программы несут с ними интерпретатор. Вы запускаете программу python, говоря python MyProgram.py - python - это скомпилированный C-код, который способен интерпретировать и запускать вашу программу "на лету". Вам нужно будет что-то делать по этим строкам, но с помощью компилятора С++.

Если вам нужны динамические функции, которые плохо, используйте другой язык:)

Ответ 7

Посмотрите libtcc; это просто, быстро, надежно и соответствует вашим потребностям. Я использую его всякий раз, когда мне нужно скомпилировать функции C "на лету".

В архиве вы найдете файл examples/libtcc_test.c, который может дать вам хороший начальный старт. Этот небольшой учебник также может помочь вам: http://blog.mister-muffin.de/2011/10/22/discovering-tcc/

Задавайте вопросы в комментариях, если вы встретите какие-либо проблемы с помощью библиотеки!

Ответ 8

Да - вы можете написать компилятор для С++, на С++, с некоторыми дополнительными функциями - написать свои собственные функции, скомпилировать и запустить автоматически (или нет)...

Ответ 9

Самое простое доступное решение, если вы не ищете производительность, - это встраивание интерпретатора языка сценариев, например. для Lua или Python.

Ответ 10

Взгляните на ExpressionTrees в .NET - я думаю, что это в основном то, чего вы хотите достичь. Создайте дерево подвыражений, а затем оцените их. Объектно-ориентированным способом каждый node может знать, как оценить себя, путем рекурсии в свои подносы. Тогда ваш визуальный язык создаст это дерево, и вы можете написать простой интерпретатор для его выполнения.

Кроме того, посмотрите Ptolemy II, как пример в Java о том, как можно записать такой язык визуального программирования.

Ответ 12

Типичный подход для этого заключается в объединении проекта С++ (или того, что он написал) с языком сценариев.
Lua является одним из лучших фаворитов, поскольку он хорошо документирован, мал и имеет привязки для большого количества языков.

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