Question Authors' Forum

can't solve it

 
Picture of Richard Lobb
Re: can't solve it
by Richard Lobb - Monday, 6 November 2017, 2:29 PM
 

The built-in "C Program" question type doesn't accept command line arguments, so you need to set up your own template for such a question. Scripting with Python is the easiest way to do this

If you you need to do this more than once, it's worth defining it as a new question type, following the instructions here. The template might be as follows - it's very similar to the version in the documentation except it runs the program with a bash command rather than by executing it directly.

""" The template for a question type that compiles and runs a student-submitted
    C program using a bash command line, supplied by the question author.
    Note: the compiled student program is 'prog' and should be run with a 
    command like 
    
    ./prog arg1 arg2 ...
"""

import subprocess

# Write the student code to a file prog.c
student_answer = """{{ STUDENT_ANSWER | e('py') }}"""
with open("prog.c", "w") as src:
    print(student_answer, file=src)

# Compile
{% if QUESTION.parameters.cflags is defined %}
cflags = """{{ QUESTION.parameters.cflags | e('py') }}"""
{% else %}
cflags = "-std=c99 -Wall -Werror"
{% endif %}
return_code = subprocess.call("gcc {0} -o prog prog.c".format(cflags).split())
if return_code != 0:
    print("** Compilation failed. Testing aborted **", file=sys.stderr)

# If compile succeeded, run the code. Since this is a per-test template,
# stdin is already set up for the stdin text specified in the test case,
# so we can run the compiled program directly.
if return_code == 0:
    bash_command = """{{ TEST.testcode | e('py') }}"""
    try:
        output = subprocess.check_output(bash_command, shell=True, universal_newlines=True)
        print(output)
    except subprocess.CalledProcessError as e:
        if e.returncode > 0:
            # Ignore non-zero positive return codes
            if e.output:
                print(e.output)
        else:
            # But negative return codes are signals - abort
            if e.output:
                print(e.output, file=sys.stderr)
            if e.returncode < 0:
                print("Task failed with signal", -e.returncode, file=sys.stderr)
            print("** Further testing aborted **", file=sys.stderr)

I attach a Moodle XML question export file containing a prototype using that template to define a  new question type C_prog_with_args . The file includes a single demo program. That one demo is the full extent of any testing I've done, so you might have to do some further tweaking.

If you wish to check that students aren't using particular functions like getchar, scanf etc you can simply add a bit of Python code to the template to see if the banned function names are present. This is a bit of hack (e.g. they might be present in comments) but is good enough for most cases, provided you warn the students. If you want to do the job properly you can use a C parser like pycparse.

Here's a screen shot of the demo program in action.

C_prog_with_args

Richard