2
\$\begingroup\$

I am trying to create an application that on the front end presents the user with a text editor to input code into and then I will run that code and return the result. I thought it would be a fun project to try and build my own version of leetcode as a learning project.

Right now this is what I am doing to run the provided code. Let's say we are running python code, because that's all I have implemented right now.

First I take in the code that the user submits and create an a file that contains the given code:

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;
}

The next thing I do is create an output file and run the previously created file but I send my stdout/stderr to another outputfile:

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;
}

Finally I return whatever I got from my output file and that is how I am executing my code.

I gotta think there is an easier way to do this. Especially since I am doing so much writing to a file and then reading from it is there anyway to get rid of that?

I also want to include more than one language in the future so I don't want to use any libraries that are specific to a certain language.

Lastly, this is my first C++ project so I would love any C++ tips!

Edit: I do want to eventually parallelize this code and find some way to encapsulate the program so it can't damage the system it is running on. If there is maybe some external program that would be good for that let me know and also gives me its stderr/stdout let me know.

Edit: As someone asked, here is the entire repo https://github.com/lkelly93/coderunner

\$\endgroup\$
3
  • 2
    \$\begingroup\$ Could you add the full code, not bits and pieces, so that I can run it in my IDE ? \$\endgroup\$ – aki Aug 21 '20 at 7:20
  • \$\begingroup\$ It's not super large but didn't want to put everything on the question here is the repo. github.com/lkelly93/coderunner \$\endgroup\$ – Luke Kelly Aug 21 '20 at 12:04
  • \$\begingroup\$ Arbitrarily running code from other people is very dangerous. Doing this properly is exceedingly hard. \$\endgroup\$ – Martin York Aug 22 '20 at 16:59
2
\$\begingroup\$

Rather than system() you should popen().

The difference is that system runs the command in a sub process with no access to this processes, while popen runs the command in a sub processes but provides accesses to the input and output streams of the sub processes.

This will allow you to run the sub-processes and stream input to the processes directly (from the input field you provided for standard input) and then read output from the processes and write it to the output field in your user interface.

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);
 

Sorted of related you don't need to save your pythong script as a file. The python command accept the - as a name which means read the script from the standard input rather than from the named file.

So you can run the python command (with popen()) then write the script you want to execute to the input stream of the file produced.

This will remove the need for any intermediate files.

\$\endgroup\$
1
\$\begingroup\$

Appreciable things:

  • Documentation for implementations and sections made using comments.
  • Classes, functions and files following Single Responsibility Principle.
  • Descriptive names of functions. (not variables as I note below)
  • Testing framework! But the tests should check the inner functions too not just the whole program.

The terminology of file streams and filenames is very confusing and makes me look-up function return types or variable declaration too often.

Program::outputFile is filename which is not clear here. I mistook it for FILE*.

In another place, std::ofstream output; output sounds like the output content of the program but it's a stream!

std::string output = getOutputFileData(this->outputFile); And here it is a string again!


The code doesn't take care of absolute and relative paths.

The test fails with this:

runnerFiles/0x1005c05c0output.txt does not exist.

With such a code, I'd be very reluctant to use rm. At most, keep all the disposable files in a folder and ask user to delete it.


std::stringstream newCommand;
newCommand << command;
newCommand << ">> ";
newCommand << outputFile;
newCommand << " 2>&1";

system(newCommand.str().c_str());

std::stringstream can be avoided and you can use concatenate std::strings directly as long as the first item is a std::string.

std::string newCommand = command + ">> " + outputFile + "2>&1";

The code in getOutputFileData that uses FILE* and char buffer (which you didn't even allocate!) can be replaced with the following (add error handling)

  std::ifstream run_output{outFileLocation};
  std::stringstream buffer;
  buffer << run_output.rdbuf();
  return buffer.str();

Since you don't need a fine control over lines, don't bother with getline.

Prefer iostreams for I/O. iostreams are safe, flexible, and extensible.


std::ofstream output;
output.open(outFile);
output << code;
output.close();

Can be made shorter as

std::ofstream output(outFile);
output << code;

Don't bother with closing if not needed. When output goes out of scope, file will be closed by itself. It's the same reason you don't go around deleting every trivially destructible std::vector or array which will be cleaned up automatically.


Use const & or std::string_view where strings are only read. They're cheap to pass around and indicate the intent that the content will not be modified.

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 will have contains so that saves you a few lines.


this->code

Instead of this->, consider appending or prepending _ to the variables to indicate that they're private members.


It's more readable IMO if implementation order follows the declaration order for functions.

In Program, constructor can go to the top of the file, instead of the bottom.

\$\endgroup\$
3
  • \$\begingroup\$ Hi @anki I greatly appreciate your tips but they don't really answer my main question. Is there an easier way to execute foreign code in C++? That's why I only posted the two snippet of code because those are my main question. Thanks so much for the general tips though! \$\endgroup\$ – Luke Kelly Aug 21 '20 at 14:49
  • \$\begingroup\$ I bet using a stringstream is more efficient than normal string concatenation as the + operator will create a new string and two copies (to put the strings together) while streaming operator can resize the output string and append. \$\endgroup\$ – Martin York Aug 22 '20 at 17:03
  • \$\begingroup\$ @MartinYork I was trying to keep it in one line and not focus on performance. But yes it could be bad when the file output is huge. I'll still use std::string::append and not use stringstream as a glue. \$\endgroup\$ – aki Aug 22 '20 at 17:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.