C ++에서 런타임 동안 동적으로 함수를 만들 수 있습니까?
C ++는 정적이고 컴파일 된 언어이며 템플릿은 컴파일 시간 동안 확인됩니다.
그러나 런타임 중에 소스 코드에 설명되어 있지 않고 컴파일 중에 기계 언어로 변환되지 않은 함수를 생성하여 사용자가 소스에서 예상하지 못한 데이터를 던질 수 있도록 할 수 있습니까?
나는 이것이 간단한 방식으로 일어날 수 없다는 것을 알고 있지만, 확실히 가능해야합니다. 컴파일되지 않은 프로그래밍 언어가 많이 있고 C 또는 C ++로 구현되는 그런 종류의 것을 동적으로 생성합니다.
모든 기본 유형에 대한 팩토리가 적절한 데이터 구조와 함께 생성되어 사용자 유형 및 함수와 같은 더 복잡한 개체로 구성되는 경우 이것이 가능할까요?
주제에 대한 모든 정보와 온라인 자료에 대한 포인터를 환영합니다. 감사!
편집 : 가능하다는 것을 알고 있으며 구현 세부 사항에 관심이있는 것 같습니다. :)
예 , 물론 다른 답변에 언급 된 도구가 없지만 단순히 C ++ 컴파일러를 사용합니다 .
C ++ 프로그램 내에서 다음 단계를 따르십시오 (리눅스에서는 있지만 다른 OS에서도 유사해야 함).
- 다음을 사용하여 C ++ 프로그램을 파일 (예 : /tmp/prog.cc)에 작성합니다.
ofstream
- 다음을 통해 프로그램을 컴파일하십시오.
system("c++ /tmp/prog.cc -o /tmp/prog.so -shared -fPIC");
- 프로그램을 동적으로로드합니다.
dlopen()
또한 바이트 코드를 함수에 직접 제공하고 아래에 설명 된대로 함수 유형으로 캐스팅 된 상태로 전달할 수도 있습니다.
예 :
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
C ++ 런타임 방법은 앞서 언급에 따라 컴파일 (출력 파일에 코드를 작성, 컴파일을 통해 대한 예를 아래에 system()
, 부하를 통해 dlopen()
하고 dlsym()
). 관련 질문 의 예를 참조하십시오 . 여기서 차이점은 함수가 아닌 클래스를 동적으로 컴파일한다는 것입니다. 이는 maker()
동적으로 컴파일 될 코드에 C 스타일 함수를 추가하여 수행됩니다 . 참조 :
이 예제는 Linux에서만 작동 LoadLibrary
하며 (Windows에는 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
단순히 임베디드 스크립팅 언어 ( Lua 는 임베딩에 적합)를 사용하거나 런타임에 사용할 C ++ 용 컴파일러를 직접 작성 하는 것 외에도 C ++를 정말로 사용하려는 경우 기존 컴파일러를 사용할 수 있습니다.
For example Clang is a C++ compiler built as libraries that could be easily embedded in another program. It was designed to be used from programs like IDEs that need to analyze and manipulate C++ source in various ways, but using the LLVM compiler infrasructure as a backend it also has the ability to generate code at runtime and hand you a function pointer that you can call to run the generated code.
Essentially you will need to write a C++ compiler within your program (not a trivial task), and do the same thing JIT compilers do to run the code. You were actually 90% of the way there with this paragraph:
I am aware this cannot happen in a straightforward way, but surely it must be possible, there are plenty of programing languages that are not compiled and create that sort of stuff dynamically that are implemented in either C or C++.
Exactly--those programs carry the interpreter with them. You run a python program by saying python MyProgram.py
--python is the compiled C code that has the ability to interpret and run your program on the fly. You would need do something along those lines, but by using a C++ compiler.
If you need dynamic functions that badly, use a different language :)
Have a look at libtcc; it is simple, fast, reliable and suits your need. I use it whenever I need to compile C functions "on the fly".
In the archive, you will find the file examples/libtcc_test.c, which can give you a good head start. This little tutorial might also help you: http://blog.mister-muffin.de/2011/10/22/discovering-tcc/
Ask questions in the comments if you meet any problems using the library!
A typical approach for this is to combine a C++ (or whatever it's written on) project with scripting language.
Lua is one of the top favorites, since it's well documented, small, and has bindings for a lot of languages.
But if you are not looking into that direction, perhaps you could think of making a use of dynamic libraries?
Yes - you can write a compiler for C++, in C++, with some extra features - write your own functions, compile and run automatically (or not)...
Have a look into ExpressionTrees
in .NET - I think this is basically what you want to achieve. Create a tree of subexpressions and then evaluate them. In an object-oriented fashion, each node in the might know how to evaluate itself, by recursion into its subnodes. Your visual language would then create this tree and you can write a simple interpreter to execute it.
Also, check out Ptolemy II, as an example in Java on how such a visual programming language can be written.
You could take a look at Runtime Compiled C++ (or see RCC++ blog and videos), or perhaps try one of its alternatives.
It worked for me like this. You have to use the -fpermissive flag. I am using CodeBlocks 17.12.
#include <cstddef>
using namespace std;
int main()
{
char func[] = {'\x90', '\x0f', '\x1'};
void (*func2)() = reinterpret_cast<void*>(&func);
func2();
return 0;
}
성능을 원하지 않는 경우 사용 가능한 가장 간단한 솔루션은 Lua 또는 Python 과 같은 스크립팅 언어 인터프리터를 포함하는 것 입니다.
'development' 카테고리의 다른 글
C #-빈 목록 사용 방법 (0) | 2021.01.10 |
---|---|
네트워크 포트에서 수신하고 데이터를 텍스트 파일에 저장 (0) | 2021.01.10 |
-[MyClassName copyWithZone :] 인식 할 수없는 선택기가 인스턴스로 전송되었습니다. (0) | 2021.01.10 |
Java 패키지 이름이 소문자 여야하는 이유는 무엇입니까? (0) | 2021.01.10 |
Rmarkdown / knit에서 R 코드를 숨기고 결과 만 표시 (0) | 2021.01.10 |