Можно ли динамически создавать функцию во время выполнения на С++?
С++ - это статический, скомпилированный язык, шаблоны разрешаются во время компиляции и т.д.
Но возможно ли создать функцию во время выполнения, которая не описана в исходном коде и не была преобразована в машинный язык во время компиляции, так что пользователь может бросить на нее данные, которые не были ожидаемы в источнике
Я знаю, что это не может произойти прямолинейно, но, конечно же, это должно быть возможно, существует множество программируемых языков, которые не компилируются и не создают такого рода вещи динамически, которые реализованы либо на C, либо на С++.
Может быть, если создаются фабрики для всех примитивных типов, а также подходящие структуры данных для организации их в более сложные объекты, такие как типы пользователей и функции, это достижимо?
Любая информация по этому предмету, а также указатели на онлайн-материалы приветствуются. Спасибо!
EDIT: я знаю, что это возможно, это больше похоже на интерес к деталям реализации:)
Ответы
Ответ 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 о том, как можно записать такой язык визуального программирования.
Ответ 11
Вы можете взглянуть на Runtime Compiled С++ (или посмотреть RCС++ блог и видео), или, возможно, попробуйте один из alternatives.
Ответ 12
Типичный подход для этого заключается в объединении проекта С++ (или того, что он написал) с языком сценариев.
Lua является одним из лучших фаворитов, поскольку он хорошо документирован, мал и имеет привязки для большого количества языков.
Но если вы не смотрите в это направление, возможно, вы могли бы подумать об использовании динамических библиотек?