2009-06-04 12 views
8

Al fine di garantire che un certo codice di inizializzazione viene eseguito prima di main (utilizzando Arduino/AVR-gcc) Ho codice come il seguente:Come posso eseguire l'inizializzazione pre-main in C/C++ con avr-gcc?

class Init { 
public: 
    Init() { initialize(); } 
}; 

Init init; 

Idealmente mi piacerebbe essere in grado di scrivere semplicemente:

initialize(); 

ma questo non compila ...

c'è un modo meno dettagliato per ottenere lo stesso effetto?

Nota: il codice fa parte di uno schizzo Arduino così la funzione main viene generato automaticamente e non può essere modificato (ad esempio per chiamare initialize prima di qualsiasi altro codice).

Aggiornamento: idealmente l'inizializzazione verranno eseguiti nella funzione setup, ma in questo caso non v'è altro codice a seconda che si verifica prima main.

risposta

11

È possibile utilizzare GCC di constructor attribute per garantire che venga chiamato prima main():

void Init(void) __attribute__((constructor)); 
void Init(void) { /* code */ } // This will always run before main() 
+0

+1 Semplice e pulito. Funziona sicuramente anche su avr-gcc? –

+0

Non lo so per certo, dato che non ho mai usato avr-gcc, ma secondo questa pagina http://www.nongnu.org/avr-libc/user-manual/porting.html, avr-gcc supporta altri tipi di attributi. –

+1

Ho provato questo usando il software Arduino (che è supportato da avr-gcc) e funziona. Accetterò questa risposta, grazie. –

2

La vostra soluzione in modo semplice e pulito. Quello che puoi fare è inserire il tuo codice nello spazio dei nomi anonimo. Non vedo alcuna necessità di fare meglio di quello :)

+0

Il wrapping in un namespace anonimo presumibilmente impedirà il nome della classe e la sua istanza che inquina lo spazio dei nomi (che è una buona cosa) ma in realtà non aiuta a rendere il codice meno dettagliato ... –

0

Certo, si mette questo in uno dei tuoi file di intestazione, dici preinit.h:

class Init { public: Init() { initialize(); } }; Init init; 

e poi, in un delle vostre unità di compilazione, messo:

void initialize(void) { 
    // weave your magic here. 
} 
#include "preinit.h" 

so che è un ripiego, ma io non sono a conoscenza di alcun modo portabile di fare l'inizio di pre-main senza utilizzare un costruttore della classe eseguito nell'ambito di file.

Si dovrebbe anche fare attenzione a includere più di una di queste funzioni di inizializzazione poiché non credo che C++ imponga l'ordine: potrebbe essere casuale.

Non sono sicuro di questo "sketch" di cui parli, ma sarebbe possibile trasformare l'unità di compilazione principale con uno script prima di averlo passato al compilatore, qualcosa come:

awk '{print;if (substr($0,0,11) == "int main (") {print "initialize();"};}' 

si può vedere come questo potrebbe influenzare il vostro programma perché:

echo '#include <stdio.h> 
int main (void) { 
    int x = 1; 
    return 0; 
}' | awk '{ 
    print; 
    if (substr($0,0,11) == "int main (") { 
     print " initialize();" 
    } 
}' 

genera il seguente con la chiamata initialize() aggiunto:

#include <stdio.h> 
int main (void) { 
    initialize(); 
    int x = 1; 
    return 0; 
} 

È possibile che non sia possibile post-elaborare il file generato, nel qual caso è necessario ignorare l'opzione finale, ma è quello che cercherò inizialmente.

+0

Una soluzione doesn ' t devono essere portatili Ha solo bisogno di lavorare usando avr-gcc. –

0

Utilizzare membri statici delle classi. Sono inizializzati prima di entrare in main. Lo svantaggio è che non è possibile controllare l'ordine di inizializzazione dei membri della classe statici.

Qui è la vostra esempio trasforma:

class Init { 
private: 
    // Made the constructor private, so to avoid calling it in other situation 
    // than for the initialization of the static member. 
    Init() { initialize(); } 

private: 
    static Init INIT; 
}; 


Init Init::INIT; 
+3

Questo è più prolisso, non meno. –

+0

Questa idea mi chiede se l'ordine di inizializzazione dei membri statici nella gerarchia * della classe * sia definito in C++? Se così fosse sarebbe un modo per affrontare il "fiasco dell'ordine di inizializzazione statica". –

+0

@Piotr: l'ordine di inizializzazione in un'unità di traduzione è definito come "l'ordine in cui vengono visualizzate le definizioni". Tra unità di traduzione è indefinito. –

2

Se si utilizza l'ambiente Arduino, c'è qualche motivo non è possibile inserirlo nel setup method?

Ovviamente, questo è dopo l'installazione hardware specifica di Arduino, quindi se si dispone di materiale di basso livello che deve andare prima del main, allora è necessario un po 'di magia del costruttore.

UPDATE:

Ok, se deve essere fatto prima che il principale Credo che l'unico modo è quello di utilizzare un costruttore come già fate.

si può sempre fare una macro preprocessore di esso:

#define RUN_EARLY(code) \ 
namespace { \ 
    class Init { \ 
     Init() { code; } \ 
    }; \ 
    Init init; \ 
} 

Ora, questo dovrebbe funzionare:

RUN_EARLY(initialize()) 

ma non è davvero facendo le cose più breve, semplicemente spostando il codice verbose intorno.

+0

+1, ma sfortunatamente non posso usare setup() per questo. Aggiornerò la domanda per chiarire questo punto. –

4

È possibile effettuare quanto sopra leggermente più corto, dando "inizializzare" un tipo di ritorno, e l'utilizzo che per inizializzare una variabile globale:

int initialize(); 
int dummy = initialize(); 

Tuttavia, è necessario stare attenti a questo, lo standard fa non garantisce che l'inizializzazione sopra (o quella per l'oggetto init) avvenga prima dell'esecuzione di main (3.6.2/3):

È definito dall'implementazione se l'inizializzazione dinamica (8.5, 9.4 , 12.1, 12.6.1) di un oggetto di scope namespace viene fatto prima della prima dichiarazione di mai n.

L'unica cosa che è garantita è che l'inizializzazione avverrà prima che il "manichino" sia mai usato.

Un'opzione più invadente (se possibile) potrebbe essere quella di utilizzare "-D main = avr_main" nel makefile. È quindi possibile aggiungere il proprio main come segue:

// Add a declaration for the main declared by the avr compiler. 
int avr_main (int argc, const char * argv[]); // Needs to match exactly 

#undef main 
int main (int argc, const char * argv[]) 
{ 
    initialize(); 
    return avr_main (argc, argv); 
} 

Almeno qui si è certi che l'inizializzazione avverrà quando ci si aspetta.

+0

Sfortunatamente l'ambiente Arduino controlla l'esecuzione del compilatore in modo che non ci siano makefile che posso modificare. –

3

Ecco un metodo un po 'male per raggiungere tale obiettivo:

#include <stdio.h> 

static int bar = 0; 

int __real_main(int argc, char **argv); 

int __wrap_main(int argc, char **argv) 
{ 
    bar = 1; 
    return __real_main(argc, argv); 
} 

int main(int argc, char **argv) 
{ 
    printf("bar %d\n",bar); 
    return 0; 
} 

Aggiungere il seguente alle bandiere linker: --wrap main

ad es.

gcc -Xlinker --wrap -Xlinker main a.c 

Il linker sostituirà tutte le chiamate al main con le chiamate per __wrap_main, vedere la ld man page su --wrap

+0

Sfortunatamente non ho alcun controllo sulla linea di comando gcc (in realtà avr-gcc) poiché questa viene gestita automaticamente dall'ambiente Arduino. –

2

È possibile utilizzare le sezioni ".init *" per aggiungere il codice C da eseguire prima di main() (e anche del runtime C). Queste sezioni sono collegate all'eseguibile alla fine e richiamate in un momento specifico durante l'inizializzazione del programma. È possibile ottenere la lista qui:

http://www.nongnu.org/avr-libc/user-manual/mem_sections.html

.init1 ad esempio è debolmente legato a __init(), quindi se si definisce __init(), che sarà collegato e chiamò prima cosa. Tuttavia, lo stack non è stato configurato, quindi devi stare attento a ciò che fai (usa solo la variabile register8_t, non chiama nessuna funzione).

0

Ecco come eseguo la codifica pre-principale. Ci sono sezioni di init sever eseguite prima di main, si riferisce alle sezioni initN http://www.nongnu.org/avr-libc/user-manual/mem_sections.html.

In ogni caso, questo funziona solo con l'ottimizzazione di -O0 per qualche motivo. Cerco ancora di scoprire quale opzione "ottimizza" il mio codice di assemblaggio pre-principale.

static void 
__attribute__ ((naked)) 
__attribute__ ((section (".init8"))) /* run this right before main */ 
__attribute__ ((unused)) /* Kill the unused function warning */ 
stack_init(void) {assembly stuff} 

Update, si scopre ho sostenuto questa funzione è inutilizzata, che porta a ottimizzare la routine di distanza. Avevo intenzione di uccidere la funzione di avvertimento inutilizzato. È invece corretto per l'attributo usato usato.