2009-04-19 4 views
6

Sto lavorando a un progetto di gruppo per la mia università e mi sono imbattuto in un ostacolo importante nel tentativo di far funzionare il mio codice.Come si può fare C++ quando il compilatore incorporato non ha l'operatore new o il supporto STL?

Il compilatore che abbiamo per il nostro microcontrollore Atmel a 8 bit non supporta gli operatori new o delete e non supporta il C++ STL. Potrei programmarlo in C, ma devo implementare un algoritmo A * che non ho mai fatto prima. Mentre ho provato C inizialmente ho capito subito che non avevo mai fatto C pura prima. Cercare di modellare gli oggetti con le strutture e le funzioni mi rallenta perché sono così abituato alla sintassi C++ molto più pulita.

Indipendentemente da ciò, la dicitura esatta per le mie mancanze compilatori può essere trovato qui: http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_cplusplus

Per superarli e utilizzare ancora C++ ho preso in considerazione le seguenti possibilità. 1) Non allocare nulla, basta usare i modelli per generare array fissi nello stack. 2) Assegna e trova qualche trucco per chiamare il costruttore per gli oggetti dopo aver assegnato loro lo spazio. Il posizionamento nuovo non è un'opzione in quanto nuovo non è un operatore. 3) Basta usare C e succhiarlo, è un microcontrollore, perché mi sto immaginando? 4) Trova un compilatore migliore che probabilmente costa $.

La seconda opzione è la più difficile, ma sarebbe la più grande ricompensa in termini di come posso scrivere questo codice. Tuttavia, immagino che il debugging potrebbe essere un enorme problema se mi sbaglio. Sto pensando di creare oggetti sullo stack, copiare i loro bit nello spazio allocato e quindi azzerare i bit nell'oggetto in modo che non chiami il suo distruttore. Per farlo, avrei accesso ai bit direttamente con un puntatore char senza segno e con l'operatore sizeof per ottenere il conteggio dei byte.

Sembra terribile e non so se potrebbe funzionare in modo affidabile, ma ci sto pensando. So che i vtables possono essere un problema, ma non intendo avere vtables dato che è solo un microcontrollore a 8 bit.

+3

Se ricordo bene gli Atomici a 8 bit non hanno abbastanza ram per pensare anche alla memoria dinamica a'la malloc/new. – lothar

+0

Se riesco a sovraccaricare questi operatori, posso controllare da dove proviene la memoria. Può essere nell'heap o nello stack. –

+0

Ho scoperto che avevo solo 1KB con cui giocare, il che essenzialmente escludeva A * come un percorso percorribile per il mio robot. Ora sto usando un DFS semplice fino a quando non ne ottengo uno con più RAM. –

risposta

10

Solo per la registrazione, l'azzeramento dei bit in un oggetto non influisce sul fatto che il distruttore venga chiamato (a meno che il compilatore non abbia una peculiarità che abilita questo comportamento). Basta scrivere alcune dichiarazioni di registrazione nel distruttore per verificarlo.

Strutturare il programma per non allocare nulla è probabilmente il modo in cui è stato progettato il sistema. Non ho mai lavorato con i sistemi embedded, tuttavia ho letto alcuni negozi embedded esperti che scoraggiano l'uso della memoria dinamica perché l'ambiente di runtime ne ha scarse quantità.


Tuttavia, se è necessario, è ancora possibile utilizzare il posizionamento nuovo. Se non si dispone l'intestazione <new>, qui ci sono le linee interessate direttamente da esso sulla mia versione di GCC:

// Default placement versions of operator new. 
inline void* operator new(std::size_t, void* __p) throw() { return __p; } 
inline void* operator new[](std::size_t, void* __p) throw() { return __p; } 

// Default placement versions of operator delete. 
inline void operator delete (void*, void*) throw() { } 
inline void operator delete[](void*, void*) throw() { } 

Stick che da qualche parte in un file di intestazione incluso da tutti i file di origine che utilizza il posizionamento nuova/delete.

file di esempio che mette alla prova questo:

#include <cstdio> 
#include <new> 

int 
main(int argc, char** argv) 
{ 
    typedef char const* cstr; 
    char foobar[16]; 
    cstr* str = new (&foobar) cstr(argc > 1 ? argv[1] : "Hello, world!"); 
    std::puts(*str); 
    str->~cstr(); 
} 

Sulla mia versione di GCC, questo non usa libstdc++ affatto (se -fno-exceptions è utilizzato).


Ora, se si desidera combinare che con malloc (se la piattaforma offre questo), allora si può fare questo:

#include <cstdio> 
#include <cstdlib> 

inline void* operator new (std::size_t n) {return std::malloc(n);} 
inline void* operator new[](std::size_t n) {return std::malloc(n);} 
inline void operator delete (void* p) {std::free(p);} 
inline void operator delete[](void* p) {std::free(p);} 

int 
main(int argc, char** argv) 
{ 
    typedef char const* cstr; 
    cstr* str = new cstr(argc > 1 ? argv[1] : "Hello, world!"); 
    std::puts(*str); 
    delete str; 
} 

Questo consente di utilizzare lo standard new/delete che si conosci bene, senza richiedere l'uso di libstdc++.

Buona fortuna!

+0

Wow, sembra che funzioni. Non ho potuto utilizzare le intestazioni fornite ma quelle funzioni dell'operatore sembrano funzionare. Da quanto ho letto su quegli operatori new [] e delete [] è necessario conoscere la dimensione dell'array per chiamare tutti i distruttori necessari, ma nel mio test sono stati chiamati tutti i distruttori necessari. –

0

Perché non scrivere prima sul computer desktop, prendendo in considerazione le limitazioni del compilatore, eseguirne il debug, assicurarsi che funzioni perfettamente e solo dopo spostarsi nell'ambiente incorporato?

+0

Non penso che sia una buona idea perché ci sono così tante limitazioni e differenze sulle piattaforme embedded (poche RAM, 8 bit invece di 32 bit), quindi non lo farai mai funzionare in questo modo. – mmmmmmmm

23

Non combattere i tuoi strumenti. Se l'unico compilatore che hai per il tuo sistema embedded è un compilatore C, impara C - non è difficile. Cercando di produrre una versione bastardata delle due lingue solo per risolvere un problema di programmazione abbastanza semplice, solo finirà in lacrime.

Per guardarlo in un altro modo, se la tua piattaforma integrata non supportava nemmeno un compilatore C, ma solo un assemblatore, il tuo primo impulso sarebbe di sederti e scrivere un compilatore C++ in assembler? Spero di no, spero che preferiresti sederti e imparare a usare l'assemblatore per completare il tuo compito - scrivere un compilatore C++ (o anche un compilatore C) sarebbe un uso del tutto inappropriato del tuo tempo, e quasi certamente causerebbe un fallimento .

+0

Anche se sono d'accordo sul fatto che combattere gli strumenti è sciocco, gli investimenti nello sviluppo di strumenti di solito si pagano da soli. Se, e quando ho usato l'assemblatore di scrivere una piccola DSL basata sul preprocessore, ne valeva la pena. Comunque non andrei mai a scrivere un compilatore C++ :) –

+0

Oh, sono d'accordo, e quando ho scritto assembler (molto tempo fa) ho usato le librerie di macro che avevo scritto. E ho effettivamente scritto metà di un compilatore C nell'assemblatore Z80 (non per risolvere un particolare problema) prima di tornare ai miei sensi, riscriverlo in C e incrociarlo compilato. Ancora non funziona, ma è stato molto meno lavoro :-) –

+0

E non dimenticare che i compilatori C più incorporati vengono solo con un sottoinsieme molto limitato della libreria C. Una delle parti importanti che ritengo manchino è la gestione dell'heap (malloc/free). – mmmmmmmm

4

Solo perché non ha questi strumenti non significa che non si possa trarre vantaggio dal C++. Se il progetto è abbastanza grande, l'accesso al design Object Oriented da solo potrebbe essere una motivazione sufficiente.

Se non supporta "nuovo", probabilmente è perché non ha senso fare una distinzione automatica tra un heap e lo stack. Ciò potrebbe essere dovuto alla configurazione della memoria. Potrebbe anche essere dovuto al fatto che le risorse di memoria sono così limitate che solo un'assegnazione molto attenta ha senso. Se devi assolutamente implementare il tuo nuovo 'nuovo' operatore, potresti cercare di adattare Doug Lea's malloc. Credo che abbia iniziato il suo allocatore in una circostanza simile (reimplementando il nuovo C++).

Amo il STL ma è ancora possibile fare cose utili senza di esso. A seconda dello scopo del progetto, potresti stare meglio usando solo un array.

2

Ho avuto un compilatore simile che ha implementato una versione bizzarra di Embedded-C++ standard. Avevamo operator new che chiameremmo costruttori per noi e i distruttori furono chiamati nella maggior parte dei casi. Il fornitore del compilatore/runtime è andato e implementato try e catch utilizzando setjmp e longjmp come convenienza per l'ingegnere. Il problema era che non hanno mai detto che un throw non avrebbe causato il richiamo di distruttori di oggetti locali!

In ogni caso, il nostro gruppo ha ereditato la base di codice dopo che qualcuno ha scritto un'applicazione comportandosi come se fosse standard C++: usando le tecniche RAII e tutte le altre qualità. Abbiamo finito per riscriverlo in quello che un certo numero di noi chiama orientato agli oggetti C. Potresti considerare di mordere il proiettile e scrivere direttamente in C. Invece dei costruttori, avere un metodo di inizializzazione chiamato esplicitamente. I distruttori diventano un metodo di terminazione chiamato esplicitamente. Non c'è molto di C++ che non si possa imitare in C abbastanza rapidamente. Sì, MI è un dolore nel ... ma l'eredità singola è piuttosto facile. Dai uno sguardo allo this PDF per alcune idee. Descrive quasi l'approccio che abbiamo preso. Vorrei davvero aver scritto il nostro metodo da qualche parte ...

0

quando si eseguono lavori incorporati, una volta non riuscivo nemmeno a collegare il runtime C per i vincoli di memoria, ma l'hardware disponeva di un'istruzione DMA (dynamic memory allocator) così ho scritto il mio malloc con quell'hardware, il tuo hardware probabilmente ha un caratteristica simile, quindi potresti scrivere un malloc e poi un nuovo basato sul malloc.

In ogni caso, alla fine, ho utilizzato il 99% di allocazioni di stack e alcuni limiti impostano gli oggetti statici che vorrei riciclare, installando sul posto. Questa potrebbe essere una buona soluzione.

6

Penso che ti stai avvicinando al problema da un punto di vista che non è ottimale.

Si sta concentrando sul compilatore (o sulla sua mancanza) invece di concentrarsi sull'HARDWARE.

La risposta più probabile alle vostre domande principali è "perché l'hardware non supporta tutto ciò che è C++". I componenti hardware integrati (microcontrollori) sono noti per la personalizzazione della progettazione hardware: mappe di memoria, gestori di interrupt, I/O, ecc.

A mio parere, è necessario innanzitutto passare del tempo con il libro hardware per il microcontrollore, l'apprendimento i dettagli del dispositivo, ovvero come è stato progettato e per quale scopo principale. Alcuni sono stati progettati per la manipolazione veloce della memoria, alcuni per la gestione veloce degli I/O, alcuni per il lavoro di tipo A/D, alcuni per l'elaborazione del segnale. Il tipo di microcontrollore impone le istruzioni assembler che hanno scritto per questo, e ciò determina ciò che qualsiasi compilatore di livello superiore può fare in modo efficiente.

Se questo è importante, dedicare un po 'di tempo a guardare anche l'assemblatore - ti dirà cosa hanno considerato importanti i designer. Ti dirà anche molto su quanto puoi ottenere da un compilatore di alto livello.

In generale, i microcontrollori non supportano il C++ in quanto il design non si preoccupa veramente degli oggetti o della gestione della memoria (dal punto di vista del C++). Può essere fatto, ma si sta spesso cercando di battere un piolo rotondo in un buco quadrato per ottenere costruttori e distruttori (e 'nuovo' e 'cancella') per lavorare nel microambiente.

SE si dispone di un compilatore C per questa unità, considerarlo una benedizione. Un buon compilatore C è spesso "più che sufficiente" per creare un eccellente software embedded.

Cheers,

-Richard

1

Si possono trovare un po 'di codice utili sulla mia A* tutorial website. Sebbene il code che ho scritto per supportare questo utilizza STL in dovrebbe essere facile da rimuovere il supporto STL. Inoltre c'è un allocatore di pool incluso in esso (fsa.h) che ho scritto per accelerare STL sulle console di gioco. È un codice C++, ma l'ho portato originariamente da C e non penso che sarebbe difficile farlo in un altro modo. Il codice è testato da oltre 10.000 persone, quindi è una buona base da cui partire.

Sostituire le strutture STL che utilizzo non è un problema poiché è limitato a Vettori. Io uso uno dei vettori come una coda di priorità usando le funzioni heap (make_heap e push_heap). È possibile sostituirlo con il mio old C code che ha una coda di priorità implementata in C che dovrebbe semplicemente cadere nel codice. (Che fa solo un allocare, quindi puoi sostituirlo con un puntatore ad un'area riservata della tua memoria

Come puoi vedere in questo frammento di codice dall'intestazione, la differenza principale nel codice C è che non c'è questo puntatore, nessun oggetto, quindi il tuo codice prende tipicamente un puntatore oggetto come primo argomento.

void PQueueInitialise(PQUEUE *pq, int32 MaxElements, uint32 MaxRating, bool32 bIsAscending); 
void PQueueFree(PQUEUE *pq); 
int8 PQueuePush(PQUEUE *pq, void *item, uint32 (*PGetRating) (void *)); 
int32 PQueueIsFull(PQUEUE *pq); 
int32 PQueueIsEmpty(PQUEUE *pq); 
void *PQueuePop(PQUEUE *pq, uint32 (*PGetRating) (void *)); 
+0

Il tuo "vecchio codice C" chiama malloc(). Non penso che ci siano molti compilatori C per piattaforme embedded a 8 bit che supportano un numero sufficiente di librerie C per darti la gestione dell'heap (malloc/free)! – mmmmmmmm

+0

Se lo si guarda con maggiore attenzione, chiama malloc per allocare un blocco di memoria per una coda con priorità di dimensione fissa. La coda stessa utilizza quindi questo blocco singolo in modo molto efficiente. Quindi può essere convertito in non usare malloc con una riga di codice. – justinhj

+0

Chi ha votato questo? Questa è roba utile, ho bisogno anche di aiuto A *. Ho un po 'di codice Google e un libro su IA ma sto ancora pensando a tutto. –

Problemi correlati