c-program questiontype: echoing stdin to stdout

c-program questiontype: echoing stdin to stdout

de către Michael Fairbank-
Număr de răspunsuri: 5

Hi, in an earlier forum post, Richard wrote

CodeRunner's 'run-in-sandbox' AJAX service, which is what the Ace-inline-filter is using, simply sends a complete job to the Jobe server which runs it and returns the results. There's no interactivity possible here. You can supply a text string to be used in lieu of keyboard input but Jobe just writes this to a file and runs the job with stdin redirected to that file. Input is not echoed to standard output so if there are, say, two successive reads from stdin with prompt strings Prompt A and Prompt B, the student will just see the output Prompt A Prompt B all on one line with no apparent input taking place. Unless they're carefully trained on what to expect, they will find this very confusing.

Is there anyway to avoid this behaviour, i.e. to automatically echo the stdin to stdout, so that it is less confusing to students?  It would be good if the "expected" and "actual" strings could match exactly what the students see if they run this C program in their own console (i.e. outside of coderunner), where the stdin comes from their keyboard and is echoed to the console.  Could we have a new question type e.g. "c-program-echo-stdinout" to do this? Or is there an existing solution? Thank you.


Ca răspuns la Michael Fairbank

Re: c-program questiontype: echoing stdin to stdout

de către Richard Lobb-
The problem is easily solved in Python: the out-of-the-box question type python3_w_input replaces the standard Python input function with one that does as you suggest, echoing standard input to standard output as it is consumed.

With other languages, though, it's harder. We never bothered to develop an equivalent question type in other languages as we teach Python as our first language and after that we expect students to understand files and I/O redirection.

But certainly it can be done. I just wrote a  getchar.c that can be compiled and linked in to replace the library getchar function. Adding that getchar.c to a question as a support file and customising the question to compile that file as well, gives a question that behaves like the following:


The support file getchar.c is the following:

/* A getchar function to replace the standard one, which echoes
 * characters from stdin to stdout as they are consumed.
 */
struct _IO_FILE;
typedef struct _IO_FILE FILE;
extern int fgetc(FILE *__stream);
extern FILE *stdin;
extern int putchar(int __c);
int getchar() {
    int c = fgetc(stdin);
    putchar(c);
    return c;
}

The xml export of the question is attached. Note that it's customised, and you have to open the Advanced Customisation section to see how the compileargs parameter is set to compile the added file.

You should be able to do the same sort of thing with scanf, though it will be much harder. You'd probably want to start with the source code for scanf (see here and here) and tweak to do the output as well.

You could of course turn this into a question type. If you do so, could you post it back here, please?

Ca răspuns la Richard Lobb

Re: c-program questiontype: echoing stdin to stdout

de către Michael Fairbank-
Hi Richard, we did not manage to implement this yet. We have just explained to the students to expect this behaviour for now.

I wonder if there is a clever trick using Unix streams or similar, which means we pass a flag to the jobe server when it runs program code, which forces it to echo all inputs from the input stream to the console, regardless of whatever language it is running? Then instead of doubling up all language question templates (e.g. creating "python3" vs "python3_w_input", and "C" vs "C_w_input") there is just one checkbox in the code-runner question-setting user-interface called "w_input" or similar? It feels to me that the might be a clever global solution to this problem, lurking underneath somewhere, waiting to be discovered?
Ca răspuns la Michael Fairbank

Re: c-program questiontype: echoing stdin to stdout

de către Richard Lobb-
Yep, it would be nice if there was a better solution. Maybe someone will post something back to this thread in the future.
Ca răspuns la Richard Lobb

Re: c-program questiontype: echoing stdin to stdout

de către Michael Fairbank-

Hi Richard, 

I have a potential solution to this now.  A possible template that seems to work reasonably well for a c_program_w_input question type would be this one:

#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>

/* Improved scanf replacement with echo */
int scanf2(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);

    int assigned = 0;
    int c;

    while (*fmt) {

        /* Handle format specifier */
        if (*fmt == '%') {
            fmt++;

            /* Read first non-space character */
            do {
                c = getchar();
                if (c != EOF) putchar(c);
            } while (isspace(c));

            if (*fmt == 'd') {
                int *ip = va_arg(args, int*);
                int sign = 1;
                int value = 0;

                if (c == '-') {
                    sign = -1;
                    c = getchar();
                    putchar(c);
                }

                while (isdigit(c)) {
                    value = value * 10 + (c - '0');
                    c = getchar();
                    if (c != EOF) putchar(c);
                }

                if (c != EOF)
                    ungetc(c, stdin);

                *ip = sign * value;
                assigned++;
            }

            else if (*fmt == 's') {
                char *sp = va_arg(args, char*);
                int i = 0;

                while (c != EOF && !isspace(c)) {
                    sp[i++] = c;
                    c = getchar();
                    if (c != EOF) putchar(c);
                }

                if (c != EOF)
                    ungetc(c, stdin);

                sp[i] = '\0';
                assigned++;
            }
        }

        /* Handle literal characters in format string */
        else {
            putchar(*fmt);
        }

        fmt++;
    }

    va_end(args);
    return assigned;
}

/* Replace scanf globally */
#define scanf scanf2

/* Student code */
{{ STUDENT_ANSWER }}

Does that seem valid to you?  It seems to meet the goal I want. It defines a new scanf2 function, which approximates the original scanf function, but echos input text to the terminal too.  And then it uses the final #define instruction to replace the built-in scanf by the new scanf2 function.   (Note that the scanf2 implementation is not as flexible and powerful as the built-in scanf function.  For example, it can only handle one instance of %d or %s in the string given.  So you can't do an input for "%d %d" say. But I can't see we'd ever need double input like that in a beginner C course.  And it can't handle "%f" for floating point input.)  P.S. Disclaimer- I built the above scanf2 function using AI.

Thanks.  

Michael


Ca răspuns la Michael Fairbank

Re: c-program questiontype: echoing stdin to stdout

de către Richard Lobb-
Hi Michael

Thanks for posting. That approach looks fine, provided students don't attempt to use unimplemented features. I no longer teach C myself but I'll pass it on to a guy in our department who does.

I vaguely recall another user solving the same problem using OS-level operations - perhaps a pseudo-terminal? - but I don't remember the details. For a simple introductory course with students reading only a single integer or a single string, your approach is probably simpler.

Thanks

Richard