Indicații generale teme

Codul vostru trebuie nu numai să “meargă”, ci și să fie ușor de citit/parcurs și fără potențiale erori (neverificate de sistemul de evaluare automată). Aveți mai jos câteva indicații pentru teme, împreună cu depunctările aferente pentru nerespectarea lor.

Recomandări

  • Pentru rezolvarea temelor este impus stilul de coding din kernelul de Linux. Puteți folosi acest wrapper peste checkpatch.pl pentru a vă verifica sursele înainte de a submite temele.
  • Folosiți utilitare precum cppcheck, valgrind, Application Verifier pentru verificarea statică/dinamică a codului.
  • Dacă nu sunteți siguri pe modul vostru de indentare, folosiți indent sau astyle.
    • Pentru cei care folosesc vim, puteți porni de la acest vimrc
  • Creați un README relevant care să nu reproducă enunțul sau comentariile din cod și care să ajute la înțelegerea soluției alese. Găsiți mai jos în secțiunea README - how to recomandările echipei de SO pentru realizarea README-ului, puteți folosi acestea ca un template când îl scrieți.
  • Folosiți lista de discuții dacă aveți întrebări referitoare la coding style sau best practices.
  • Testați-vă tema dincolo de testele automate. O aplicație reușită nu se rezumă doar la trecerea unui subset de teste.

Listă depunctări

  • Temele care nu se compilează sau nu rulează, indiferent de motive, vor primi punctaj 0; folosiți sistemul vmchecker pentru a verifica rularea/compilarea corespunzătoare a temelor.
  • Pe sistemele de operare general purpose de tipul Linux, *OS, Windows, FreeBSD, cu biblioteci standard C complexe, resursele proprii / nepartajate procesului (memorie alocată, fișiere deschise, thread-uri create) sunt eliberate și închise la încheierea procesului (exit). Pe aceste sisteme nu e nevoie să eliberați explicit înainte de exit; pot există medii specifice în care acest lucru nu se întâmplă. Cea mai bună practică (best practice) este să eliberați explicit resursele ca să acoperiți toate situațiile.

Funcționalitate

  • -0.2: accesarea unor zone de memorie nevalide (care nu rezultă în mesaj de eroare de tipul “Segmentation fault”): buffer overflow, lucru neadecvat cu funcții pe șiruri; pentru depistarea accesărilor invalide puteți folosi valgrind;
  • -0.2: nu se eliberează resursele după utilizarea acestora: nu se eliberează memoria alocată; leak-uri de memorie; recomandăm folosirea utilitarului valgrind pentru a depista leak-urile de memorie din program;
  • -0.2: nu se eliberează resursele după utilizarea acestora: nu se închid descriptorii de fișiere (Linux), respectiv handler-ele (Windows)
  • -0.2: nu sunt verificate valorile de retur ale funcțiilor; recomandăm folosirea macro-ului DIE, prezent în scheletul de laborator;
  • -0.1: nu sunt întoarse coduri de eroare relevante;
  • -0.1: folosire alocare dinamică pentru o dimensiune statică (cunoscută la compilare);
  • -0.1: folosire buffere statice (dimensiune fixă) acolo unde este nevoie de alocare dinamică (acolo unde dimensiunea se cunoaște doar la runtime);
  • -0.1: mult cod duplicat;

Structura codului

  • -0.5: definiții de funcții non-statice în headere;
  • -0.5: includere a unei surse (exemplu: #include “file.c”); se includ doar fișiere header (exemplu: #include “file.h”);
  • -0.1: funcții nepublice (interne modulului) nedefinite folosind identificatorul static; variabile globale nepublice (interne modulului) nedefinite folosind identificatorul static;

Portabilitate

  • -0.1: folosirea directivelor de compilare condiționată (#ifdef linux …) acolo unde ar fi fost mai modular să se folosească fișiere separate și includere condiționată în fișiere header;

Build

  • -0.2: warning-uri de compilare; trebuie folosit, în cadrul compilării, flag-ul -Wall pe Linux și flag-ul /W3, cel puțin, pe Windows;
    • puteți folosi /D_CRT_SECURE_NO_DEPRECATE pentru a evita unele warninguri pe Windows.
  • -0.1: fișier Makefile necorespunzător: o singură regulă, nu există target de clean, nu se folosesc dependențe;

Mentenabilitate

  • -0.1: folosire de valori hard-coded, în loc să se definească macrodefiniții;
  • -0.1: funcții kilometrice (> 150 de linii); dezavantajele unei funcții prea lungi: este dificil de a o citi și înțelege, este predispusă la a conține mai multe defecte, indică un design precar al programului;
  • -0.2: funcții “și mai kilometrice” (> 300 de linii);
  • -0.1: denumire neadecvată a funcțiilor sau variabilelor (do_stuff, my_var) ;

Coding style

  • În scheletul de cod din laboratoare și din teme este folosit stilul de coding din kernelul Linux. Testul 0 din cadrul checker-ului fiecărei teme verifică automat coding style-ul surselor voastre folosind scriptul checkpatch.pl. Câteva exemple de utilizare a scriptului găsiți aici.
    • Vă puteți folosi de acest wrapper peste checkpatch.pl a verifica sursele folosind criteriile considerate în evaluarea temelor.
    • Pentru mai multe informații despre un cod de calitate citiți pagina de recomandări.
  • Exemple de erori de evitat:
    • mai mult de 20 de linii mai lungi de 80 de caractere;
    • cod înghesuit: nu se folosesc spații, nu se folosesc linii libere pentru a separa “paragrafele” de cod;
    • tab-uri amestecate cu spații pentru indentare; folosiți fie numai spații, fie numai tab-uri
    • indentare inconsecventă (uneori există, alteori nu, un TAB, mai multe TAB-uri)
    • mixed line endings (adică atât CRLF – Windows, cât și LF – Unix);
    • trailing whitespace; nu lăsați spații sau tab-uri la finalul liniilor;

Cleanup

  • -0.1: linii de cod comentate în surse; pentru debug recomandăm folosirea unui macro – puteți urmări exemplul de aici;
  • -0.1: includerea de fișiere binare sau irelevante în arhivă;
  • -0.2: cod inutil; prezența unor funcții sau bucăți de cod care nu sunt folosite sau sunt inutile în cadrul programului (de exemplu, se alocă un element și apoi nu se folosește sau se execută cod după apelul exec);

Documentație

  • -0.1: comentarii inadecvate: comentarii nerelevante sau absente;
  • -0.1: comentarii cu C99 (// in loc de /* */)
  • -0.2: README necorespunzător: nu se descrie soluția, se indică doar comentarii din sursă, copy paste la enunț sau la comentarii.

README - how to

Organizare

Explicație pentru structura creată (sau soluția de ansamblu aleasă):

Obligatoriu:

  • De făcut referință la abordarea generală menționată în paragraful de mai sus. Aici se pot băga bucăți de cod/funcții - etc.
  • Consideri că tema este utilă?
  • Consideri implementarea naivă, eficientă, se putea mai bine?

Opțional:

  • De menționat cazuri speciale (corner cases), nespecificate în enunț și cum au fost tratate (în special dacă comentariile din cod nu le acoperă)

Implementare

  • De specificat dacă întregul enunț al temei e implementat sau ce bucăți
  • Dacă există funcționalități extra, pe lângă cele din enunț - dați o descriere succintă (maximum 3-4 rânduri/funcționalitate) și motivarea lor (maximum 1-2 fraze)
  • De specificat funcționalitățile lipsă din enunț, dar necesare realizării complete a temei (dacă există) și de menționat dacă testele reflectă sau nu acest lucru
  • Dificultăți întâmpinate
  • Lucruri interesante descoperite pe parcurs

Cum se compilează și cum se rulează?

  • Explicație, ce biblioteci linkează, cum se face build
  • Cum se rulează executabilul, se rulează cu argumente (sau nu)

Bibliografie

  • Resurse utilizate - toate resursele publice de pe internet/cărți/code snippets, chiar dacă sunt laboratoare de SO

Git

Link către repo-ul de git folosit pentru localizarea surselor

Ce să NU conțină un readme

  • Detalii de implementare despre fiecare funcție/fișier în parte
  • Fraze lungi care să ocolească subiectul în cauză
  • Răspunsuri și idei neargumentate
  • Comentarii copy/paste din cod și TODO-uri

Cele mai frecvente greșeli întâlnite în teme

  • Neverificarea valorilor de retur a apelurilor de sistem și implicit netratarea erorilor. Aici sunt două variante:
    • fie se termină programul brusc prin folosirea unui assert sau a macro-ului DIE
    • dacă eroarea nu este “neapărat” fatală și permite continuarea rulării programului trebuie acumulate undeva erorile și se recomandă folosirea unor debug prints

Se preferă abordarea din primul bullet (DIE/assert). Dacă un apel low-level de sistem pică este indicată de cele mai multe ori o problemă mai adâncă la nivelul sistemului și preferăm un die general.

  • Alocarea dinamică atunci când se poate folosi cea statică (mai puțin heap consumat și mai puțini ciclii!)
    • exemplu: cerința spune că un buffer de comenzi are maxim 256 de caractere. Mai bine alocăm un char cmd[256]
    • tot în acest caz (și altele similare) întâlnim des folosirea de valori hardcodate. Corect este:
   /* Undeva într-un fișier .h */
   #define MAX_CMD_BUF_SIZE		256
 
   (...)
 
   /* Undeva în sursele .c unde avem nevoie de buffer */
   char cmd[MAX_CMD_BUF_SIZE];
 
  • Neînchiderea/necurățarea resurselor folosite:
    • Memorie alocată dinamic (cu malloc/calloc) neeliberată folosind free - se poate verifica utilizând valgrind
      • Fișiere deschise cu open care nu mai sunt închise folosind close - se poate folosi opțiunea –track-fds a valgrind
      • Procese pornite cu fork după care nu se face wait/waitpid - rezultă în apariția unor procese zombie
  • Implementare nemodulară, într-un singur fișier. Exemplu: avem de implementat un hashtable cu bucket-urile fiind liste înlănțuite
    • semnăturile funcțiile, macro-uri, definiții pentru structuri utile lucrului cu liste se pun intr-un header list.h
    • implementările funcțiilor de mai sus se fac în sursa list.c
    • semnăturile funcțiile, macro-uri, funcție de hash, definiții pentru structuri utile lucrului cu hashtable se pun intr-un header hashtable.h
    • implementările funcțiilor de mai sus se fac în sursa hashtable.c
    • lucrul cu apeluri de sistem (e.g. wrappere) sau macro-uri de assert se pot pune într-un util.h și util.c
    • NOTĂ: Funcțiile și variabilele globale interne unui modul (e.g. funcții folosite doar în list.c) se declară folosind calificatorul static.

Nu aruncați toată implementarea temei într-un singur fișier .c chiar dacă merge. Face imposibil de urmărit codul și pentru voi dar și pentru asistenți sau pentru cei care peste 5 ani vor citi codul vostru. Țineți minte următorul motto:

Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live.

Sursa: http://wiki.c2.com/?CodeForTheMaintainer

so/teme/general.txt · Last modified: 2020/03/09 09:41 by razvan.deaconescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0