An automatic grader in c++ - c++

I am trying to write an automatic grading program for a C++ course I am teaching. Usually, automatic graders use (input,output) pairs: the submitted program reads from standard input and writes to standart output, and the grader compares it with the expected output. But I want my students to practice specific C++ constructs (and not e.g. write a program in C), so the tests are written in C++. In a simple example, I give them a main program such as:
#include "func.hpp"
...
int main() {
test(func(1)==2);
test(func(2)==33);
...
/* some 100 tests, including some randomized tests */
...
cout << "Grade: " << grade << endl;
}
The students have to submit the files func.hpp and func.cpp (for more complex assignments, some more files are required).
There is a bash script that compiles main.cpp with func.cpp, runs the program and reads the grade from the last line (It is run within a docker container, to prevent inadvertent damage to the host computer).
The problem is that a student can, from within func, print "Grade: 100" and exit.
Is there a simple way to make the grader more reliable?

I don't want to go crazy with this, but if you want to make it safe, you should put the student work in a "clean room" so to speak. This is a tedious exercise, but once done, it will be very safe. You said you run in a docker container, so I would run the student's work in a sub-process with IO redirected (to a pipe, file, or to /dev/null, depending on whether some of your assignments involve console output). That way a smart-ass student can print whatever they want, but they will only be piping it to you, not printing to the console. I'm not going to go into the code for this - I saw lots of examples I liked here on Stack Overflow by searching for "fork with redirected stdout" and it's a classic forumula you probably know.
In pseudo code, it would look something like:
main() {
<tedious setup for stdin/stdout/stderr redirection>;
int ch = fork();
if (ch == 0) {
test(func(1)==2);
test(func(2)==33);
.
.
// test clearly generates "grade" so use it as an exit code as an
// easy way to return the information you want
exit(grade);
} else {
for (;;) {
<wait on and read stdout and stderr and do whatever you want to it - it won't go to the console, so no dirty tricks.>;
<you could analyze the student's output as part of grading, or pass it to the console with a highlight, so you know it's not your grade output>
<wait on ch to exit, get the exit code, display it as the grade and break>;
<generate some input if you want to have an assignment where the students read from stdin>;
<add a time-out on waiting for the child to exit. That way evil student can't just hang or busy-loop, whether on purpose by accident.>;
<if it seems to be hanging, you have to process id and can kill it.>;
}
}
}
Obviously the above is sketchy. When you loop on managing a child process, you have to check on everything simultaneously with select or something similar, but you sound like a pretty senior engineer. You get the basic idea. The students can't get away with anything if you keep their run-time environment in a child process that you manage. Plus it gives you you a platform to pull the strings of their code anyway you see fit.

Easy. When you do the analysis, just use a different main than what you gave the students as a reference program. Your version prints your secret key.
int main() {
test(func(1)==2);
test(func(2)==33);
...
/* some 100 tests, including some randomized tests */
...
cout << "The student doesn't know my version prints this next line" << endl;
cout << "Secret validation code: NCC-1701" << endl;
cout << "Grade: " << grade << endl;
}
A clever student who does what you suggests (prints 100 and exits), won't know about the secret message that your version prints and script validates for.
True story. When I was teaching a networking class, there was a homework assignment to implement a "stop and wait" protocol with UDP sockets. My code implemented the "client". The student's version implemented the "server". One student didn't have the networking calls working, but was clever enough to just print the expected output with sleep calls in between print statements. He would have gotten away with it if I hadn't of stopped my client program first and noticed his was still printing our lines of "incoming" data being received.

Related

Infinite loop after cin c++

I'm pretty newbie at c++, so this confuses me.
This code seems to loop infinitely after std::cin :
#include <iostream>
#include <string>
using namespace std;
int main() {
cout << "My name is Martin. What's yours?" << endl;
string name;
cin >> name; //It seems to loop around here
cout << "Ah, so you are" << name << "." << endl; //It doesn't print this message
return 0;
}
Thanks!
EDIT: I'm not sure I understand your answers, but what happens is that when you enter a name and press enter, it does nothing. The terminal just continues to the next line, letting you write something on that one too. It does this infinitely, a little bit like when you just press enter without any text.
EDIT 2: My bad, I should try to clear up things. This is what comes in the JDoodle console:
My name is Martin. What's yours?
JDoodle - Timeout - Some common reasons for Timeout
Your Program may have a endless loop
Please check the program and try again or contact JDoodle support at jdoodle#nutpan.com for more info.
The reason I believe it is a loop is that in a console that doesn't write timeout, I can continue to input on a new line all the time.
Since you are using JDoodle the console will be waiting on the server. If you don't type anything there it hangs until it eventually gets killed for being too slow.
The way to fix this is to enter the text you wish to type before running the program into the box labeled "Stdin Inputs...".
Alternatively you can set the "Interactive mode" to "On" which will show the console in the website where you can enter some text and press enter. (I tried, it works).

C++ Primer 1.4.4 — Importance of EOF and how to write in a code that will end without EOF?

Referring to two questions:
Incorrect output from C++ Primer 1.4.4
Confused by control flow execution in C++ Primer example
My question is answered in both of those posts, but I want to delve further.
First, I know this is only the beginning, but let's say I make a fully functional program that runs in a designed window. By that level, will I already know how to implement a EOF? I can't expect someone running my program to know that they need to hit Control-Z.
Is there a way to implement a specific code that functions so that it does not need me to type in an unrecognized value?
Also one guy in those questions somewhat answered the importance of EOF, but how come the program doesn't even post the final cnt - 1?
Let's say I do the numbers 10 10 10 20 20 20. Without EOF, this will only show the "10 repeats 3 times." How come the program doesn't at least type in the count "10 repeats 3 times and 20 repeats 2 times" minus the final one with white space?
lets say I make a fully functional program that runs in a designed window. By that level, will I already know how to implement a eof? I can't expect someone running my program to know that they need to hit ctrl + z.
You could either tell the user explicitly to do a specific action to end input or the design of the window itself could tell the user the information implicitly. For instance, a dialog box could ask the user to enter input and click an OK button when done.
Is there a way to implement a specific code that functions so that it does not need me to type in an unrecognized value?
It seems like you would rather use a newline character to terminate your input. An example of this usage could be std::getline. Instead of writing
while (std::cin >> val)
you could instead use
std::string line;
if (std::getline(std::cin,line))
and assume that your user's input only consists of one line of values. There are plenty of other ways to similarly achieve this task depending on how you want to constrain the user's input.
Let's say I do the numbers 10 10 10 20 20 20. WIthout eof this will only show the "10 repeats 3 times." How come the program doesn't at least type in the count "10 repeats 3 times and 20 repeats 2 times" minus the final one with white space?
Without the eof your program is still executing the while (std::cin >> val) loop since std::cin >> val has not yet received invalid input.
Since the line
std::cout << currVal << " occurs " << cnt << " times" << std::endl;
occurs after that while loop finishes execution, you don't (yet) see any information about the three 20's in the input.
When you are reading a sequence of inputs you'll need some indication when your down. That could be a sentinel value ("enter 999 to stop reading"; you'd need to detect that while reading), an invalid input ("enter X to stop reading"; when reading an int the value X is illegal and causes the stream to got into failure mode, i.e., have std::ios_base::failbit set), or the more conventional "there isn't anything more to read". For a file, the last conditions is straight forward. When reading data from the console you'll either need to teach people how to terminate the input or you'll need to use a different approach.
If you want to intercept any keypressed and react on them directly you may do so, too. You could, e.g., use ncurses and control your input via that. You could also set the concole to non-buffering (on POSIX systems using tcgetattr() and tcsetattr() to clear the ICANON flag) and deal directly with all key presses to decide whether you want to continue reading or not.
Although I'm certainly up to doing fancy I/O stuff I normally don't bother: users will understand the "end of input" character and just deal with it. That is, my input normally looks something like this:
while (in >> whatever_needs_to_be_read) { ... }
... or, if the input is genuinely line oriented
for (std::string line; std::getline(in, line); ) { ... }
The function doing this input will then be called with a suitable std::istream which may be std::cin although I have typically some way to also read from a file (in addition to the shell-privided input redirection).
BTW, despite some indications in the questions referenced, "EOF" is not a character being read. It is a character entered, though (normally). ... and it is quite conventional to "know" the end of input character (on POSIX systems a ctrl-D and on Windows a ctrl-Z). You can use other indicators, e.g., the "interrupt" (ctrl-C) but that takes more work and doesn't integrate nicely with stream. To use the interrupt chacter you'd need to setup a signal handler for SIGINT and deal with that. One slightly annoying part of doing so is that if you get it wrong you'll need to find a different way to kill the program (e.g. on POSIX using ctrl-Z to put the process to sleep and kill it via a harsher signal).

C++ File outputting strange number, and part of code not running

Yeah. So, I'm trying to make a code for a guessing game. In this game, there's a hard mode. In hard mode, you have 15 guesses, and have to guess between 1 and 500. But my problem is this:
I'm trying to have hard mode save & display your wins/losses, but when it outputs the contents of wins.txt it outputs something like this:
Wins: 0x7fffee26df78
Losses: 0x7fffee26e178
It's really confusing me. Here's the part of the code I have for that:
ifstream losses_var("losses.txt");
ifstream wins_var("wins.txt");
losses_var>> loss;
wins_var>> win;
wins_var.close();
losses_var.close();
Then it gets called with:
cout<<"Wins: "<< wins <<"\nLosses: "<< losses <<"\n"
If you would like to see the full source code, it's here: http://pastebin.com/gPT37uBJ
My second problem:
Hard mode won't display when you win. That's pretty much the whole problem. In my code, the loop for asking users for input uses
while (guess != randNum)
So at the end bracket I have what I want the code to display when a user wins, but it just doesn't run. It just stops. I would like it if someone could help me with this. The line that has the bug is line 97 through 105. Again, source code is here: http://pastebin.com/gPT37uBJ
You've got your variable names confused
cout<<"Wins: "<< wins <<"\nLosses: "<< losses <<"\n";
should be
cout<<"Wins: "<< win <<"\nLosses: "<< loss <<"\n";
It's important to pick good variable names. One reason is so that you don't confuse yourself about what your variables mean (if you confuse yourself think how it's going to be for someone else looking at your code).
Others have already answered the output problem (win vs. wins). The other problem is probably in your logic of while loop nesting. The outer loop (while (guess != randNum)) starts, but its body contains the entire inner loop (while (guesses_left != 0)). This means that the outer condition is not checked again until the inner loop terminates, which means you've run out of guesses. Also note that if you guess correctly, inner loop will never terminate. You probably want something like this:
while (guesses_left > 0) {
// input user's guess
if (guess < randNum) {
// process it
} else if (guess > randNum) {
// process it
} else {
// it's equal, user won
// do what's necessary for a win
return 0;
}
}
// ran out of guesses
// do what's necessary for a loss
return 0;
You are not writing your variables win and loss to cout. From your pasted code, I can see that wins and losses are ofstream objects, which means you are probably seeing addresses there. I would advise you to choose more informative variable names to avoid hard to spot mistakes like this.

Automatic cout flushing

Good day,
I wrote a Java program that starts multiple C++ written programs using the Process object and Runtime.exec() function calls. The C++ programs use cout and cin for their input and output. The Java program sends information and reads information from the C++ programs input stream and outputstream.
I then have a string buffer that builds what a typical interaction of the program would look like by appending to the string buffer the input and output of the C++ program. The problem is that all the input calls get appended and then all the output calls get posted. For example, and instance of the StringBuffer might be something like this...
2
3
Please enter two numbers to add. Your result is 5
when the program would look like this on a standard console
Please enter two numbers to add. 2
3
Your result is 5
The problem is that I am getting the order of the input and output all out of wack because unless the C++ program calls the cout.flush() function, the output does not get written before the input is given.
Is there a way to automatically flush the buffer so the C++ program does not have to worry about calling cout.flush()? Similiar to as if the C++ program was a standalone program interacting with the command console, the programmer doesn't always need the cout.flush(), the command console still outputs the data before the input.
Thank you,
In case someone comes looking for a way to set cout to always flush. Which can be totally fair when doing some coredump investigation or the like.
Have a look to std::unitbuf.
std::cout << std::unitbuf;
At the beginning of the program.
It will flush at every insertion by default.
I can't guarantee that it will fix all of your problems, but to automatically flush the stream when you're couting you can use endl
e.g.:
cout << "Please enter two numbers to add: " << endl;
using "\n" doesn't flush the stream, like if you were doing:
cout << "Please enter two numbers to add:\n";
Keep in mind that using endl can be (relatively) slow if you're doing a lot of outputting
See this question for more info

how do I tell when a c++ program is waiting for input?

I'm trying to control a simple c++ program through python. The program works by prompting the user for input. The prompts are not necessarily endl terminated. What I would like to know is if there is a way to tell from python that the c++ program is no longer generating output and has switched to requesting input.
Here's an simple example:
c++
#include <iostream>
using namespace std;
int main()
{
int x;
cout << "Enter a number ";
cin >> x;
cout << x << " squared = " << x * x << endl;
}
python:
#! /usr/bin/python
import subprocess, sys
dproc = subprocess.Popen('./y', stdin=subprocess.PIPE,stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while (True) :
dout = dproc.stdout.read(1)
sys.stdout.write(dout)
dproc.stdin.write("22\n")
This sort of works but writes to dproc.stdin too much. What I am looking for instead is a way to read everything from dproc.stdout until the program is ready for input and then write to dproc.stdout .
If possible, I would like to do this w/o modifying the c++ code. ( However, I have tried playing with buffering on the c++ side but it didn't seem to help )
Thank you for any responses.
I'm not aware of a sufficiently general paradigm for detecting that the remote end is waiting for input. I can think of basic ways, but also of situations in which they will fail. Examples:
Read with a non-blocking pipe/socket until no input arrives: the response might be interrupted by the process writing to disk, waiting for a reply from a database etc.
Wait until the process becomes idle: a background thread might still be running.
You'll have to be aware of the protocol used by the application you're trying to control. In a sense, you'll be designing an interpreter for that process. pexpect is a toolset for Python based on this idea.
Have a look at pexpect.