Equivalente C ++ agli inizializzatori designati?

Recentemente ho lavorato su alcuni dispositivi embedded, 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 ‘ flash o SRAM a un costo di prestazioni. 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.

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).

Non sono sicuro che tu possa farlo in C ++. Per gli elementi che è necessario inizializzare utilizzando gli inizializzatori designati, è ansible inserirli 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 

Basandosi sulla risposta di Shing Yip, e con beneficio di 3 anni, C ++ 11 ora può garantire l’inizializzazione del tempo di compilazione:

 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); 

Questo è sia una risposta sia una 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 da ciò 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  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 agli inizializzatori designati in stile C99 con un avvertimento che menzionerò più avanti. (Probabilmente lo inseriresti in #ifdef __cplusplus se vuoi che la struttura sia compilata da entrambi). La seconda versione di codice che ho visto è questa:

 #include  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; } 

Fondamentalmente, guardando il disassemblaggio, sembra che il primo esempio sia 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'output di assembly del primo cpp è stato compilato e assomigliava a:

 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 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 ++.

Il seguente codice si compila senza problemi con g ++:

 #include  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 con gli inizializzatori C ++ è che è necessario inizializzare tutti gli elementi della struttura o il resto verrà inizializzato 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 imposto il membro "float" fornendo il valore int equivalente. È un po 'un trucco, ma se risolve il tuo problema.

 #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 

Nel sindacato C ++ possono avere anche costruttori. Potrebbe essere questo quello che volevi?

Rapporto sui fori secchi:

Dato

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

Ho provato a derivare S1 da S, dove il costruttore predefinito inline di S1 ​​richiama S (int) e passa un valore hardcoded …

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

… e poi compilato con gcc 4.0.1 -O2 -S. La speranza era che l’ottimizzatore vedesse che s1.mB sarebbe necessariamente 22 e assegna il valore in fase di compilazione, ma dall’assemblatore …

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

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