A teacher posted the following question as a github issue, but I'm answering it here.
====== Question begins ======
Hello,
I am tryin to create a question for a C program which uses certain modules such as a stack (stack.h and stack.c). I have managed to get it working with the template leveraging make provided in the documentation--which is what I was aiming for. However, in that template the complete student answer-main file-is provided in a separate file, along with the required module. I have also had success when uploading the files in the question configuration and providing the full answer from the student in the Answer box. This way, the template box only has the following code:
import subprocess import sys student_answer = """{{ STUDENT_ANSWER | e('py') }}""" with open("prog.c", "w") as src: print(student_answer, file=src) # Call make make_result = subprocess.run(['make'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8') if make_result.returncode != 0: print(make_result.stdout) print("** Make failed. Testing aborted **", file=sys.stderr) else: # If Make succeeded, run prog. 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. try: output = subprocess.check_output(["./prog"], 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)
The issue is that I am struggling creating a template that mixes some code from a student such as a function from the main file and some code provided. For instance, I am trying to create the template like this:
import subprocess import sys template = """ #include <stdio.h> #include <string.h> #include "stack.h" {{ STUDENT_ANSWER | e('py') }} void showPassengers(Stack *stack) { while (!STACK_isEmpty(*stack)) { Passenger passenger = STACK_top(stack); printf("%s %s %d %c\n", passenger.name, passenger.flight_no, passenger.row, passenger.seat); STACK_pop(stack); } } int main() { Passenger *passengers = NULL; int n_passengers = 0; Stack stack; passengers = loadPassengers("passengers", &n_passengers); stack = STACK_create(); for (int i = 0; i < n_passengers; i++) { STACK_push(&stack, passengers[i]); } {{ TEST.testcode }} STACK_destroy(&stack); free(passengers); return 0; } """ with open("prog.c", "w") as src: print(student_answer, file=src) # Call make make_result = subprocess.run(['make'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8') if make_result.returncode != 0: print(make_result.stdout) print("** Make failed. Testing aborted **", file=sys.stderr) else: # If Make succeeded, run prog. 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. try: output = subprocess.check_output(["./prog"], 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)
As you can see, part of the code is mine, while a function just needs to implement a certain function. However, this is not working as expected apparently because Python is escaping certain characters. I have tried several steps such as using double curly braces, etc. to no avail. Is there a particular way I should proceed? In other words, I want to have a program consisting of several modules in C where a user just needs to implement one function from a module. I am quite certain the issue is with the escape of characters in Python due to the output:
gcc -c prog.c
prog.c: In function ‘showPassengers’:
prog.c:41:16: warning: missing terminating " character
41 | printf("%s %s %d %c
| ^
prog.c:41:16: error: missing terminating " character
41 | printf("%s %s %d %c
| ^~~~~~~~~~~~
prog.c:42:1: warning: missing terminating " character
42 | ", passenger.name, passenger.flight_no, passenger.row, passenger.seat);
| ^
prog.c:42:1: error: missing terminating " character
42 | ", passenger.name, passenger.flight_no, passenger.row, passenger.seat);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
prog.c:43:25: error: expected ‘)’ before ‘;’ token
43 | STACK_pop(stack);
| ^
| )
prog.c:41:15: note: to match this ‘(’
41 | printf("%s %s %d %c
| ^
prog.c:43:9: error: invalid use of void expression
43 | STACK_pop(stack);
| ^~~~~~~~~~~~~~~~
prog.c:43:26: error: expected ‘;’ before ‘}’ token
43 | STACK_pop(stack);
| ^
| ;
44 | }
| ~
make: *** [makefile:7: prog.o] Error 1
Thanks for your help,
==================== Question ends ==================I'm pretty sure the problem here is with the line
{{ TEST.testcode }}
That should be
{{ TEST.testcode | e('py') }}
in order to escape things like backslashes in the test code string.
It looks like you have a test of the form printf("..stuff.. \n"). But that's embedded within a Python string, so the two-character sequence '\n' will be converted to a single newline character, prematurely terminating the printf line.
A useful tip to help debug complex templates: click the template debugging checkbox. That will let you see how the template code is being expanded, i.e. to see just what source code is being run.