2009-05-13 13 views
30

Recentemente ho lavorato su alcuni dispositivi incorporati, dove abbiamo alcune strutture e unioni che devono essere inizializzate in fase di compilazione in modo che possiamo mantenere certe cose in flash o ROM che non devono essere modificate e salvare un po 'di flash o SRAM a un costo leggermente superiore. Attualmente il codice viene compilato come C99 valido, ma senza questo aggiustamento veniva usato anche per compilare come codice C++, e sarebbe bello supportare anche le cose che vengono compilate in quel modo. Una delle cose fondamentali che impedisce questo è che stiamo usando gli inizializzatori designati C99 che non funzionano all'interno del sottoinsieme C di C++. Non sono un gran che un C++ buff, quindi mi chiedo quali semplici modi ci possano essere per fare in modo che ciò accada in C + compatibile con C++, o in C++ che comunque permetta l'inizializzazione in fase di compilazione in modo che non siano necessarie le strutture e i sindacati inizializzato dopo l'avvio del programma in SRAM.equivalenti in C++ agli inizializzatori designati?

Un ulteriore punto di nota: un motivo chiave per l'utilizzo di inizializzatore designato è initalizing come NON il primo membro di un sindacato. Inoltre, il mantenimento di standard C++ o ANSI C è un vantaggio per mantenere la compatibilità con altri compilatori (conosco le estensioni GNU che forniscono qualcosa come inizializzatori designati senza C99).

+1

Nota che inizializzatori designati ora funzionano in g ++. Ho la versione 4.8.1 e potrei usare gli inizializzatori da un enum e ha funzionato esattamente come previsto. –

risposta

19

Non sono sicuro di poterlo fare in C++. Per le cose che è necessario inizializzare utilizzando inizializzatori designati, si può mettere quelli separatamente in un file .c compilato come C99, ad esempio:

// In common header file 
typedef union my_union 
{ 
    int i; 
    float f; 
} my_union; 

extern const my_union g_var; 

// In file compiled as C99 
const my_union g_var = { .f = 3.14159f }; 

// Now any file that #include's the header can access g_var, and it will be 
// properly initialized at load time 
+3

+1 Martelli per chiodi, cacciaviti per viti. –

+2

L'ho preso in considerazione e alla fine potrei finire per quella via. Sfortunatamente c'è una piattaforma che vorremmo portare a quella che usa una libreria C++ per fornire supporto perhipheral. Forniscono l'accesso a un compilatore online, ma lo hanno limitato a una modalità solo C++ (per semplicità). Ho provato ad afferrare la libreria precompilata e ad usare una toolchain GCC locale, ma ci sono alcuni simboli che non vengono risolti dalla loro libreria (usano RealView di Keil/ARM) quando si collegano a GCC e Newlib. Potrei pre-compilare localmente tutto il codice C99 e collegarlo online. Sto solo cercando di mantenere le cose semplici :-) –

+7

Questo non risolve il problema in C++. –

1

Il seguente codice viene compilato senza problemi con g ++:

#include <iostream> 

struct foo 
{ 
    int a; 
    int b; 
    int c; 
}; 

union bar 
{ 
    int a; 
    float b; 
    long c; 
}; 

static foo s_foo1 = {1,2,3}; 
static foo s_foo2 = {1,2}; 
static bar s_bar1 = {42L}; 
static bar s_bar2 = {1078523331}; // 3.14 in float 


int main(int, char**) 
{ 
    std::cout << s_foo1.a << ", " << 
       s_foo1.b << ", " << 
       s_foo1.c << std::endl; 

    std::cout << s_foo2.a << ", " << 
       s_foo2.b << ", " << 
       s_foo2.c << std::endl; 

    std::cout << s_bar1.a << ", " << 
       s_bar1.b << ", " << 
       s_bar1.c << std::endl; 

    std::cout << s_bar2.a << ", " << 
       s_bar2.b << ", " << 
       s_bar2.c << std::endl; 

    return 0; 
} 

Ecco il risultato:

$ g++ -o ./test ./test.cpp 
$ ./test 
1, 2, 3 
1, 2, 0 
42, 5.88545e-44, 42 
1078523331, 3.14, 1078523331 

l'unica cosa che con il C++ inizializzatori è che è necessario inizializzare tutti gli elementi della struttura o il resto sarà initiali zato con zeri. Non puoi scegliere. Ma quello dovrebbe essere ancora OK per il tuo caso d'uso.

Un ulteriore punto di nota: un motivo chiave per l'utilizzo di inizializzatore designato è initalizing come NON il primo membro di un sindacato.

Per questo è necessario utilizzare la "soluzione alternativa" mostrata nell'esempio in cui ho impostato il membro "float" fornendo il valore int equivalente. È un po 'un trucco, ma se risolve il tuo problema.

+0

Questo è solo perché 42L è implicitamente trasmesso su un numero intero. Se si desidera inizializzare il membro mobile, ad esempio 3.5, non è possibile farlo in C++. –

+0

Non è possibile inizializzare più di un membro di un sindacato.Dopotutto è un'unione ;-) Tuttavia, se si desidera inizializzare la parte "float", sarà necessario inizializzarla con l'intero equivalente (probabilmente come numero esadecimale) – lothar

+6

Sì - ma gli inizializzatori designati da C99 consentono di inizializzare un elemento di un sindacato diverso dal primo senza ricorrere a hack come la determinazione di un intero equivalente definito da float. –

0

secco rapporto foro:

Dato

struct S { 
    int mA; 
    int mB; 
    S() {} 
    S(int b} : mB(b) {} // a ctor that does partial initialization 
}; 

ho provato derivante S1 da S, in cui il costruttore in linea di default di S1 ​​invoca S (int) e passa un valore hard-coded ...

struct S1 { 
    S1() : S(22) {} 
} s1; 

... e quindi compilato con gcc 4.0.1 -O2 -S. La speranza era che l'ottimizzatore vedrebbe che s1.mB sarebbe necessariamente 22 e assegnare il valore che al momento della compilazione, ma dalla assembler ...

movl $22, 4+_s1-"L00000000002$pb"(%ebx) 

... sembra che il codice generato fa il inizializzazione in fase di esecuzione prima di main. Anche se avesse funzionato, difficilmente sarebbe compilabile come C99 e avrebbe il kludge di derivare una classe per ogni oggetto che si desidera inizializzare; quindi, non preoccuparti.

+1

Grazie. Non deve necessariamente essere compilato come C99. Gli inizializzatori sono in un numero di punti, ma sono definiti utilizzando alcune macro, quindi qualcosa in cui posso ifdef __cplusplus con una modifica minima, va bene pure. –

1
#ifdef __cplusplus 
struct Foo 
{ 
    Foo(int a, int b) : a(a), b(b) {} 
    int a; 
    int b; 
}; 

union Bar 
{ 
    Bar(int a) : a(a) {} 
    Bar(float b) : b(b) {} 
    int a; 
    float b; 
}; 

static Foo foo(1,2); 
static Bar bar1(1); 
static Bar bar2(1.234f); 
#else 
/* C99 stuff */ 
#endif // __cplusplus 

In unione C++ può avere anche costruttori. Potrebbe essere questo quello che volevi?

+0

L'inizializzazione viene eseguita in fase di runtime o in fase di compilazione? Questo è, suppongo, il problema critico qui. –

+0

Dovrò consultare lo standard di agrifoglio per essere sicuro, ma appena fuori dalla mia testa, penso che tutte le variabili statiche globali siano inizializzate e memorizzate nella sezione di dati dell'immagine eseguibile. La soluzione migliore è provarla con il compilatore e vedere cosa fa. –

+1

No, lo spazio verrà allocato nell'immagine eseguibile, inizializzato su zeri e il costruttore chiamato in fase di esecuzione. Solo per aggiungere divertimento, però, i sistemi embedded possono essere un po 'incoerenti su quest'ultimo punto: in teoria il linker raccoglie un elenco di chiamate al costruttore statico e quindi libgcc le chiama durante il processo di bootstrap, ma a seconda della piattaforma non può succede, o succede solo se selezioni le giuste opzioni di costruzione. – Tom

2

Questa è una specie di risposta e domanda. Mi rendo conto che questo thread è morto, ma è esattamente quello che stavo guardando stasera.

Ho fatto un po 'di spiccioli e la cosa più vicina che posso ottenere in quello che voglio (che è simile a quello che vuoi ... Ho lavorato con le foto e non ho bisogno di usare il C++, ma sono curioso di sapere come potrebbe essere fatto) è il primo esempio di codice:

#include <iostream> 

using namespace std; 

extern "C" 
{ 
    typedef struct stuff 
    { 
     int x; 
     double y; 
    } things; 
} 

int main() 
{ 
    things jmcd = { jmcd.x = 12, jmcd.y = 10.1234 }; 
    cout << jmcd.x << " " << jmcd.y << endl; 
    return 0; 
} 

Questo ha un aspetto molto simile ai inizializzatori designati stile C99 con un avvertimento citerò più tardi. (. E 'probabile che questo avvolgere in #ifdef __cplusplus se si voleva la struttura per essere compilato da entrambi) La seconda versione del codice che ho guardato è questo:

#include <iostream> 

using namespace std; 

extern "C" 
{ 
    typedef struct stuff 
    { 
     int x; 
     double y; 
    } things; 
} 


int main() 
{ 
    things jmcd; 
    jmcd.x = 12; 
    jmcd.y = 10.1234; 
    cout << jmcd.x << " " << jmcd.y << endl; 
    return 0; 
} 

In sostanza, guardando il disassemblaggio, sembra il primo esempio è in realtà più lento. Ho dato un'occhiata alla produzione del gruppo e, beh, devo essere un po 'arrugginito. Forse qualcuno potrebbe darmi qualche intuizione. L'uscita assemblaggio del primo cpp compilato e sembrava:

main: 
.LFB957: 
    .cfi_startproc 
    .cfi_personality 0x0,__gxx_personality_v0 
    pushl %ebp 
    .cfi_def_cfa_offset 8 
    movl %esp, %ebp 
    .cfi_offset 5, -8 
    .cfi_def_cfa_register 5 
    subl $24, %esp 
    movl $0, 12(%esp) 
    movl $0, 16(%esp) 
    movl $0, 20(%esp) 
    movl $12, 12(%esp) 
    movl 12(%esp), %eax 
    movl %eax, 12(%esp) 
    fldl .LC0 
    fstpl 16(%esp) 
    fldl 16(%esp) 
    fstpl 16(%esp) 
    movl 12(%esp), %eax 
    movl %eax, 4(%esp) 
    fildl 4(%esp) 
    fldl 16(%esp) 
    faddp %st, %st(1) 
    fnstcw 2(%esp) 
    movzwl 2(%esp), %eax 
    movb $12, %ah 
    movw %ax, (%esp) 
    fldcw (%esp) 
    fistpl 4(%esp) 
    fldcw 2(%esp) 
    movl 4(%esp), %eax 
    leave 
    ret 
    .cfi_endproc 

Il secondo esempio sembrava:

main: 
.LFB957: 
    .cfi_startproc 
    .cfi_personality 0x0,__gxx_personality_v0 
    pushl %ebp 
    .cfi_def_cfa_offset 8 
    movl %esp, %ebp 
    .cfi_offset 5, -8 
    .cfi_def_cfa_register 5 
    subl $24, %esp 
    movl $12, 12(%esp) 
    fldl .LC0 
    fstpl 16(%esp) 
    movl 12(%esp), %eax 
    movl %eax, 4(%esp) 
    fildl 4(%esp) 
    fldl 16(%esp) 
    faddp %st, %st(1) 
    fnstcw 2(%esp) 
    movzwl 2(%esp), %eax 
    movb $12, %ah 
    movw %ax, (%esp) 
    fldcw (%esp) 
    fistpl 4(%esp) 
    fldcw 2(%esp) 
    movl 4(%esp), %eax 
    leave 
    ret 
    .cfi_endproc 

Entrambi questi sono stati generati con un comando g++ -O0 -S main.cpp. Chiaramente, l'esempio intuitivamente meno efficiente ha generato un opcode più efficiente in termini di numero di istruzioni. D'altra parte, ci sono alcuni casi in cui potrei immaginare che le poche istruzioni siano critiche. (D'altra parte, ho davvero difficoltà a capire l'assemblaggio non scritto dagli umani, quindi forse mi manca qualcosa ...) Penso che questo fornisca una soluzione, anche se in ritardo, alla domanda che James ha posto. La prossima cosa che dovrei provare è se la stessa inizializzazione è permessa in C99; se funziona, penso che risolva completamente il problema di James.

Disclaimer: Non ho idea se questo funziona o si comporta in modo simile per qualsiasi altro compilatore diverso da g ++.

+0

Funziona per me, non in una sezione di percorso veloce. Soluzione migliore rispetto all'utilizzo di un altro file C! Upvote! – Sam

+5

Ancora con il ritardo, ma questo non funziona se si scambiano jmcd.x e jmcd.y nella prima soluzione. Questo perché non è un costrutto speciale, è solo una normale inizializzazione con più espressioni. Quindi jmcd.x = 12 viene eseguito e quindi il valore del risultato per questa espressione (12) viene assegnato al primo campo della struct (x). Lo stesso per y. Se li si scambia, entrambi i campi saranno 12. – Asaf

+0

Il supporto per gli inizializzatori designati in C++ è un'estensione GNU e non parte di C++ 11. Anche con l'estensione GCC, l'inizializzazione designata è consentita solo per i tipi POD. – chys

18

Sulla risposta di Shing Yip, e con il vantaggio di tempo tre anni di, C++ 11 può ora garantire compilare il tempo di inizializzazione:

union Bar 
{ 
    constexpr Bar(int a) : a_(a) {} 
    constexpr Bar(float b) : b_(b) {} 
    int a_; 
    float b_; 
}; 

constexpr Bar bar1(1); 
constexpr Bar bar2(1.234f); 
Problemi correlati