Ci sono delle linee guida comunemente accettate su come scrivere la C moderna?

13

Ho un strong background Java / Groovy e sono stato assegnato a un team che mantiene una base di codice C piuttosto ampia per un software amministrativo.

Alcuni punti critici, come trattare il BLOB nel database o generare report in PDF ed Excel sono stati esternalizzati al servizio web java.

Tuttavia, come sviluppatore Java, sono un po 'confuso da alcuni aspetti del codice:

  • è prolisso (specialmente quando si tratta di 'eccezione')
  • ci sono molti metodi enormi (molti metodi con oltre 2000 righe)
  • non ci sono strutture dati avanzate (mi manca Lista, Set e Mappa molto)
  • nessuna separazione di interesse (SQL è mescolato con gioia tutto intorno al codice)

Come risultato, sento che il business è nascosto in tonnellate di codice tecnico e il mio cervello, modellato con Object Oriented e un pizzico di programmazione funzionale, non è a suo agio.

Il lato positivo del progetto è che il codice è diretto: non esiste un framework, nessuna manipolazione del codice byte in fase di esecuzione, nessun AOP. E il server può rispondere simultaneamente a più di 10000 utenti con una sola macchina, utilizzando meno memoria di quella che ha bisogno di java per sputare "Ciao mondo".

Voglio imparare come scrivere il codice C in base ai principi moderni comunemente accettati. Esistono principi comunemente accettati su come la C moderna deve essere scritta e strutturata?

Qualcosa di simile all'equivalente del libro 'Effective Java', ma per C.

Modifica alla luce di risposte e commenti:

  • Cercherò di adattare la mia mentalità al codice C e non provare a specularla su OOP.
  • Ho iniziato a leggere le guide di stile di codifica consigliate dal commento (GNU Coding Standards e Linux Kernel Coding Style).
  • Quindi proverò a proporre questo stile di codice ai miei colleghi. La parte più difficile potrebbe essere quella di convincere i colleghi che un enorme metodo potrebbe essere suddiviso in parti più piccole e che per ripetere le stesse 4 righe del codice di gestione degli errori potrebbe essere evitato con l'aiuto di un metodo.
posta Guillaume 03.11.2016 - 14:04
fonte

4 risposte

14

Posso leggere dalla tua domanda che il problema non è che il codice è vecchio C ma solo cattivo programmazione. La maggior parte dei problemi che hai menzionato come verbosità, enormi funzioni di linea 2000+ o nessuna separazione di problemi è applicabile a qualsiasi linguaggio, C o Java allo stesso modo.

La verbosità è stata menzionata nel contesto della gestione degli errori. Non hai fornito un esempio, quindi posso solo ricordare che il codice di gestione degli errori è anche il codice . Non ci sono scuse per le sezioni ripetitive del codice boilerplate. Factor fuori; a una funzione o (se non vale la pena creare una funzione separata) fai il modello goto Error; e sposta la gestione degli errori e la pulizia delle risorse in una sezione Error: nella parte inferiore della funzione.

Se passare l'errore sulla catena di chiamata sembra essere il problema, chiediti: la funzione lassù veramente ha bisogno di sapere che qualche tizio qui sotto ha avuto un problema? I meccanismi di eccezione incorporati in una lingua rendono facile farlo, ma in generale è meglio gestire le eccezioni in anticipo (in qualsiasi lingua) in modo che la condizione di errore non inquini la logica del codice di alto livello. E se la funzione lassù veramente deve sapere, ci sono modi per emula le eccezioni con setjmp e longjmp .

Penso che l'unico problema C-related menzionato sia la mancanza di contenitori standard. Mentre Set può in generale essere sostituito con una matrice ordinata e Map (per la maggior parte) con una matrice di coppie o una struct (se conosci il set di chiavi prima della mano, map[key] = value si trasforma in s.key = value ), ma il fatto è che non c'è alcun contenitore di array dinamico nella libreria standard. In C99 puoi almeno dichiarare una matrice di lunghezza variabile nello stack ( int array[len] ) ma devi calcolare in anticipo len (di solito non è difficile) e ovviamente non puoi restituirlo come qualsiasi oggetto allocato allo stack. La maggior parte dei progetti termina con la scrittura del proprio contenitore di array dinamico o l'adozione di uno open source.

In una nota di chiusura, vorrei sottolineare che sono stato lì. Sono stato il programmatore Java che si è spostato in C ++ e in puro C. Vorrei consigliare "leggere il libro X per imparare la buona C" ma non ce n'è alcuno come non ce n'è per Java. Il modo per andare avanti è quello di assorbire tutte le intricatezze della lingua e della libreria standard; google molto, leggi molto e codice molto finché non inizi a pensare in C. Cercare di scrivere cose in C come faresti in Java è frustrante come provare a scrivere una frase in una lingua straniera con parole tradotte direttamente dalla tua lingua madre; sia tu che il lettore ti vergognerai. La buona notizia è che l'apprendimento della buona programmazione è lento, ma l'apprendimento di un'altra lingua è veloce. quindi se scrivi codice decente in Java, applicando lo stesso senso comune a C funzionerà anche.

    
risposta data 05.11.2016 - 10:01
fonte
7

The good side of the project is that the code is straight forward: there is no framework, no byte code manipulation at runtime, no AOP. And the server can simultaneously answer to 10000+ users with a single machine by using less memory than java needs to spit "hello world".

Vorrei raccomandare di essere cauti sul fatto che questo valga il tuo tempo e il denaro della società per spendere risorse per "modernizzare" un software funzionante con una bassa complessità del codice e che funzioni bene. C'è un'alta probabilità che introduci nuovi bug, specialmente perché sembra essere un sistema con cui non hai familiarità.

Se vuoi continuare su questa strada, ti suggerisco di seguire:

  • Crea (o genera) un diagramma di stato del software / codice
  • Immergiti nel codice e crea una lista delle parti più complesse o critiche del codice, rispettivamente
  • Trova qualcuno che sia a conoscenza di quel codice e chiedi loro perché è stato costruito in questo modo e ciò che è noto causa problemi
  • Scrivi la documentazione da ciò che hai imparato

A questo punto, decidi se valga la pena esplorare. Se la cultura della tua azienda non premia il fallimento, ottieni il semaforo verde da un superiore o un gestore.

  • Compartimentalizza i diversi blocchi costitutivi del software e scrivi i test unitari per ciascuno.
  • Esegui l'iter fino a quando non puoi incollare insieme i diversi moduli
  • Esegui ulteriori test che simulino l'interazione dell'utente reale (stress test ecc.)

Penso che sia una buona cartina stradale e ti porti dove ti serve. Senza conoscere le specifiche di questo progetto è difficile aiutarti molto. Si prega di non scartare il mio disclaimer come eccessivamente allarmista. Le tonnellate di eccellenti programmatori hanno battuto la polvere cercando di riscrivere un progetto esistente nella loro lingua preferita o usando strumenti "moderni". Questa è una decisione che deve essere attentamente pensata e vi esorto a non fare il ladro e farlo da soli senza il supporto della direzione o l'assistenza dei vostri colleghi.

    
risposta data 03.11.2016 - 22:06
fonte
2

Se preferisci un linguaggio di livello superiore, ci sono alcuni linguaggi come C ++ o Objective-C che possono essere mescolati con codice C abbastanza facilmente.

In alternativa, C e C ++ sono ragionevolmente compatibili. Potresti essere in grado di compilare l'intero codebase come C ++ con poche modifiche - avrai la variabile occasionale chiamata "class" o "template" che dovrai rinominare, ma in pratica sarà tutto. (sizeof ('a') è diverso in C e C ++, ma non credo di averlo mai usato).

Se segui questa strada, considera che il prossimo manutentore potrebbe non essere molto fluente con C ++. Non lasciarti trasportare. Approfitta del C ++, ma solo così lontano che un programmatore C può facilmente dare un senso a questo.

    
risposta data 05.11.2016 - 23:16
fonte
1

Fondamentalmente, scrivere un buon codice C equivale a scrivere un buon codice C ++ o Java: vuoi una classe, usa un struct . Vuoi ereditarietà, includi la base struct come primo membro senza nome. Vuoi funzioni virtuali, aggiungi un puntatore ad una struct di puntatori di funzione statici. E così via, ecc. È esattamente ciò che fa C ++ sotto la cappa, l'unica differenza è che è esplicito in C. Quindi, puoi fare perfettamente la programmazione orientata agli oggetti in C, sembra solo un po 'diverso e più caldo di quello che tu sono abituati a

Il punto è che una buona programmazione riguarda i paradigmi, non le caratteristiche del linguaggio. È vero, è sempre bello se le funzionalità della lingua forniscano un valido supporto per i paradigmi che si desidera utilizzare, ma le funzionalità linguistiche non sono un requisito. Una volta capito questo, puoi scrivere un buon codice praticamente in qualsiasi lingua (a parte alcuni linguaggi esoterici come Brainfuck o INTERCAL, cioè).

Ovviamente, il problema rimane che la libreria C standard non contiene nessuna di quelle classi di contenitore eleganti a cui sei abituato. Sfortunatamente, ciò significa che dovrai utilizzare il tuo o aggirare questa mancanza utilizzando array allocati dinamicamente. Ma scommetto che presto scoprirai che tutto ciò di cui hai veramente bisogno sono gli array dinamici ( malloc() ) e gli elenchi / alberi collegati che vengono implementati tramite i membri del puntatore all'interno delle tue classi.

    
risposta data 11.11.2016 - 15:15
fonte

Leggi altre domande sui tag