Attualmente sto aggiornando una libreria C++ per Arduino (in particolare i processori AVR a 8 bit compilati usando avr-gcc).Variabile extern solo nell'intestazione che funziona in modo imprevisto, perché?
In genere gli autori delle librerie Arduino predefinite amano includere una variabile extern per la classe all'interno dell'intestazione, che viene anche definita nel file class.cpp. Questo presumo fondamentalmente di avere tutto pronto per i principianti come oggetti built-in.
Lo scenario che ho è: La libreria che ho aggiornato non richiede più il file .cpp e l'ho rimosso dalla libreria. Non è stato fino a quando non sono andato su un passaggio finale a verificare i bug che ho realizzato, non è stato prodotto alcun errore di linker nonostante non fosse stata fornita una definizione per la variabile extern
in un file .cpp.
Questo è semplice come posso farlo (file di intestazione):
struct Foo{
void method() {}
};
extern Foo foo;
Compreso il codice ed il suo utilizzo in uno o più file di origine non causa alcun errore di linker. L'ho provato in entrambe le versioni di GCC che Arduino usa (4.3.7, 4.8.1) e con C++ 11 abilitato/disabilitato.
Nel mio tentativo di causare un errore, ho trovato che era possibile solo quando si faceva qualcosa come prendere l'indirizzo dell'oggetto o modificare il contenuto di una variabile fittizia che ho aggiunto.
Dopo aver scoperto questo trovo 'importante notare:
- Le funzioni di classe restituiscono solo altri oggetti, come in, niente come gli operatori che tornano riferimenti a se stesso, o anche una copia.
- Modifica solo oggetti esterni (registri che sono effettivamente riferimenti a
volatile uint8_t
nel codice) e restituisce provvisori di altre classi. - Tutte le funzioni di classe in questa intestazione sono così semplici che costano meno o uguale al costo di una chiamata di funzione, quindi sono (nei miei test) completamente allineate nel chiamante. Una tipica affermazione può creare molti oggetti temporanei nella catena di chiamata, tuttavia il compilatore vede attraverso questi e produce registri di modifica del codice efficienti direttamente, piuttosto che un insieme di chiamate di funzioni annidate.
Ricordo anche la lettura in n37977.1.1 - 8 che extern
può essere utilizzato su tipi incompleti, ma la classe è completamente definito considerando che la dichiarazione non è (questo è probabilmente irrilevante).
Sono portato a credere che questo possa essere il risultato di ottimizzazioni in gioco. Ho visto l'effetto che prendere l'indirizzo ha su oggetti che altrimenti sarebbero considerati costanti e compilati senza l'utilizzo della RAM. Aggiungendo qualsiasi livello di riferimento indiretto a un oggetto in cui il compilatore non può garantire lo stato causerà questo comportamento di consumo della RAM.
Quindi, forse ho risposto alla mia domanda semplicemente chiedendole, tuttavia sto ancora facendo delle supposizioni e mi dà fastidio. Dopo un po 'di codifica C++ per hobby, letteralmente l'unica cosa nella mia lista di do-not's è fare ipotesi.
Davvero, quello che voglio sapere è:
- Per quanto riguarda la soluzione di lavoro che ho, si tratta di un semplice caso di documentare l'impossibilità di prendere l'indirizzo (causa indiretto) della classe?
- È solo un comportamento caso limite causato da ottimizzazioni che eliminano la necessità di collegare qualcosa?
- O è un comportamento non definito semplice e semplice. Come in GCC potrebbe esserci un bug e consentire il codice che potrebbe fallire se le ottimizzazioni fossero ridotte o disabilitate?
Oppure uno di voi può avere la fortuna di essere in possesso di un anello decodificatore che può trovare un paragrafo adatto nello standard che delinea le specifiche.
Questa è la mia prima domanda qui, quindi fammi sapere se desideri conoscere alcuni dettagli, posso anche fornire link GitHub al codice se necessario.
Modifica: Poiché la libreria deve essere compatibile con il codice esistente, è necessario mantenere la possibilità di utilizzare la sintassi del punto, altrimenti avrei semplicemente una classe di funzioni statiche.
Per rimuovere le ipotesi, per ora, vedo due opzioni:
- Aggiungi un cpp solo per la dichiarazione delle variabili.
- Utilizzare una definizione nell'intestazione come
#define foo (Foo())
consentendo la sintassi del punto tramite un temporaneo.
Preferisco il metodo utilizzando un definire, cosa pensa la comunità?
Cheers.
Essere coerenti. Aggiungi il file .cpp solo per le definizioni esterne e commentalo correttamente. L'aggiunta di define renderà ulteriormente offuscato il codice. – SChepurin