Il C ++ è adatto per i sistemi embedded?


168

Una domanda comune, qui e altrove. Il C ++ è adatto per i sistemi embedded?

Microcontrollori? RTOS? Tostapane? PC incorporati?

OOP è utile sui microcontrollori?

Il C ++ rimuove il programmatore troppo lontano dall'hardware per essere efficiente?

Il C ++ di Arduino (senza gestione dinamica della memoria, modelli, eccezioni) dovrebbe essere considerato "C ++ reale"?

(Spero che questa wiki serva come luogo per contenere questa potenziale guerra santa)


5
Domanda veloce: quando dici incorporato , intendi microcontrollore? microprocessore? x86 incorporato / PC incorporato?
J. Polfer,

1
Non intendevo provocare una guerra santa; l'intento era quello di scoprire quali erano le tue argomentazioni contrarie.
J. Polfer,

2
È emerso in diverse domande prima, quindi ho pensato che un posto centrale sarebbe stato buono.
Toby Jaffey,

4
C ++ vs embedded è un argomento controverso. Ho una forte opinione, ma non pensavo fosse giusto sollevare una domanda e giocare a punti. Spero che una wiki della community possa favorire una discussione più equilibrata.
Toby Jaffey,

13
Questa è una cattiva domanda poiché "incorporato" è un attributo insignificante nel decidere se una lingua particolare e il suo bagaglio associato sono adatti. Il punto è piccolo rispetto ai sistemi di grandi dimensioni, in cui i piccoli sistemi non eseguono un sistema operativo, hanno memoria limitata, potrebbero non essere von-Neuman, potrebbero avere varie restrizioni hardware su stack di chiamate, stack di dati, non è possibile allocare dinamicamente un Mb o anche un kb, ecc. La maggior parte dei microcontrollori sono sistemi "piccoli". I computer a scheda singola sono generalmente incorporati, ma generalmente sono sistemi "di grandi dimensioni".
Olin Lathrop,

Risposte:


135

Sì, il C ++ è ancora utile nei sistemi integrati. Come hanno detto tutti gli altri, dipende ancora dal sistema stesso, come se un uC a 8 bit sarebbe probabilmente un no-no nel mio libro anche se c'è un compilatore là fuori e alcune persone lo fanno (rabbrividire). C'è ancora un vantaggio nell'usare C ++ anche quando lo ridimensioni a qualcosa come "C +" anche in un micro mondo a 8 bit. Cosa intendo per "C +"? Voglio dire, non usare nuovo / elimina, evita eccezioni, evita le classi virtuali con ereditarietà, possibilmente evita l'ereditarietà tutti insieme, stai molto attento con i modelli, usa le funzioni incorporate anziché le macro e usa le constvariabili invece di #defines.

Lavoro in C e C ++ nei sistemi embedded da oltre un decennio ormai e parte del mio entusiasmo giovanile per C ++ si è definitivamente esaurito a causa di alcuni problemi del mondo reale che scuotono la propria ingenuità. Ho visto il peggio di C ++ in un sistema embedded che vorrei definire come "programmatori CS impazziti in un mondo EE". In effetti, è qualcosa su cui sto lavorando con il mio cliente per migliorare questa base di codice che hanno tra gli altri.

Il pericolo del C ++ è perché è uno strumento molto potente molto simile a una spada a doppio taglio che può tagliare sia il braccio che la gamba se non istruito e disciplinato correttamente nel suo linguaggio e nella stessa programmazione generale. C è più simile a un'arma a un taglio, ma comunque altrettanto affilata. Con C ++ è troppo facile ottenere livelli molto alti di astrazione e creare interfacce offuscate che diventano insignificanti a lungo termine, e ciò è in parte dovuto alla flessibilità C ++ nel risolvere lo stesso problema con molte funzionalità linguistiche diverse (modelli, OOP, procedurali, RTTI, modelli OOP +, sovraccarico, allineamento).

Ho terminato due seminari di 4 ore su Embedded Software in C ++ del guru C ++, Scott Meyers. Ha sottolineato alcune cose sui modelli che non avevo mai considerato prima e quanto più possono aiutare a creare codice critico per la sicurezza. Il fatto è che non è possibile avere un codice morto nel software che deve soddisfare severi requisiti di codice critici per la sicurezza. I modelli possono aiutarti a raggiungere questo obiettivo, poiché il compilatore crea solo il codice necessario per creare un'istanza dei modelli. Tuttavia, è necessario acquisire un'istruzione più approfondita nel loro uso per progettare correttamente questa funzione che è più difficile da realizzare in C perché i linker non ottimizzano sempre il codice morto.

Scott Meyers è un grande sostenitore dei template e dell'uso giudizioso dell'inline, e devo dire che sono ancora scettico sull'essere entusiasta dei template. Tendo a evitarlo, anche se dice che dovrebbero essere applicati solo dove diventano lo strumento migliore. Sottolinea inoltre che C ++ offre gli strumenti per creare interfacce davvero valide, facili da usare e che rendono difficile l'uso sbagliato. Ancora una volta, questa è la parte difficile. Uno deve raggiungere un livello di padronanza in C ++ prima di poter sapere come applicare queste funzionalità nel modo più efficiente per essere la migliore soluzione di progettazione.

Lo stesso vale per OOP. Nel mondo incorporato, è necessario acquisire familiarità con il tipo di codice che il compilatore sputerà per sapere se è possibile gestire i costi di runtime del polimorfismo di runtime. Devi anche essere disposto a effettuare misurazioni per dimostrare che il tuo progetto soddisferà i tuoi requisiti di scadenza. Quella nuova classe InterruptManager renderà la mia latenza di interrupt troppo lunga? Esistono altre forme di polimorfismo che potrebbero adattarsi meglio al tuo problema, come il polimorfismo del tempo di collegamento che C può fare anche, ma C ++ può fare attraverso il modello di progettazione Pimpl (puntatore Opaque) .

Dico tutto per dire che il C ++ ha il suo posto nel mondo embedded. Puoi odiare tutto quello che vuoi, ma non sta andando via. Può essere scritto in modo molto efficiente, ma è più difficile imparare a farlo correttamente che con C. A volte può funzionare meglio di C nel risolvere un problema e talvolta esprimere un'interfaccia migliore, ma ancora una volta, devi educare te stesso e non aver paura di imparare come.


1
Ciò è in linea con quanto ho letto da altri consulenti di sistemi embedded. Mi è sempre stato insegnato che con C, ti tagli costantemente in piccoli modi, ma un bug in C ++ sarà più raro, ma quando sbagli ti perderai una gamba. Grazie per aver scritto una risposta chiara con alcune competenze che non ho.
Kortuk,

3
Sulle note di laurea in Informatica che impazziscono in terra EE. Nel mio lavoro il peggio di codice che abbiamo è stato scritto da un maggiore CS. Abbiamo trascorso per sempre cercando di insegnargli quale fosse l'hardware. Ha creato un sistema strutturato usando UML e ha costruito l'intero sistema basato su di esso in Java usando l'eredità corretta e così via. Ha funzionato, fino a quando non è cambiato nulla, quindi è stato un brutto lavoro di patch per aggiungere funzionalità o una riprogettazione completa. Il codice non è quasi utilizzabile a causa di quanto accuratamente offuscasse l'intera cosa con l'eredità.
Kortuk,

2
Non sono solo i bug che ti mordono, ma anche il codice non mantenibile che ti morde. Certo, quando inizi quel progetto usando quelle caratteristiche C ++ pulite, tutto procede a meraviglia, ma dopo 2 o 3 anni entra in gioco l'entropia se nessuno sforzo serio viene messo in refactoring mentre sviluppi. Questo è quello che sto affrontando in questo momento ... il codice marcisce più velocemente nel tempo in C ++.
Jay Atkinson,

2
UML e macchine a stati. Devi davvero esaminare le cose di Miro Samek su state-machine.com . Ha costruito un sistema efficiente che è facile da refactoring e cambiare, ma ci vuole del tempo per farlo.
Jay Atkinson,

2
Dipende molto dal tuo sistema e da quanta memoria hai a disposizione. Stai scrivendo codice su un micro a 8 bit con pochissima RAM? Quindi potresti stare meglio evitando di impazzire su interfacce astratte. Se stai scrivendo qualcosa di simile a sistemi embedded a 32 bit con lastre di memoria, provaci. Devi davvero soppesarlo. Ad esempio, ogni volta che si attacca il mondo "virtuale" a quella classe, si ottiene un puntatore aggiuntivo, che potrebbe essere 8 bit, 16 bit o 32 bit a seconda del sistema, per ogni singola istanza dichiarata di quell'oggetto. Non ti renderai nemmeno conto, amico,
Jay Atkinson,

56

Il C ++ è assolutamente adatto per i sistemi embedded. Ora uso la presenza / assenza di buoni strumenti di sviluppo (o la loro mancanza) come criterio principale per l'utilizzo o meno di un determinato microprocessore.

Aree di C ++ che possono essere utilizzate su sistemi embedded perché hanno bassi costi di risorse:

  • modularità apportata dal buon uso di classi / strutture
  • modelli se il compilatore fa un buon lavoro nel compilarli in modo efficiente. I modelli sono un buon strumento per portare il riutilizzo degli algoritmi a diversi tipi di dati.

Aree OK:

  • funzioni virtuali: ero contro questo, ma il costo delle risorse è molto piccolo (una vtable per classe , non per oggetto; un puntatore alla vtable per oggetto; un'operazione di dereferenziazione per chiamata di funzione virtuale) e il grande vantaggio di questo è che ti permette di avere un array contenente diversi tipi di oggetti senza dover sapere di che tipo sono. L'ho usato di recente per avere una serie di oggetti ognuno dei quali rappresenta un dispositivo I2C, ciascuno con metodi separati.

Aree da non utilizzare, principalmente a causa del sovraccarico di runtime inaccettabile su piccoli sistemi:

  • allocazione dinamica della memoria - altri lo hanno menzionato, ma un'altra importante ragione per non usare l'allocazione dinamica della memoria è che rappresenta l'incertezza nei tempi; molte ragioni per usare i sistemi embedded sono per applicazioni in tempo reale.
  • RTTI (informazioni sul tipo di tempo di esecuzione): il costo della memoria è piuttosto elevato
  • eccezioni: un preciso no-no, a causa del colpo di velocità di esecuzione

Grazie per l'input. Interessante e molto simile a quello che ho letto.
Kortuk,

1
L'allocazione della memoria in realtà dinamica va bene e talvolta inevitabile. È il problema la disallocazione dinamica della memoria (e il successivo riutilizzo). RTTI è un porco di memoria, sono d'accordo su quello. Ma qual è il problema con le eccezioni?
Wouter van Ooijen,

1
@WoutervanOoijen: il problema con le eccezioni è che se le foochiamate barall'interno di un blocco try/ catche barcreano alcuni oggetti e chiamate boz, il che genera un'eccezione, il sistema deve in qualche modo chiamare i distruttori per gli oggetti barcreati prima di restituire il controllo foo. A meno che le eccezioni non siano completamente disabilitate, barnon avranno modo di sapere se bozpotrebbero essere lanciate, e deve quindi includere un codice aggiuntivo per consentire tale possibilità. Mi piacerebbe vedere una variazione di C ++ con "eccezioni controllate" per affrontarlo; se routine che potrebbero consentire la fuga delle eccezioni ...
supercat

1
... doveva essere dichiarato come tale, quindi sarebbe solo necessario che il compilatore includesse il codice di gestione delle eccezioni nei chiamanti di tali routine. Certo, dover aggiungere tutte le dichiarazioni richieste, pur sperando di evitare quelle non necessarie, sarebbe un po 'oneroso, ma consentirebbe di utilizzare le eccezioni nei luoghi in cui sono utili, senza aggiungere spese generali dove non lo sono.
supercat,

3
@WoutervanOoijen: Per inciso, se stavo progettando l'ABI per tale gestione delle eccezioni su ARM, specificarei quel codice che chiama una routine che può uscire tramite un'eccezione dovrebbe avere R14 che punta a un indirizzo due byte prima dell'indirizzo di ritorno desiderato (questo sarebbe si verificano naturalmente se il chiamante ha seguito l'istruzione CALL con una parola a 16 bit). La routine chiamata sarebbe quindi uscita normalmente tramite add r15,r14,#2invece di mov r15,r14; per uscire via eccezione, ldrhs r0,[r14] / add r15,r14,r0. Costo del ciclo zero per l'uscita normale e nessuna restrizione sullo stack frame.
supercat,

36

Sì, C ++ è sicuramente adatto per sistemi embedded. Innanzitutto chiariamo un paio di idee sbagliate sulla differenza tra C e C ++:

In un micro incorporato, avrai sempre bisogno di usare con attenzione le lingue di alto livello se sei preoccupato per i limiti di tempo o spazio. Ad esempio, molte MCU non gestiscono bene i puntatori, quindi sono molto inefficienti quando si utilizza lo stack. Questo significa che devi stare attento a passare le variabili alle funzioni, usando matrici e puntatori e ricorsione. Una semplice linea di C come:

a[i] = b[j] * c[k];

può generare circa 4 pagine di istruzioni a seconda della natura di tali variabili.

Ogni volta che usi un linguaggio di alto livello e sei preoccupato per i limiti di tempo e spazio, devi sapere come ogni funzione di quella lingua si traduce in istruzioni automatiche sul tuo MCU (almeno, ogni caratteristica che usi). Questo vale per C, C ++, Ada, qualunque cosa. Probabilmente tutte le lingue conterranno funzionalità che non si traducono in modo efficiente su piccoli MCU. Controlla sempre gli elenchi di smontaggio per assicurarti che il compilatore non generi risme di istruzioni per qualcosa di banale.

C è adatto per MCU integrate? Sì, purché si tenga d'occhio il codice generato.
Il C ++ è adatto per MCU integrate? Sì, purché si tenga d'occhio il codice generato.

Ecco perché penso che C ++ sia migliore di C anche su MCU a 8 bit: C ++ offre un supporto migliorato per:

  • Dati nascosti
  • Digitazione / controllo più forti
  • Trasparenza multi-periferica usando le classi
  • Modelli (come sempre se usati con attenzione)
  • Elenchi di inizializzazione
  • const

Nessuna di queste funzioni è più pesante delle caratteristiche tipiche di C.

Quando si spostano MCU fino a 16 o 32 bit, allora ha senso utilizzare le funzioni più pesanti di C (stack, heap, puntatori, array, printf, ecc.) Allo stesso modo, su un MCU più potente diventa appropriato per utilizzare le funzioni più pesanti di C ++ (stack, heap, riferimenti, STL, nuovo / elimina).

Quindi, non è necessario rabbrividire al pensiero di C ++ su un PIC16. Se conosci correttamente la tua lingua e il tuo MCU, saprai come usarli entrambi in modo efficace insieme.


3
Questa è una risposta molto ben espressa e ragionevole alla domanda. +1 Saluti!
Vicatcu,

1
" a[i] = b[j] * c[k];può generare circa 4 pagine di istruzioni a seconda della natura di tali variabili." Se il tuo MCU / compilatore fa questo, è perché stai usando una CPU per hobbisti da garage degli anni '80.
Lundin,

@Lundin - Sospiro. No, significa che stai usando un piccolo MCU economico progettato per essere il più piccolo ed economico possibile, per non avere cose complesse come l'indicizzazione dello stack.
Rocketmagnet,

2
@Rocketmagnet Ok forse negli anni '90? Oggi i pessimi 8 bitter hanno lo stesso prezzo di un 32 bitter. L'unico motivo rimasto per scegliere il primo è il consumo attuale. E per quanto riguarda quegli 8 bitter extra-schifosi senza stack: se scrivi C invece di assemblatore per un MCU così limitato, probabilmente stai sbagliando. Le 4 pagine generate sono quindi colpa tua per la scrittura di programmi troppo complessi per la CPU, ed essenzialmente C è lo strumento sbagliato per l'attività. (L'ho fatto in passato su Freescale RS08, è stata un'idea molto stupida.)
Lundin,

Il processore @Lundin a 32 bit non è necessario più velocemente di 16 bit. Ciò era evidente nel lontano giorno in cui la transizione del programma da 16 a 32 bit stava avvenendo nel mondo dei PC.
Barleyman il

24

Trovo sempre che questi dibattiti siano divertenti da leggere. Non tanto per la discussione intellettuale sui pro e contro delle varie lingue disponibili, ma perché di solito puoi definire la posizione di qualcuno sull'argomento in base al suo lavoro / esperienza / area di interesse. È proprio lì con le argomentazioni di "ottimizzazione prematura" in cui i maggiori CS e i programmatori di manutenzione citano Knuth a destra ea sinistra e quelli che lavorano nel mondo reale in cui le questioni relative alle prestazioni pensano che siano tutte pazze (sono un membro di quest'ultimo gruppo ad essere onesti).

Alla fine della giornata, puoi sviluppare un software eccellente in C o C ++ o inserire la lingua qui . Dipende dalle capacità dello sviluppatore e non dalla lingua. Essere un esperto in una lingua è di solito richiesto solo se hai scelto la lingua sbagliata per cominciare e ora devi portarla a risolvere il tuo problema, nella maggior parte dei casi queste sono le uniche situazioni in cui devi immergerti in funzionalità oscure o compilatore trucchi per raggiungere l'obiettivo.

Sento spesso che le persone iniziano questi argomenti come "Sono un esperto di linguaggio X e blah blah" Onestamente scredito immediatamente queste persone perché, secondo me, hanno già affrontato il problema da un'angolazione sbagliata e tutto ciò che è stato contaminato dal loro desiderio di usare il loro strumento per risolvere il problema e mostrare quanto sia "bello".

Guardo così spesso gli sviluppatori che scelgono prima un set di strumenti e provo a piegarlo al loro problema in secondo luogo, il che è completamente sbagliato e si traduce in soluzioni schifose.

Come ho detto in un commento a un'altra risposta, queste guerre linguistiche spesso si rivolgono a sostenere che il linguaggio X consente al programmatore di fare cose più stupide. Mentre è divertente da leggere, tutte queste affermazioni significano davvero che hai un problema ad assumere buoni sviluppatori e devi affrontare direttamente quel problema piuttosto che cercare di aiutare la situazione a continuare a assumere cattivi sviluppatori e scegliere strumenti in modo che possano fare il minimo danno possibile.

Secondo me buoni sviluppatori, che si tratti di sviluppo di software o hardware, ricercano il problema, progettano una soluzione e trovano gli strumenti che consentono loro di esprimere la soluzione nel "modo migliore". Non importa se lo strumento richiesto è qualcosa che non hai mai usato prima, dopo aver usato 3-4 lingue / strumenti di sviluppo per i progetti che ne prendono uno nuovo dovrebbe avere un impatto minimo sul tempo di sviluppo.

Naturalmente, "modo migliore" è un termine soggettivo e deve anche essere definito nella fase di ricerca. È necessario considerare una moltitudine di problemi: prestazioni, facilità di espressione, densità del codice, ecc. In base al problema in questione. Non ho incluso la manutenibilità in quell'elenco per un motivo, non mi interessa quale lingua scegliate, se avete scelto lo strumento giusto e vi siete presi il tempo per capire il problema, questo dovrebbe venire "gratuitamente". Difficile mantenere il codice è spesso il risultato della scelta dello strumento sbagliato o di una struttura di sistema scadente, questo si traduce in un brutto pasticcio disordinato per farlo funzionare.

Affermare che una lingua è "migliore" di qualsiasi altra è sciocca senza definire un particolare problema di interesse. Un approccio orientato agli oggetti non è sempre migliore di un approccio funzionale. Ci sono alcuni problemi che si prestano molto bene a un paradigma progettuale orientato agli oggetti. Ce ne sono molti che non lo fanno. La stessa affermazione può essere fatta su molte funzionalità linguistiche su cui le persone sembrano apprezzare.

Se stai spendendo più del 20% del tuo tempo su un problema con la digitazione del codice, probabilmente stai producendo un sistema molto scarso o hai sviluppatori molto poveri (o stai ancora imparando). Dovresti dedicare la maggior parte del tuo tempo a tracciare il problema e determinare come interagiscono i vari elementi dell'applicazione. Attaccare un gruppo di talentuosi sviluppatori in una stanza con un tabellone e un problema da risolvere e dire loro che non sono autorizzati a scrivere alcun codice o scegliere qualsiasi strumento fino a quando non si sentono a proprio agio con l'intero sistema farà di più per migliorare la qualità del produzione e velocità di sviluppo rispetto alla scelta di qualsiasi nuovo strumento caldo garantito per migliorare i tempi di sviluppo. (cerca lo sviluppo della mischia come riferimento per l'opposto polare del mio argomento)

Spesso la sfortunata realtà è che molte aziende possono misurare il valore di uno sviluppatore solo in base al numero di righe scritte o visualizzando "output tangibile". Considerano le 3 settimane in una stanza con un tabellone come una perdita di produttività. Gli sviluppatori sono spesso costretti ad accelerare la fase di sviluppo del "pensiero" o sono costretti a utilizzare uno strumento impostato da un problema politico all'interno dell'azienda, "Il fratello del mio capo lavora per IBM in modo che possiamo usare solo i loro strumenti", quel tipo di spazzatura . O peggio, si ottiene una serie di requisiti in costante cambiamento dall'azienda perché non sono in grado di fare adeguate ricerche di mercato o non comprendono l'impatto dei cambiamenti sul ciclo di sviluppo.

Mi dispiace di essere leggermente fuori tema con questo rant, ho opinioni abbastanza forti su questo argomento.


2
Ora, non sto superando i test unitari a livello di applicazione (sopra il driver) su alcuni sistemi embedded. C'è un certo valore nel feedback istantaneo del test unitario e del rooting out dei bug all'inizio della fase di sviluppo, ma l'intero paradigma TDD per dare vita al progetto mi sembra un po 'complicato. Preferisco prendermi del tempo per "pensare" al problema e disegnarlo nella mia testa, sulla carta o su una lavagna, prima di iniziare a scrivere codice. Penso anche che TDD incoraggi il mercato a non effettuare ricerche iniziali sui requisiti, perché dovrebbe aiutare a cambiare costantemente i requisiti.
Jay Atkinson,

2
E per mettere un'ultima nota sul mio commento super lungo .. Non abbiamo bisogno di esperti di lingua per lavorare al design. Abbiamo bisogno di progettisti esperti che sappiano lavorare le lingue.
Jay Atkinson,

1
PRD = documento sui requisiti del prodotto, MRD = documento sui requisiti di marketing, TRD = documento sui requisiti tecnici. TDD = Test Driven Development.
Jay Atkinson,

1
@Mark - Sono d'accordo con i tuoi sentimenti di design-up-front, ma solo fino a un certo punto. Penso che pesanti lavori di progettazione anticipata ripagano se a) i requisiti sono abbastanza stabili / noti eb) gli sviluppatori che fanno il progetto hanno esperienza . In un precedente lavoro, mi è stato affidato il compito di fare un disegno ed è stato pesantemente messo in timebox dal mio capo squadra, e ho pensato: "Che cosa stupida da fare! Design-up-front risparmia denaro (vedi il libro Codice completo) ??" Ma nel codice ho scoperto tonnellate di cose che non sapevo cercare. Se avessi fatto un sacco di progettazione e minimizzato il tempo di programmazione, sarebbe stato uno spreco. JME.
J. Polfer,

1
@sheepsimulator Sono ovviamente d'accordo sul secondo punto, presumo che i principali architetti di sistema siano sviluppatori esperti. Sul primo punto in realtà non sono d'accordo. Penso che più ti aspetti che i requisiti cambino, più tempo dovresti dedicare in fase di progettazione perché devi produrre un design buono, facile da cambiare. So che alcune filosofie propongono un rapido sviluppo. In alcuni casi questo funziona bene come molti programmatori cattivi o inesperti nello staff. Tutte queste filosofie progettuali finiscono col dire "non ho la preghiera di progettare un sistema flessibile, quindi non perdiamo tempo a provare".
Segna il

17

Qualsiasi lingua può essere adatta per un sistema incorporato. Incorporato significa solo: parte di un apparato più grande, al contrario di un computer gratuito.

La domanda ha maggiore rilevanza quando viene chiesto un sistema (difficile) in tempo reale o con risorse limitate .

Per un sistema in tempo reale, C ++ è uno dei linguaggi più alti che è ancora appropriato quando si programmano vincoli temporali rigorosi. Ad eccezione dell'uso dell'heap (operatore gratuito) non ha costrutti che hanno un tempo di esecuzione indeterminato, quindi puoi verificare se il tuo programma soddisfa i suoi requisiti di temporizzazione e con qualche esperienza in più potresti persino prevederlo. Ovviamente l'uso dell'heap dovrebbe essere evitato, sebbene il nuovo operatore possa ancora essere utilizzato per l'allocazione una tantum. I costrutti offerti da C ++ su C possono essere utilizzati in un sistema incorporato: OO, eccezioni, modelli.

Per sistemi a risorse molto limitate (chip a 8 bit, meno di pochi KB di RAM, nessuno stack accessibile) il C ++ completo potrebbe non essere adatto, anche se potrebbe comunque essere usato come una "C migliore".

Penso che sia un peccato che Ada sembri essere usata solo in alcune nicchie. In molti modi è un Pascal ++, ma senza l'onere di essere compatibile con un linguaggio che era già un vero casino per cominciare. (modifica: il disordine serio è ovviamente C. Pascal è un linguaggio bello ma in qualche modo poco pratico.)

================================================== ==============

EDIT: stavo scrivendo una risposta a una nuova domanda ("In quali casi è necessario il C ++ quando stiamo programmando i microcontrollori"?) Che è stato chiuso facendo riferimento a questo, quindi aggiungerò quello che ho scritto:

Non c'è mai una ragione generale per l'uso di alcun linguaggio di programmazione, ma possono esserci argomenti che hanno più o meno peso in una particolare situazione. Discussioni su questo possono essere trovate in molti luoghi, con posizioni che vanno da "mai usare C ++ per un microcontrollore" a "usare sempre C ++". Sono più con l'ultima posizione. Posso dare alcune argomentazioni, ma dovrai decidere tu stesso quanto peso hanno in una particolare situazione (e in quale direzione).

  • I compilatori C ++ sono più rari dei compilatori C ++; per alcuni target (ad esempio PIC core a 12 e 14 bit) non ci sono compilatori C ++.
  • (buoni) i programmatori C ++ sono più rari dei (buoni) programmatori C, specialmente tra quelli che sono (un po ') ben informati nell'elettronica.
  • C ++ ha più costrutti di C che non sono appropriati per piccoli sistemi (come eccezioni, RTTI, uso frequente dell'heap).
  • C ++ ha un set più ricco di librerie (standard) rispetto a C, ma una conseguenza del punto precedente è che le librerie C ++ usano spesso funzionalità inadeguate per piccoli sistemi e quindi non utilizzabili su piccoli sistemi.
  • C ++ ha più costrutti di C che ti permettono di spararti nel piede.
  • C ++ ha più costrutti di C che ti consentono di impedirti di spararti nel piede (sì, questo e quello precedente sono entrambi veri).
  • Il C ++ ha una serie più ricca di meccanismi di astrazione, quindi consente modi di programmazione migliori, specialmente per le biblioteche.
  • Le funzionalità del linguaggio C ++ (ad esempio costruttori / distruttori, funzioni di conversione) rendono più difficile vedere attraverso il codice per vedere la macchina generata e quindi il costo nello spazio e nel tempo di un costrutto del linguaggio.
  • Il costrutto del linguaggio C ++ rende meno necessario essere consapevoli di come vengono tradotti esattamente nel codice macchina perché fanno "la cosa giusta" in un modo più astratto.
  • Lo standard del linguaggio C ++ si sta evolvendo rapidamente ed è adottato rapidamente dai grandi compilatori (gcc, clang, microsoft). La C si sta evolvendo in modo piuttosto superficiale, e l'adozione di alcune nuove funzionalità (array di varianti) è spaventosa ed è stata persino ripristinata in uno standard successivo. Questo punto in particolare è interessante in quanto diverse persone lo usano per supportare posizioni opposte.
  • C ++ è senza dubbio uno strumento più nitido di C. Ti fidi dei tuoi programmatori (o di te stesso) di usare un tale strumento per realizzare una bella scultura, o temi di far loro del male e preferiresti accontentarti di un prodotto meno bello ma a basso rischio ? (Ricordo che il mio insegnante di scultura una volta mi disse che gli strumenti contundenti in alcune situazioni possono essere più pericolosi di quelli affilati.)

Il mio blog contiene alcuni scritti sull'uso del C ++ su piccoli sistemi (= microcontroller).


15

Nella mia esperienza, il C ++ di solito non è adatto ai piccoli sistemi embedded. Con questo intendo microcontrollori e dispositivi senza OS.

Molte tecniche OOP C ++ si basano sull'allocazione dinamica della memoria. Questo spesso manca nei piccoli sistemi.

STL e Boost dimostrano davvero la potenza del C ++, entrambi hanno un ingombro enorme.

Il C ++ incoraggia il programmatore a sottrarre la macchina, dove nei sistemi vincolati deve essere abbracciata.

L'anno scorso ho trasferito un prodotto desktop remoto commerciale sui telefoni cellulari. È stato scritto in C ++ ed è stato eseguito su Windows, Linux e OSX. Ma si basava fortemente su eccezioni STL, memoria dinamica ed C ++. Per farlo funzionare su ambienti WinCE, Symbian e senza OS, la riscrittura in C era l'opzione più salutare.


Concordo con riferimento ai piccoli sistemi, ma penso che abbiamo diverse definizioni di piccoli sistemi. Quando hai 1kB di ROM e un codice C ben scritto occupa tutti tranne 1 byte di ROM, questo è un piccolo sistema.
Kortuk,

6
Non sto sostenendo che C non possa avere un footprint più piccolo, ma avresti potuto usare ancora C ++ e ottenere un risultato molto simile per la progettazione di ciò che è stato appena discusso. Penso che il problema sia che la maggior parte dei programmatori OOP sono abituati a sistemi con memoria dinamica e che utilizzano costrutti molto inefficienti, risultando in codice completamente inutile per sistemi a bassa potenza.
Kortuk,

4
quindi, come dici, non vuoi usare C ++, vuoi usare qualcosa tra C e C ++ (chiamiamolo semplicemente C +?). In tal caso, sono d'accordo, c'è molta merda in C ++ che le persone usano solo perché è disponibile, non perché è ottimale. Quasi ogni linguaggio è in grado di produrre un codice buono e veloce, dipende dal modo in cui viene utilizzato. La maggior parte delle guerre sante sulle lingue non sono il risultato delle capacità linguistiche, ma un argomento su quanto sia facile per un idiota fare cose idiote, il che è davvero un argomento idiota: p
Marco

2
"La maggior parte delle guerre sante sulle lingue non sono il risultato delle capacità linguistiche, ma un argomento su quanto sia facile per un idiota fare cose idiote, che in realtà è un argomento idiota." È stata una frase molto piacevole. Ho bisogno del tuo cognome per poterlo citare.
Kortuk,

1
Tuttavia, in realtà non uso la memoria dinamica in C. Non c'è nessun posto dove devo averlo. A lungo termine ho letto che può essere molto molto segmentato e iniziare a causare problemi. Devo disporre di casi ben progettati per esaurire la memoria e devo essere in grado di monitorare esattamente quanto resta.
Kortuk,

11

Spero di aggiungere più luce che calore a questa discussione sul C ++ su sistemi bare metal e risorse limitate.

Problemi in C ++:

  • Le eccezioni sono in particolare un problema di RAM in quanto il "buffer di emergenza" richiesto (dove ad esempio l'eccezione di memoria insufficiente) può essere maggiore della RAM disponibile ed è sicuramente uno spreco di microcontrollori. Per maggiori informazioni vedi n4049 e n4234 . Dovrebbero essere disattivati ​​(che è attualmente un comportamento non specificato, quindi assicurati di non lanciarlo mai). SG14 sta attualmente lavorando su modi migliori per farlo.

  • RTTI probabilmente non vale mai il sovraccarico, dovrebbe essere spento

  • Build di debug di grandi dimensioni, anche se questo non è un problema nello sviluppo desktop classico se il debug non si adatta al chip può essere un problema. Il problema deriva dal codice basato sul modello o da chiamate di funzioni extra aggiunte per maggiore chiarezza. Queste funzioni aggiuntive verranno rimosse dall'ottimizzatore e la maggiore chiarezza o flessibilità può essere un grande vantaggio, tuttavia nelle build di debug questo può essere un problema.

  • Allocazione dell'heap. Sebbene l'STL consenta l'uso di allocatori personalizzati, questo può essere complesso per la maggior parte dei programmatori. L'allocazione dell'heap non è deterministica (cioè non in tempo reale difficile) e la frammentazione può portare a situazioni impreviste di memoria insufficiente nonostante abbia lavorato nei test. La conservazione dei libri richiesta dall'heap per tenere traccia dello spazio libero e delle dimensioni variabili può essere un problema con piccoli oggetti. Di solito è meglio usare l'allocazione di pool (sia in C che in C ++) ma questo può essere anormale per i programmatori C ++ abituati a usare solo l'heap.

  • Il polimorfismo di runtime e altre chiamate indirette sono di solito un grande successo di prestazioni, il problema è di solito più perché l'ottimizzatore non può vedere attraverso di loro più del vero recupero e saltare all'indirizzo. Le chiamate indirette devono essere evitate per questo motivo in C e C ++ dove, come in C ++, sono più radicate nella cultura (e sono piuttosto utili in altri domini).

  • l'interfacciamento implicito con clib può essere problematico. Potrebbe essere controintuitivo che i problemi di clib siano nella categoria C ++, ma il problema deriva dalla condivisione implicita di risorse in ambienti concorrenti (la condivisione è più esplicita in C). L'uso dell'implementazione comune di newLib spesso trascina molto gonfiore, che di solito non è necessario negli Stati Uniti, d'altra parte newLibNanno non è rientrante, quindi l'accesso ad esso deve essere serializzato (semplificando eccessivamente qui). Questo è un problema anche per C ma l'accesso è più esplicito. Come regola generale non si dovrebbe essenzialmente usare nulla dallo spazio dei nomi std nel contesto ISR a meno che non si sia sicuri che in qualche modo non acceda allo stato in clib (errorno o l'heap per esempio). È anche importante se stai usando i thread (preferisco RTC) per sovrascrivere nuovi ed eliminare per sincronizzare l'accesso a malloc e gratuito.

In conclusione il C ++ ha alcuni problemi ma sono essenzialmente tutti riparabili o evitabili.

Ora per C, qui il problema è di ordine superiore. In C non ho la capacità sintattica di astrarre le cose in modo da poter eseguire l'ottimizzazione o controllare gli invarianti al momento della compilazione. Pertanto non posso incapsulare correttamente le cose in modo che l'utente non abbia bisogno di sapere come funzionano per usarle e la maggior parte del mio rilevamento degli errori viene eseguito in fase di esecuzione (che non è solo troppo tardi ma aggiunge anche costi). Essenzialmente l'unico modo per essere generico in C è attraverso i dati, passo una stringa di formato a printf o scanf che viene valutata ad esempio durante l'esecuzione. È quindi abbastanza difficile per il compilatore dimostrare che non sto usando alcune delle opzioni che sono teoricamente possibili quando vengono passati i dati giusti, il che significa potenziale generazione di codice morto e perdita di potenziale di ottimizzazione.

So che potrei scatenare una tempesta qui, ma la mia esperienza sui microcontrollori a 32 bit è che in un confronto mele-mele di C e C ++ entrambi scritti da esperti (come in C ++ potenzialmente altamente templato) C ++ è il linguaggio molto più efficiente non appena qualsiasi cosa deve essere del tutto generica (come in qualsiasi biblioteca) e sono essenzialmente equivalenti in casi non generici. È anche più facile per un principiante sfruttare l'esperienza di un esperto implementatore di librerie in C ++.

Allo stesso tempo, in realtà ci sono davvero poche funzioni a cui non posso trasmettere dati errati, non appena l'input non è un int ma un somethingper il quale mi capita di usare un int come metodo di rappresentazione, allora c'è un potenziale ottenerlo sbagliato (passa un valore non valido o un 'altroThing' piuttosto che un 'qualcosa'). In C il mio unico metodo per verificare se l'utente ha sbagliato è in fase di esecuzione. In C ++ ho la possibilità di eseguire alcuni controlli, non tutti i controlli ma alcuni controlli in fase di compilazione che sono gratuiti.

Alla fine della giornata, una squadra C è spesso potente quanto il suo programmatore più debole e il vantaggio del codice risultante ha un multiplayer di 1 o una penalità per le prestazioni. Ciò che intendo con questo è che si tratta di alte prestazioni per uno e un solo lavoro unico in un ambiente unico di decisioni di progettazione uniche o è abbastanza generico da essere utilizzato in più ambienti (altri microcontrollori, altre strategie di gestione della memoria, altre latenze vs. throughput trade off ecc. ecc.) ma ha un costo inerente alla performance.

In C ++ le cose possono essere incapsulate dagli esperti e utilizzate in molti ambienti in cui la generazione di codici temporali si adatta all'attività specifica e il controllo statico impedisce agli utenti di fare cose stupide a costo zero. Qui abbiamo molto meno compromessi tra l'essere generici e l'essere veloci e quindi, in definitiva, dal punto di vista dei costi rispetto ai benefici, il linguaggio più performante, più sicuro e più produttivo.

È una critica valida che vi sia ancora una grande carenza di buone librerie C ++ per embedded, questo può portare a decisioni pragmatiche di usare principalmente C su un compilatore C ++. Le decisioni di usare solo C in un progetto sono essenzialmente o ideologicamente guidate, per necessità di supporto legacy o per l'ammissione che il team non è abbastanza disciplinato da astenersi da una serie molto selezionata di cose stupide che si possono fare in C ++ ma non in C e allo stesso tempo abbastanza disciplinato da non fare nessuna delle più stupide serie di cose stupide che non si possono proteggere in C ma in C ++.


Bella aggiunta alla mia risposta :) Chi sarebbe questo misterioso amante del C ++? Il suo profilo afferma "Apparentemente, questo utente preferisce mantenere un'aria di mistero su di loro". (pessimo inglese, BTW) MA AHA la location è "Bochum, Germania" ..... Ci vediamo alla conferenza!
Wouter van Ooijen,

Ah sì, ho aggiornato il mio profilo;) bello sapere che verrai su emBO ++, sarà una buona folla
venerdì

10

Il mio background: appena uscito dalla scuola con i vecchi programmatori Bell Labs; ha lavorato per 3 anni, 2 sul progetto di ricerca universitaria; acquisizione dati / controllo di processo in VB.NET. Trascorso 1,5 anni a lavorare su un'applicazione di database aziendale in VB6. Attualmente al lavoro su progetto per PC embedded con 2 GB di memoria, 512 MB di RAM, CPU x86 500MHz; diverse app in esecuzione contemporaneamente scritte in C ++ con un meccanismo IPC in mezzo. Sì, sono giovane.

La mia opinione: penso che C ++ possa funzionare in modo efficace dato l'ambiente che ho scritto sopra . Certo, le prestazioni in tempo reale non sono un requisito per l'app in cui mi trovo, e in alcune applicazioni integrate, questo può essere un problema. Ma ecco le cose che ho imparato:

  • C ++ è fondamentalmente diverso da C (cioè, non esiste C / C ++). Mentre tutto ciò che è C valido è C ++ valido, C ++ è un linguaggio molto diverso e bisogna imparare a programmare in C ++, non in C, per usarlo efficacemente in qualsiasi situazione. In C ++, è necessario programmare orientato agli oggetti, non proceduralmente e non un ibrido dei due (grandi classi con molte funzioni). In generale, dovresti concentrarti sulla creazione di piccole classi con poche funzioni e comporre tutte le piccole classi insieme in una soluzione più ampia. Uno dei miei colleghi mi ha spiegato che ero solito programmare in modo procedurale in oggetti, il che è un gran casino ed è difficile da mantenere. Quando ho iniziato ad applicare più tecniche orientate agli oggetti, ho scoperto che la manutenibilità / leggibilità del mio codice è aumentata.

  • C ++ offre funzionalità aggiuntive sotto forma di sviluppo orientato agli oggetti che possono fornire un modo per semplificare il codice per facilitare la lettura / manutenzione . Onestamente, non penso che ci sia molto in termini di miglioramento delle prestazioni / efficienza dello spazio nel fare OOP. Ma penso che OOP sia una tecnica che può aiutare a dividere un problema complesso in molti piccoli pezzi. E questo è utile per le persone che lavorano sul codice, un elemento di questo processo che non dovrebbe essere ignorato.

  • Molti argomenti contro C ++ hanno principalmente a che fare con l'allocazione dinamica della memoria. Anche C ha questo stesso problema. È possibile scrivere un'applicazione orientata agli oggetti senza utilizzare la memoria dinamica, anche se uno dei vantaggi dell'utilizzo degli oggetti è che è possibile allocare queste cose in modo dinamico in modo semplice. Proprio come in C, devi stare attento a come gestire i dati per ridurre le perdite di memoria, ma la tecnica RAII rende questo più semplice in C ++ (fai distruggere automaticamente la memoria dinamica incapsulandola in oggetti). In alcune applicazioni, dove ogni posizione di memoria conta, potrebbe essere troppo selvaggio e lanoso da gestire.

MODIFICARE:

  • WRT la domanda "Arduino C ++" : direi che C ++ senza gestione dinamica della memoria può ancora essere utile. È possibile organizzare il codice in oggetti e quindi posizionare tali oggetti in varie posizioni all'interno dell'applicazione, configurare le interfacce di callback, ecc. Ora che ho sviluppato in C ++, posso vedere molti modi in cui un'applicazione con tutti i dati allocati sul stack può ancora essere utile con gli oggetti. Devo ammetterlo però: non ho mai scritto un'app integrata come quella per Arduino, quindi non ho prove a sostegno della mia richiesta. Ho alcune opportunità di fare un po 'di sviluppo di Arduino in un prossimo progetto - spero di poter testare il mio reclamo lì.

2
Vorrei commentare il tuo secondo punto, tu dici che aiuta a spezzare un problema complesso in molti piccoli pezzi e quella caratteristica dovrebbe essere ignorata. Questa è la ragione esatta per cui sono così pro-C ++. Un'ampia ricerca sulla programmazione mostra che una crescita lineare delle dimensioni del programma produce una crescita esponenziale nei tempi di sviluppo. questo segue il modo opposto, se puoi dividere correttamente un programma allora puoi dare un decadimento esponenziale nei tempi di sviluppo. Questa è di gran lunga la cosa più importante.
Kortuk,

anche sul secondo punto: il semplice utilizzo di una metodologia di progettazione OOP non produce codice più compartimentato. Avere una buona progettazione di base fa, come si esprime quel design è lasciato allo sviluppatore. OOP non definisce che separi correttamente il tuo codice, fornisce un'altra opzione e, soprattutto, l'aspetto che hai fatto, ma certamente non impone un buon design, dipende dallo sviluppatore.
Segna il

Questo è sempre vero. Non ho mai sentito parlare di un linguaggio che imponga un buon design. Penso che sottintendiamo principalmente che è il lavoro degli sviluppatori e che il C ++ lo rende facile da usare e implementare in modo organizzato.
Kortuk,

@Mark - Sono d'accordo. È stato un processo di apprendimento per me.
J. Polfer,

7

Sì, il problema con C ++ è la maggiore impronta del codice.

In alcuni sistemi stai contando i byte, e in quel caso dovrai accettare un costo di esecuzione che vicino ai limiti dei tuoi sistemi comporta un aumento del costo di sviluppo di C.

Ma, anche in C, per un sistema ben progettato è necessario mantenere tutto incapsulato. I sistemi ben progettati sono difficili e il C ++ offre ai programmatori un posto per un metodo di sviluppo molto strutturato e controllato. C'è un costo per imparare OOP, e se vuoi passare ad esso lo accetti molto, e in molti casi la gestione preferirebbe continuare con C e non pagarne il costo, poiché è difficile misurare i risultati di uno switch che aumenta la produttività. Puoi vedere un articolo del guru dei sistemi embedded Jack Ganssle qui .

La gestione dinamica della memoria è il diavolo. Non proprio, il diavolo è auto-route, la gestione dinamica della memoria funziona benissimo su un PC, ma puoi aspettarti di riavviare un PC almeno ogni poche settimane. Scoprirai che mentre un sistema integrato continua a funzionare per 5 anni, la gestione dinamica della memoria può davvero rovinarsi e iniziare a fallire. Ganssle discute cose come stack e heap nel suo articolo.

Ci sono alcune cose in C ++ che sono più inclini a causare problemi e usano molte risorse, rimuovere la gestione dinamica della memoria e i modelli sono grandi passi per mantenere l'impronta di C ++ più vicina all'impronta di C. Questo è ancora C ++, non è necessario dinamico gestione della memoria o modelli per scrivere un buon C ++. Non mi sono reso conto che hanno rimosso le eccezioni, considero le eccezioni una parte importante del mio codice che rimuovo nella versione, ma le uso fino a quel momento. Nel test sul campo posso fare in modo che le eccezioni generino messaggi per informarmi di un'eccezione rilevata.


1
Sono stato d'accordo sul fatto che l'impronta del codice sia un problema, ma recentemente sembra che la dimensione del flash abbia un'influenza molto litte sul prezzo di un microcontrollore, molto meno della dimensione della RAM o del numero di pin IO.
Wouter van Ooijen,

L'argomento sulla memoria dinamica è l'IMO più importante. Ho visto sistemi industriali che potevano funzionare senza sosta per settimane, ma il livello diagnostico (scritto in C ++) avrebbe limitato il tempo di riavvio a circa 12 ore.
Dmitry Grigoryev,

6

Ho pensato che questo rant anti-C ++ di Linus Torvalds fosse interessante.

Una delle caratteristiche peggiori in assoluto di C ++ è come rende molte cose così dipendenti dal contesto, il che significa semplicemente che quando si guarda il codice, una vista locale raramente dà abbastanza contesto per sapere cosa sta succedendo.

Non sta parlando del mondo dei sistemi embedded, ma dello sviluppo del kernel Linux. Per me la rilevanza deriva da questo: il C ++ richiede la comprensione di un contesto più ampio e posso imparare a usare un insieme di modelli di oggetti, non mi fido di me stesso di ricordarli quando dovrò aggiornare il codice tra qualche mese.

(D'altra parte, sto attualmente lavorando su un dispositivo incorporato usando Python (non C ++, ma usando lo stesso paradigma OOP) che avrà esattamente quel problema. A mio parere, è un sistema incorporato abbastanza potente da essere chiamato PC 10 anni fa.)


5
Potremmo essere diversi, ma trovo che solo aprendo un progetto non posso dire cosa sta succedendo immediatamente, ma se so qualcosa di quello che sta facendo e ho qualcosa di ben codificato in C e qualcosa di ben codificato in C ++, il C ++ sembra sempre di più chiaro. È ancora necessario implementare l'incapsulamento per un buon sviluppo in C, cosa che C ++ rende molto facile da fare. L'uso corretto delle classi può chiarire dove sono le interfacce e possono essere completamente gestite attraverso un oggetto.
Kortuk,

Totalmente concordato su incapsulamento e classi. Sovraccarico ed ereditarietà dell'operatore, non tanto.
spazzato il

1
Haha, sì, il sovraccarico dell'operatore può essere utilizzato per offuscare la funzione del codice. Se qualcuno sta sovraccaricando l'operatore, deve essere per ragioni chiare o per nulla fatto. L'ereditarietà dovrebbe essere utilizzata solo in casi specifici in cui si sta effettivamente facendo qualcosa che è proprio come il genitore con alcune aggiunte. Penso che non userei tutte le funzioni in OOP. Ho usato entrambi, ma in un sistema embedded non riesco a pensare a un caso in cui lo farei. Proprio come penso che un compilatore con un limite di 80 caratteri per i nomi delle variabili debba essere immediatamente eliminato.
Kortuk,

2
Ho appena vomitato un po 'in bocca al pensiero di programmare un MCU in Python ...
vicatcu

Non sei l'unico, ma se funziona bene ed è efficiente, posso perdonare.
Kortuk,

6

Penso che altre risposte abbiano fatto un buon caso per i pro, i contro e i fattori decisionali, quindi vorrei solo riassumere e aggiungere alcuni commenti.

Per microcontrollori piccoli (8 bit), nessun modo. Stai solo chiedendo di farti del male, non c'è guadagno e rinuncerai a troppe risorse.

Per i microcontrollori di fascia alta (es. 32-bit, 10s o 100s di MB per RAM e archiviazione) che hanno un sistema operativo decente, è perfettamente OK e, oserei dire, anche consigliato.

Quindi la domanda è: dov'è il confine?

Non lo so per certo, ma una volta ho sviluppato un sistema per un uC a 16 bit con 1 MB di RAM e 1 MB di spazio di archiviazione in C ++, solo per rimpiangerlo in seguito. Sì, ha funzionato, ma il lavoro extra che ho avuto non ne valeva la pena. Ho dovuto adattarlo, assicurarmi che cose come le eccezioni non producessero perdite (il supporto OS + RTL era piuttosto difettoso e inaffidabile). Inoltre, un'app OO in genere esegue molte piccole allocazioni e l'overeap dell'heap per quelle era un altro incubo.

Data questa esperienza, presumo per i progetti futuri che sceglierò C ++ solo nei sistemi almeno a 16 bit e con almeno 16 MB per RAM e archiviazione. Questo è un limite arbitrario, e probabilmente varierà in base a cose come il tipo di applicazione, gli stili di codifica e gli idiomi, ecc. Ma date le avvertenze, consiglierei un approccio simile.


2
Non sono d'accordo qui, non è un punto improvviso in cui il C ++ diventa accettabile a causa delle risorse di sistema, una buona pratica di progettazione può mantenere l'impronta di C ++ dove si trova l'impronta di C. Ciò si traduce in codice con design OOP che occupano lo stesso spazio. Una C scritta male può essere altrettanto cattiva.
Kortuk,

1
Bene, dipende da quanto è grande la tua applicazione e da quanto usi alcune funzionalità che richiedono più spazio (come modelli ed eccezioni). Ma personalmente preferirei usare C piuttosto che limitarmi a un C ++ contenuto. Ma anche allora avrai il sovraccarico di un RTL più grande, thunk di metodo virtuale, invocazione di catene di costruttori / distruttori ... questi effetti possono essere mitigati con un'attenta codifica, ma poi stai perdendo il motivo principale dell'uso del C ++, dell'astrazione e prospettiva di alto livello.
fceconel,

4

Ci sono alcune funzionalità di C ++ che sono utili nei sistemi embedded. Ce ne sono altri, come le eccezioni, che possono essere costosi e i cui costi potrebbero non essere sempre evidenti.

Se avessi i miei druther, ci sarebbe una lingua popolare che combinava il meglio dei due mondi e includeva alcune caratteristiche che mancano in entrambe le lingue; alcuni venditori includono alcune di queste funzionalità, ma non ci sono standard. Alcune cose che vorrei vedere:

  1. Gestione delle eccezioni un po 'più simile a Java, in cui le funzioni che possono generare eccezioni devono essere dichiarate come tali. Mentre un requisito per tali dichiarazioni può essere in qualche modo fastidioso dal punto di vista della programmazione, migliorerebbe la chiarezza del codice nei casi in cui una funzione può restituire un numero intero arbitrario se riesce, ma può anche fallire. Molte piattaforme potrebbero gestire questo codice in modo economico, ad es. Avendo il valore di ritorno in un registro e l'indicazione di successo / fallimento nel flag carry.
  2. Sovraccarico di sole funzioni statiche e in linea; la mia comprensione è che gli organismi di standardizzazione di C hanno evitato il sovraccarico delle funzioni in modo da evitare la necessità di modificare il nome. Consentire sovraccarichi di funzioni statiche e in linea eviterebbe solo questo problema e darebbe il 99,9% del vantaggio di sovraccaricare le funzioni esterne (poiché i file .h potrebbero definire sovraccarichi in linea in termini di funzioni esterne con nomi diversi)
  3. Sovraccarichi per valori di parametri costanti risolvibili in fase di compilazione specifici o arbitrari. Alcune funzioni possono essere integrate in modo molto efficiente se passate con qualsiasi valore costante, ma se non passano una variabile molto in linea. Altre volte il codice che può essere un'ottimizzazione se un valore è costante può essere una pessimizzazione se non lo è. Per esempio:
    inline void copy_uint32s (uint32_t * dest, const uint32_t * src, __is_const int n)
    {
      if (n <= 0) restituisce;
      else if (n == 1) {dest [0] = src [0];}
      else if (n == 2) {dest [0] = src [0]; dest [1] = src [1];}
      else if (n == 3) {dest [0] = src [0]; dest [1] = src [1]; dest [2] = src [2];}
      else if (n == 4) {dest [0] = src [0]; dest [1] = src [1]; dest [2] = src [2]; dest [3] = src [3];}
      else memcpy ((void *) dest, (const void *) src, n * sizeof (* src));
    }
    
    Se 'n' può essere valutato in fase di compilazione, il codice sopra sarà più efficiente di una chiamata a memcpy, ma se 'n' non può essere valutato in fase di compilazione il codice generato sarebbe molto più grande e più lento del codice che semplicemente chiamato memcpy.

So che il padre di C ++ non è troppo appassionato di una versione solo integrata di C ++, ma penso che potrebbe offrire alcuni miglioramenti considerevoli rispetto al solo utilizzo di C.

Qualcuno sa se qualcosa di simile a quanto sopra è stato preso in considerazione per qualsiasi tipo di standard?



@Joby Taffey: immagino di aver modificato il mio post per omettere di menzionare che il creatore di C ++ non era appassionato di un sottoinsieme incorporato; Sono consapevole del fatto che ci sono stati degli sforzi, ma a quanto ho capito non erano andati così lontano. Penso che ci sarebbe un uso definito per un linguaggio standardizzato che sarebbe suscettibile ai processori a 8 bit e funzionalità come quelle sopra descritte sembrerebbero utili su qualsiasi piattaforma. Hai mai sentito parlare di lingue che offrono cose come la # 3 sopra? Sembrerebbe molto utile, ma non ho mai visto nessun linguaggio offrirlo.
supercat,

"Il padre del C ++" non ha esperienza nella programmazione di sistemi embedded, quindi perché qualcuno dovrebbe preoccuparsi della sua opinione?
Lundin,

@Lundin: Il fatto che alcune persone influenti sembrano preoccuparsi delle sue opinioni su varie questioni sembrerebbe essere motivo, in sé e per sé, per altre persone di farlo. Sto pensando che da quando ho scritto quanto sopra la potenza crescente dei template potrebbe aver aggiunto nuove possibilità per avere sovraccarichi basati su quali costanti possono essere risolte in fase di compilazione, anche se molto meno chiaramente rispetto a se una cosa del genere fosse supportata come compilazione- funzione tempo (da quello che ho capito, si dovrebbe specificare un modello che dovrebbe provare varie cose in ordine e andare con il primo che non fallisce ...
supercat

... ma ciò richiederebbe al compilatore di sprecare un bel po 'di sforzo nella compilazione di potenziali sostituzioni che finirebbero per essere scartate. Essere in grado di dire in modo più chiaro "Se questa è una costante, fare questo, altrimenti farlo" senza "false partenze" sembrerebbe un approccio più pulito.
supercat

3

C ++ è più di un linguaggio di programmazione:

a) È un "migliore" C b) È un linguaggio orientato agli oggetti c) È un linguaggio che ci consente di scrivere programmi generici

Sebbene tutte queste funzioni possano essere utilizzate separatamente, i migliori risultati si ottengono quando si utilizzano contemporaneamente tre di essi. Tuttavia, se si sceglie di sceglierne solo uno, la qualità del software incorporato aumenterà.

a) È un "migliore" C

C ++ è un linguaggio tipizzato forte; più forte di C. I tuoi programmi beneficeranno di questa funzione.

Alcune persone hanno paura dei puntatori. C ++ include i riferimenti. Funzioni sovraccaricate.

E vale la pena dirlo: nessuna di queste funzionalità è stata sostenuta da programmi più grandi o più lenti.

b) È un linguaggio orientato agli oggetti

Qualcuno ha detto in questo post che astrarre la macchina nei microcontrollori non è una buona idea. Sbagliato! Tutti noi, gli ingegneri embedded, abbiamo sempre estratto la macchina, solo con altre sintassi quella di C ++. Il problema che vedo con questo argomento è che alcuni programmatori non sono abituati a pensare negli oggetti, in questo modo non vedono i benefici di OOP.

Ogni volta che sei pronto per utilizzare la periferica di un microcontrollore, è probabile che la periferica sia stata astratta per noi (da te o da una terza parte) sotto forma di driver del dispositivo. Come ho detto prima, quel driver usa la sintassi C, come mostra il prossimo esempio (preso direttamente da un esempio NXP LPC1114):

/ * Impostazione del timer per la corrispondenza e l'interruzione a TICKRATE_HZ * /

Chip_TIMER_Reset (LPC_TIMER32_0);

Chip_TIMER_MatchEnableInt (LPC_TIMER32_0, 1);

Chip_TIMER_SetMatch (LPC_TIMER32_0, 1, (timerFreq / TICKRATE_HZ2));

Chip_TIMER_ResetOnMatchEnable (LPC_TIMER32_0, 1);

Chip_TIMER_Enable (LPC_TIMER32_0);

Vedi l'astrazione? Quindi, quando si utilizza C ++ per lo stesso scopo, l'astrazione viene portata al livello successivo attraverso il meccanismo di astrazione e incapsulamento del C ++, a costo zero!

c) È una lingua che ci consente di scrivere programmi generici

I programmi generici sono realizzati tramite modelli e anche i modelli non hanno costi per i nostri programmi.

Inoltre, il polimorfismo statico si ottiene con i modelli.

Metodi virtuali, RTTI ed eccezioni.

Vi è un compromesso quando si utilizzano metodi virtuali: software migliore contro un certo rigore nelle prestazioni. Tuttavia, ricorda che è probabile che il bind dinamico venga implementato usando una tabella virtuale (un array di puntatori a funzione). Ho fatto lo stesso in C molte volte (anche su base regolare), quindi non vedo gli svantaggi nell'uso dei metodi virtuali. Inoltre, i metodi virtuali in C ++ sono più eleganti.

Infine, un consiglio su RTTI ed eccezioni: NON UTILIZZARLI nei sistemi embedded. Evitali a tutti i costi !!


2

Il mio background, incorporato (mcu, pc, unix, altro), in tempo reale. Sicurezza critica. Ho presentato un precedente datore di lavoro a STL. Non lo faccio più.

Alcuni contenuti di fiamma

Il C ++ è adatto per i sistemi embedded?

Meh. Il C ++ è un dolore da scrivere e un dolore da mantenere. C + va bene (non usare alcune funzionalità)

C ++ nei microcontrollori? RTOS? Tostapane? PC incorporati?

Ancora una volta dico Meh. C + non è poi così male, ma ADA è meno doloroso (e questo sta davvero dicendo qualcosa). Se sei fortunato come me, puoi fare Java incorporato. L'accesso alla matrice verificato e nessuna aritmetica del puntatore rendono il codice molto affidabile. I Garbage Collector in Java incorporato non hanno la massima priorità e vi è la memoria con ambito e il riutilizzo degli oggetti, quindi un codice ben progettato può essere eseguito per sempre senza un GC.

OOP è utile sui microcontrollori?

Certo che lo è. L'UART è un oggetto ..... Il DMAC è un oggetto ...

Le macchine a stati dell'oggetto sono molto semplici.

Il C ++ rimuove il programmatore troppo lontano dall'hardware per essere efficiente?

A meno che non sia un PDP-11, C non è la tua CPU. Il C ++ era originariamente un preprocessore in cima al C, quindi Bjarne Stroustrup avrebbe smesso di essere deriso per avere simulazioni lente di Simula mentre era in AT&T. C ++ non è la tua CPU.

Vai a ottenere un MCU che esegue bytecode java. Programma in Java. Ridi dei ragazzi C.

Il C ++ di Arduino (senza gestione dinamica della memoria, modelli, eccezioni) dovrebbe essere considerato "C ++ reale"?

No. proprio come tutti i compilatori C bastardizzati là fuori per MCU.

Forth, Embedded Java o Embedded ADA sono standardizzati (ish); tutto il resto è dolore.


2
È così facile trovare microcontrollori che supportano Java? Penso che questo limiterebbe considerevolmente le scelte. E quali sono le tue esperienze sulla penalità di prestazione (dato che negli Stati Uniti di solito non avresti JIT)? Che dire dell'impatto dell'imprevedibilità del GC nei sistemi in tempo reale?
fceconel,

2
Quali MCU sono là fuori che supportano Java incorporato?
J. Polfer,

www.ajile.com per i principianti.
Tim Williscroft,

+1 per Ada. C'è molto da fare in embedded, compresi gli Arduinos.
Brian Drummond,

java VM portatile per micro scritte in c è open source. dmitry.co/index.php?p=./04.Thoughts/…
Tim Williscroft,

-2

I sistemi incorporati sono progettati per svolgere alcune attività specifiche, anziché essere un computer per scopi generali per più attività. Un sistema incorporato è una combinazione di hardware e software. C è la madre di tutte le lingue moderne. È un linguaggio di basso livello ma potente e offre tutti i tipi di hardware. Quindi C / C ++ è una scelta ottimale per lo sviluppo di software per sistemi embedded, che è molto utile per ogni sistema embedded. Come sappiamo C è un linguaggio di sviluppo. Il sistema operativo UNIX è scritto in C.Poiché lo sviluppo di software di successo si basa così spesso sulla selezione della lingua migliore per un determinato progetto, è sorprendente scoprire che il linguaggio C / C ++ si è dimostrato appropriato sia per i processori a 8 che a 64 bit ; nei sistemi con byte, kilobyte e megabyte di memoria. C ha il vantaggio di indipendenza del processore, che consente ai programmatori di concentrarsi sugli algoritmi e sulle applicazioni, piuttosto che sui dettagli della particolare architettura del processore. Tuttavia, molti di questi vantaggi si applicano ugualmente ad altre lingue di alto livello. Ma C / C ++ è riuscito dove molte altre lingue hanno ampiamente fallito?


6
Non sono davvero sicuro di ciò che si aggiunge alla discussione.
Dave Tweed

-3

<Rant>

Penso che il C ++ sia in primo luogo un linguaggio schifoso. Se si desidera utilizzare OOP, scrivere programmi Java. Il C ++ non fa nulla per imporre i paradigmi OOP, poiché l'accesso diretto alla memoria è pienamente in vostro potere di (ab) uso.

Se hai un MCU, stai probabilmente parlando di meno di 100 kB di memoria flash. Volete programmare in un linguaggio la cui astrazione della memoria è: quando dichiaro una variabile o un array, ottiene memoria, punto; malloc (nota anche come "nuova" parola chiave in C ++) dovrebbe essere più o meno vietato dall'uso nel software incorporato, tranne forse in rare occasioni una chiamata durante l'avvio del programma.

Al diavolo, ci sono (frequentemente) tempi nella programmazione integrata in cui C non è abbastanza di basso livello ed è necessario fare cose come allocare variabili ai registri e scrivere assembly inline per rafforzare le routine di servizio di interruzione (ISR). Parole chiave come "volatile" diventano dannatamente importanti da capire. Trascorri molto tempo a manipolare la memoria a livello di bit , non a livello di oggetto .

Perché vorresti illuderti nel pensare che le cose siano più semplici di quanto non siano in realtà?

</ Rant>


Il mio problema qui è semplicemente, perché voglio sapere la complessità del driver che è stato scritto per controllare USART1 se è stato completamente sviluppato per gestire l'interfaccia.
Kortuk,

1
Non ho votato in negativo, ma vorrei sottolineare che il C ++ non ha bisogno di imporre OOP, fornisce solo gli strumenti per farlo. L'applicazione di buoni standard di codifica è compito dello sviluppatore. Può aiutare se la lingua lo rende più semplice, ma la lingua non lo farà mai da sola. C può essere illeggibile in alcuni casi.
Kortuk,

1
Tutte le lingue vanno bene per qualcosa. Il C ++ è veloce. OOP, se ben fatto, rende molto più facile per più sviluppatori lavorare in parallelo e codificare per l'ignoto. Penso che questo sia il motivo per cui ha tanta trazione nello sviluppo del gioco.
Toby Jaffey,

1
Si, sono d'accordo. Il motivo per cui lo vedo per il mondo embedded è a causa della grande quantità di caratteristiche e funzioni aggiunte a molti dei diversi sistemi già in atto e di nuovi sistemi in fase di sviluppo. Il progetto diventa sempre più grande. O impieghiamo più tempo per svilupparli o iniziamo ad applicare e contorcere ciò che il mondo CS ha già fatto sui PC.
Kortuk,

5
Ancora un'altra persona che non capisce correttamente il C ++. Mi stupisce sempre quanti ce ne sono.
Rocketmagnet,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.