C ++ 내부에서 외부 파일 실행
프런트 엔드에서 사용자에게 코드를 입력 할 수있는 텍스트 편집기를 제공하는 응용 프로그램을 만들려고 한 다음 해당 코드를 실행하고 결과를 반환합니다. 나만의 leetcode 버전을 학습 프로젝트로 빌드하는 것이 재미있는 프로젝트라고 생각했습니다.
지금은 제공된 코드를 실행하기 위해 수행하는 작업입니다. 내가 지금 구현 한 전부이기 때문에 우리가 파이썬 코드를 실행하고 있다고 가정 해 봅시다.
먼저 사용자가 제출 한 코드를 가져와 주어진 코드가 포함 된 파일을 만듭니다.
std::string python(std::string code){
std::string langCommand = "python3 ";
std::string outFile;
//I am hoping to parallelize this operation so I add threadID to output
outFile = createOutFileName("PythonRunner.py");
std::ofstream output;
output.open(outFile);
output << code;
output.close();
return langCommand + outFile;
}
다음으로 출력 파일을 만들고 이전에 만든 파일을 실행하지만 stdout / stderr를 다른 출력 파일로 보냅니다.
std::string Program::run(){
std::string command = createFile(this->lang, this->code);
this->outputFile = createOutFileName("output.txt");
std::stringstream newCommand;
newCommand << command;
newCommand << ">> ";
newCommand << outputFile;
newCommand << " 2>&1";
system(newCommand.str().c_str());
std::string output = getOutputFileData(this->outputFile);
cleanupFiles(command);
return output;
}
마지막으로 출력 파일에서 얻은 내용을 반환하고 이것이 코드를 실행하는 방법입니다.
더 쉬운 방법이 있다고 생각합니다. 특히 파일에 너무 많은 글을 쓰고 읽는 중이므로 어쨌든 그것을 제거 할 수 있습니까?
또한 앞으로 두 개 이상의 언어를 포함하고 싶으므로 특정 언어에 특정한 라이브러리를 사용하고 싶지 않습니다.
마지막으로, 이것이 저의 첫 번째 C ++ 프로젝트이므로 C ++ 팁을 좋아합니다!
편집 : 결국이 코드를 병렬화하고 프로그램을 캡슐화하여 실행중인 시스템을 손상시킬 수 없도록하는 방법을 찾고 싶습니다. 그것에 대해 좋은 외부 프로그램이 있으면 알려주고 stderr / stdout도 알려주십시오.
편집 : 누군가 요청했듯이 여기에 전체 저장소가 있습니다. https://github.com/lkelly93/coderunner
답변
system()
당신 보다 popen()
.
차이점은 시스템이이 프로세스에 대한 액세스 권한이없는 하위 프로세스에서 명령을 실행하는 반면 popen은 하위 프로세스에서 명령을 실행 하지만 하위 프로세스 의 입력 및 출력 스트림에 대한 액세스를 제공한다는 것입니다.
이렇게하면 하위 프로세스를 실행하고 프로세스에 대한 입력을 직접 (표준 입력에 제공 한 입력 필드에서) 스트리밍 한 다음 프로세스에서 출력을 읽고 사용자 인터페이스의 출력 필드에 쓸 수 있습니다.
FILE* proc = popen(command);
std::string inputFromUser = getUserInputFromUI();
// Using fwrite() correctly left to user.
// You need to check for errors and continue etc.
fwrite(inputFromUser.c_str(), 1, inputFromUser.size(), proc);
char buffer[100];
std::size_t size;
while((size = fread(buffer, 1, 100, proc)) != 0) {
// Check for read errors here.
sendToUserInterface(std::string(bufffer, buffer + size));
}
pclose(proc);
관련 항목을 정렬하면 pythong 스크립트를 파일로 저장할 필요가 없습니다. python 명령 -
은 이름 으로을 허용합니다. 즉, 이름이 지정된 파일이 아닌 표준 입력에서 스크립트를 읽습니다.
따라서 python 명령 (popen () 사용)을 실행 한 다음 생성 된 파일의 입력 스트림에 실행할 스크립트를 작성할 수 있습니다.
이렇게하면 중간 파일이 필요하지 않습니다.
주목할만한 것들 :
- 주석을 사용하여 만든 구현 및 섹션에 대한 문서입니다.
- 단일 책임 원칙을 따르는 클래스, 함수 및 파일 .
- 기능을 설명하는 이름. (아래에 언급 한 변수가 아님)
- 테스트 프레임 워크! 그러나 테스트는 전체 프로그램뿐만 아니라 내부 기능도 확인해야합니다.
파일 스트림 및 파일 이름의 용어는 매우 혼란스럽고 조회 함수 반환 유형 또는 변수 선언을 너무 자주 만듭니다.
Program::outputFile
여기서 명확하지 않은 파일 이름 입니다. 나는 그것을 FILE*
.
다른 곳에서 std::ofstream output;
출력은 프로그램의 출력 내용처럼 들리지만 스트림입니다!
std::string output = getOutputFileData(this->outputFile);
그리고 여기 다시 문자열입니다!
코드는 절대 및 상대 경로를 처리하지 않습니다.
테스트는 다음과 같이 실패합니다.
runnerFiles/0x1005c05c0output.txt does not exist.
이러한 코드를 사용하면 rm
. 기껏해야 모든 일회용 파일을 폴더에 보관하고 사용자에게 삭제하도록 요청하십시오.
std::stringstream newCommand;
newCommand << command;
newCommand << ">> ";
newCommand << outputFile;
newCommand << " 2>&1";
system(newCommand.str().c_str());
std::stringstream
피할 수 있으며 std::string
첫 번째 항목이 인 한 concatenate를 직접 사용할 수 있습니다 std::string
.
std::string newCommand = command + ">> " + outputFile + "2>&1";
그리고 char 버퍼 getOutputFileData
를 사용 하는 코드 FILE*
(할당하지 않은 것!)는 다음으로 대체 될 수 있습니다 (오류 처리 추가).
std::ifstream run_output{outFileLocation};
std::stringstream buffer;
buffer << run_output.rdbuf();
return buffer.str();
- https://stackoverflow.com/questions/2602013/read-whole-ascii-file-into-c-stdstring?noredirect=1&lq=1
선을 세밀하게 제어 할 필요가 없기 때문에 getline
.
I / O에 iostreams를 선호합니다. iostream은 안전하고 유연하며 확장 가능합니다.
- https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rio-streams
std::ofstream output;
output.open(outFile);
output << code;
output.close();
짧게 만들 수 있습니다.
std::ofstream output(outFile);
output << code;
필요하지 않은 경우 닫는 것을 귀찮게하지 마십시오. 때 output
범위를 벗어나, 파일은 그 자체로 폐쇄 될 것입니다. 사소하게 파괴 std::vector
될 수 있거나 자동으로 정리되는 배열을 모두 삭제하지 않는 것과 동일한 이유 입니다.
const &
또는 std::string_view
문자열이 읽기 전용 인 경우를 사용합니다 . 전달하기에 저렴하고 콘텐츠가 수정되지 않을 의도를 나타냅니다.
std::string createFile(std::string lang, std::string code)
std::string getOutputFileData(std::string outFileLocation)
bool isSupportedLanguage(std::string lang)
void Program::cleanupFiles(std::string oldCommand)
auto iter = supportedLanguages.find(lang);
C ++ 20 contains
에서는 몇 줄을 절약 할 수 있습니다.
- https://en.cppreference.com/w/cpp/container/unordered_map
this->code
대신 변수를 this->
추가하거나 앞에 추가 _
하여 개인 멤버임을 나타냅니다.
구현 순서가 함수 선언 순서를 따르는 경우 더 읽기 쉬운 IMO입니다.
에서는 Program
, 생성자 대신에 아래의 파일의 맨 위로 이동할 수 있습니다.
- https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#nl16-use-a-conventional-class-member-declaration-order