是否有可能dynamic地创build一个函数,在C ++运行时?
C ++是一种静态的编译语言,模板在编译期间被parsing,等等。
但是在运行时是否可以创build一个函数,这在源代码中没有描述,在编译过程中没有被转换为机器语言,这样用户就可以在源代码中抛出一个没有预料到的数据呢?
我意识到这不可能以一种简单的方式发生,但是一定是可能的,有很多编程语言没有被编译,并且dynamic地创build那些以C或C ++实现的东西。
也许如果创build了所有基本types的工厂,并且有合适的数据结构来将它们组织成更复杂的对象,比如用户types和函数,这是可以实现的吗?
关于这个问题的任何信息,以及在线材料的指针,欢迎。 谢谢!
编辑:我知道这是可能的,这是更像我有兴趣的实现细节:)
是的 ,当然, 没有在其他答案中提到的任何工具 ,但只是使用C ++编译器 。
只需从您的C ++程序(在Linux上,但必须在其他操作系统上类似)
- 用一个
ofstream
将一个C ++程序写入一个文件(例如在/tmp/prog.cc) - 通过
system("c++ /tmp/prog.cc -o /tmp/prog.so -shared -fPIC");
编译程序system("c++ /tmp/prog.cc -o /tmp/prog.so -shared -fPIC");
- dynamic加载程序,例如使用
dlopen()
您也可以直接将字节码提供给一个函数,并将其作为函数types传递,如下所示。
例如
byte[3] func = { 0x90, 0x0f, 0x1 } *reinterpret_cast<void**>(&func)()
是的,JIT编译器一直在做。 他们分配一块已被OS授予特殊执行权限的内存,然后用代码填充它,并将指针转换为函数指针并执行它。 很简单。
编辑:下面是如何在Linux中执行它的示例: http : //burnttoys.blogspot.de/2011/04/how-to-allocate-executable-memory-on.html
除了简单地使用embedded式脚本语言( Lua非常适合embedded)或者编写自己的C ++编译器以便在运行时使用,如果您真的想使用C ++,则可以使用现有的编译器。
例如, Clang是一个C ++编译器,可以很容易地embedded到另一个程序中。 它被devise用于像IDE这样的程序,它们需要以各种方式分析和操作C ++源代码,但是使用LLVM编译器infrasructure作为后端,它也能够在运行时生成代码,并且提供一个函数指针,你可以调用运行生成的代码。
- 铛
- LLVM
下面是一个基于前面提到的方法(写代码输出文件,通过system()
编译,通过dlopen()
和dlsym()
)加载的C ++运行时编译的例子。 另请参阅相关问题中的示例。 这里的区别是它dynamic编译一个类而不是一个函数。 这是通过将C风格的maker()
函数添加到要dynamic编译的代码来实现的。 参考文献:
这个例子只能在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
从本质上讲,您需要在程序中编写一个C ++编译器(而不是一个简单的任务),并且执行JIT编译器运行代码的相同的事情。 你实际上是这个段落的90%:
我意识到这不可能以一种简单的方式发生,但是一定是可能的,有很多编程语言没有被编译,并且dynamic地创build那些以C或C ++实现的东西。
确切地说 – 那些程序带着解释器。 你运行python程序说python MyProgram.py
python是编译的C代码,有能力解释和运行你的程序。 你需要按照这些方法做一些事情,但是通过使用C ++编译器。
如果你需要dynamicfunction,使用不同的语言:)
看看libtcc ; 它简单,快速,可靠,适合您的需求。 每当我需要“即时”编译C函数时,我都会使用它。
在档案中,您会find文件examples / libtcc_test.c ,它可以给你一个良好的开端。 这个小教程也可以帮助你: http : //blog.mister-muffin.de/2011/10/22/discovering-tcc/
如果您在使用图书馆遇到任何问题,请在评论中提问!
是的 – 您可以用C ++编写C ++编译器,并带有一些额外function – 编写您自己的函数,编译并自动运行(或不运行)…
可用的最简单的解决scheme,如果你不寻找性能是embedded一个脚本语言解释器,例如为Lua或Python 。
在.NET中查看ExpressionTrees
– 我想这基本上是你想要实现的。 创build一个子expression式的树,然后评估它们。 在面向对象的方式中,可能通过recursion到其子节点来知道如何评估自身。 你的视觉语言会创build这个树,你可以写一个简单的解释器来执行它。
另外,请查阅Ptolemy II ,作为Java中关于如何编写这种可视化编程语言的示例。
你可以看一下Runtime Compiled C ++ (或者看RCC ++博客和video ),或者试试其中的一个select 。
一个典型的方法就是把C ++(或者其他任何写的)项目与脚本语言结合起来。
Lua是最受欢迎的作品之一,因为它是有据可查的,很小的,并且有很多语言的绑定。
但是,如果你没有看到这个方向,也许你可以考虑使用dynamic库?