Funzionamento grader

Salve a tutti, 

invista delle OII dove, a quanto pare, l’utilizzo di funzioni specifiche è tornato di moda; chi mi spiega bene come si eseguono in locale grader e funzione creata per fare i test? come si richiama quest’ultima? …(so che può sembrare banale ma mi sono sempre esercitato con i classici input.txt/output.txt diretti) 

Non mi sono mai preoccupato di riprodurre in locale la stessa situazione delle OII. Ho sempre fatto due file di codice, uno contenente la funzione da mandare e l’altro che si occupa dell’input/output che non fa altro che includere il file con la funzione.

Non c’è nulla da esercitare dato che alle OII sarà già tutto preparato
Non c'è nulla da esercitare dato che alle OII sarà già tutto preparato

Lawliet

Vero, tuttavia fate conto che agli stage di allenamento (dopo le OII) ci si aspetta che gli studenti selezionati sappiano utilizzare i grader autonomamente (quindi non è detto che vengano forniti script/progetti/makefile).
chi mi spiega bene come si eseguono in locale grader e funzione creata per fare i test? come si richiama quest'ultima?

gianpiero96

Supponendo che tu utilizzi C o C++, l'idea è che devi lavorare con due file anziché uno, e uno di questo due (il grader) non lo devi (o meglio "non lo dovresti") modificare.

Per esempio, se un problema ti chiede di implementare una funzione "somma", avrai una situazione di questo tipo:

grader.c (ha la funzione main e dichiara un prototipo della funzione che devi implementare)
int somma(int, int);

int main() {
    // lettura da file di a, b
    // chiamata di somma(a, b) e scrittura del risultato su file
    return 0;
}

tuasoluzione.c (contiene un'implementazione compatibile col prototipo dichiarato nel grader)
int somma(int x, int y) {
    return x + y;
}

Per capire come si fa a compilare "insieme" due file che si riferiscono a vicenda (come in questo caso) devi sapere che il processo che spesso viene semplicemente chiamato "compilazione" in realtà è suddiviso in due fasi distinte che avvengono una dopo l'altra: (1) compilazione, (2) linking.

Assumiamo che per compilare un file utilizziamo questo comando (che produce di default, se tutto va a buon fine, un eseguibile a.out)

gcc nome_file.c

Questo comando in realtà prima compila nome_file.c in un file oggetto con estensione .o, e infine linka questo file oggetto creando un eseguibile.

Chiaramente, se provi a compilare con quel comando uno solo dei due file di cui abbiamo parlato prima, riceverai un errore nella fase di linking:

  • linkando il file oggetto ottenuto dalla compilazione di grader.c, ci sarà un errore che dice che il riferimento a "somma" non è definito.
  • linkando il file oggetto ottenuto dalla compilazione di tuasoluzione.c, ci sarà un errore che dice che manca il riferimento alla main().

Un modo per "risolvere" il problema è quello di compilare entrambi i file, per poi eseguire la fase di linking su entrambi i file oggetto (con estensione .o) che sono stati creati. Ovvero:

gcc nome_file1.c nome_file2.c

In questo modo la fase di linking andrà a buon fine (dato che tutti i riferimenti ci sono) e verrà prodotto l'eseguibile a.out (per specificargli un altro nome, si può usare come sempre il flag -o di gcc).

tl;dr

Un modo è questo:

1) Crea per ogni problema una cartella apposita, in ogni cartella crea uno script bash chiamato "compila.sh" con dentro:

#!/bin/bash
gcc -o tuoeseguibile grader.c tuasoluzione.c

2) Fai tasto destro sul file, vai sulle proprietà e impostalo come eseguibile (assumo che si stia usando Ubuntu).

3) Configura il tuo IDE in modo da mappare una combinazione di tasti a "esegui compila.sh".

4) Configura il tuo IDE in modo da mappare un'altra combinazione di tasti a "esegui tuoeseguibile".

O più semplicemente si puó modificare la riga di compilazione dell’IDE aggiungendo “grader.cpp” alla riga usata dall’IDE, molto rapido con software come geany per esempio.

chi mi spiega bene come si eseguono in locale grader e funzione creata per fare i test? come si richiama quest'ultima?

gianpiero96

Supponendo che tu utilizzi C o C++, l'idea è che devi lavorare con due file anziché uno, e uno di questo due (il grader) non lo devi (o meglio "non lo dovresti") modificare.

Per esempio, se un problema ti chiede di implementare una funzione "somma", avrai una situazione di questo tipo:

grader.c (ha la funzione main e dichiara un prototipo della funzione che devi implementare)
int somma(int, int);

int main() {
    // lettura da file di a, b
    // chiamata di somma(a, b) e scrittura del risultato su file
    return 0;
}

tuasoluzione.c (contiene un'implementazione compatibile col prototipo dichiarato nel grader)
int somma(int x, int y) {
    return x + y;
}

Per capire come si fa a compilare "insieme" due file che si riferiscono a vicenda (come in questo caso) devi sapere che il processo che spesso viene semplicemente chiamato "compilazione" in realtà è suddiviso in due fasi distinte che avvengono una dopo l'altra: (1) compilazione, (2) linking.

Assumiamo che per compilare un file utilizziamo questo comando (che produce di default, se tutto va a buon fine, un eseguibile a.out)

gcc nome_file.c

Questo comando in realtà prima compila nome_file.c in un file oggetto con estensione .o, e infine linka questo file oggetto creando un eseguibile.

Chiaramente, se provi a compilare con quel comando uno solo dei due file di cui abbiamo parlato prima, riceverai un errore nella fase di linking:

  • linkando il file oggetto ottenuto dalla compilazione di grader.c, ci sarà un errore che dice che il riferimento a "somma" non è definito.
  • linkando il file oggetto ottenuto dalla compilazione di tuasoluzione.c, ci sarà un errore che dice che manca il riferimento alla main().

Un modo per "risolvere" il problema è quello di compilare entrambi i file, per poi eseguire la fase di linking su entrambi i file oggetto (con estensione .o) che sono stati creati. Ovvero:

gcc nome_file1.c nome_file2.c

In questo modo la fase di linking andrà a buon fine (dato che tutti i riferimenti ci sono) e verrà prodotto l'eseguibile a.out (per specificargli un altro nome, si può usare come sempre il flag -o di gcc).

tl;dr

Un modo è questo:

1) Crea per ogni problema una cartella apposita, in ogni cartella crea uno script bash chiamato "compila.sh" con dentro:

#!/bin/bash
gcc -o tuoeseguibile grader.c tuasoluzione.c

2) Fai tasto destro sul file, vai sulle proprietà e impostalo come eseguibile (assumo che si stia usando Ubuntu).

3) Configura il tuo IDE in modo da mappare una combinazione di tasti a "esegui compila.sh".

4) Configura il tuo IDE in modo da mappare un'altra combinazione di tasti a "esegui tuoeseguibile".

wil93

C'è un modo di "automatizzare" tutto questo in codeblocks, magari usando i progetti?
C'è un modo di "automatizzare" tutto questo in codeblocks, magari usando i progetti?

mark03

Sì, basta creare un progetto ed aggiungerci entrambi i file :)
Alle ultime OII infatti i contestant avevano nelle proprie home (o forse nelle scrivanie?) una cartella per ogni task, e dentro ognuna di queste c'erano altre cartelle: "codeblocks" (con un progetto), "geany" (con un Makefile, credo), "altro" (con un compila.sh).

Ad ogni modo quando "citi" un post precedente in un forum non devi per forza citarlo in modo "completo" :P, si può (anzi è auspicabile) "sfoltire" in modo da lasciare solo la parte alla quale si vuole rispondere. http://pub.tsn.dk/how-to-quote.php

Alle ultime OII infatti i contestant avevano nelle proprie home (o forse nelle scrivanie?) una cartella per ogni task, e dentro ognuna di queste c'erano altre cartelle: "codeblocks" (con un progetto), "geany" (con un Makefile, credo), "altro" (con un compila.sh).

-wil93

Era sulla scrivania ma non ricordavo ci fosse anche codeblocks... Io avevo usato geany ed era impostato il giusto comando per la compilazione dei due file insieme

Grazie per le spiegazioni. Comunque da quel che ho capito al correttore va inviato soltanto il mio_file.cpp/.c contenente la funzione risolutiva, giusto? Quindi, anche per motivi di tempo, non converrebbe modificare il grader magari includendo il file con la mia funzione come se fosse una libreria? 
Es. #include<C:…\mio_file.cpp> 

non converrebbe modificare il grader magari includendo il file con la mia funzione come se fosse una libreria?

gianpiero96

In quel caso faresti
#include "percorso/miofile.cpp"

(con "" invece di <>, per i file locali).

Beh, questo è un altro modo, tuttavia secondo me non dà molti vantaggi dato che ogni volta devi compilare ed eseguire il grader (mentre modifichi un altro file) quindi dovresti comunque perdere tempo a configurare l'IDE (o chi per lui) in modo da fare la cosa giusta.

Un modo migliore (apparentemente) sarebbe invece quello di includere il grader nel miofile.cpp. Questo però ti obbliga a togliere (o "commentare via") l'#include ogni volta che sottoponi.

Per bypassare questo, si può sfruttare il fatto che il correttore definisce (o meglio, almeno CMS lo fa) una costante EVAL. Quindi in teoria basterebbe aggiungere all'inizio del miofile.cpp le seguenti righe:

#ifndef EVAL
#include "grader.cpp"
#endif

questa mi sembra l'alternativa più "trasparente", ovvero lavori (compilazione, esecuzione) su un solo file come al solito. Però dipende dal correttore: se un giorno si decide che il correttore non definisce più EVAL? in tal caso ti ritroveresti a dover cambiare l'ifndef in un ifdef e a dover definire EVAL (o TEST_LOCALE, o quel che vuoi) andando a cambiare la riga di comando che usi per compilare.

Ho provato a risolvere qualche problema con grader degli ultimi inseriti per provarne il funzionamento ma in tutti i casi questo non funziona. pare che il programma di blocchi al momento dell’input ossia all’ultima riga del seguente stralcio:


int main(){
    FILE *fin, *fout;
    int i,j;
#ifdef EVAL
    assert(fin = fopen(“input.txt”, “r”));
    assert(fout = fopen(“output.txt”, “w”));
#else
    fin = stdin;
    fout = stdout;
#endif
    fscanf(fin, “%d”, &N);

è un problema di librerie o sbaglio qualche cosa io? 

Si blocca perché aspetta di leggere i dati da tastiera (quando viene sottoposto invece lavora su file).


Cambia quella parte di codice in:

int main(){
    FILE *fin, *fout;
    int i,j;
    assert(fin = fopen(“input.txt”, “r”));
    assert(fout = fopen(“output.txt”, “w”));
    fscanf(fin, “%d”, &N);

P.S. ho modificato i grader allegati che avevano #ifdef EVAL (petali e sponsor), ora dovrebbero leggere di default da file.

Perfetto, effettivamente queste erano funzioni che non conoscevo, non mi ero accorto dell’input da tastiera, Grazie!