2009-05-13 17 views
30

Recientemente he estado trabajando en algunos dispositivos integrados, donde tenemos algunas estructuras y uniones que deben inicializarse en tiempo de compilación para que podamos mantener ciertas cosas en flash o ROM que no necesitan ser modificadas, y guardar un pequeño flash o SRAM a un costo de rendimiento un poco. Actualmente, el código se compila como C99 válido, pero sin este ajuste, también se compilaba como código C++, y sería genial apoyar las cosas que se compilan de esa manera también. Una de las cosas clave que previene esto es que estamos usando inicializadores designados C99 que no funcionan dentro del subconjunto C de C++. No soy muy aficionado a C++, así que me pregunto qué maneras simples podría haber para que esto suceda en C compatible con C++ o en C++ que todavía permite la inicialización en tiempo de compilación para que las estructuras y uniones no necesiten ser inicializado después del inicio del programa en SRAM.C++ Equivalente a los inicializadores designados?

Un punto adicional a tener en cuenta: una razón clave para el uso del inicializador designado es INICIARSE como NO el primer miembro de una unión. Además, seguir con el estándar C++ o ANSI C es una ventaja para mantener la compatibilidad con otros compiladores (conozco las extensiones de GNU que proporcionan algo así como los inicializadores designados sin C99).

+1

Tenga en cuenta que inicializadores designados ahora trabajan en g ++. Tengo la versión 4.8.1 y podría usar los inicializadores de una enumeración y funcionó tal como se esperaba. –

Respuesta

19

No estoy seguro de poder hacerlo en C++. Para las cosas que usted necesita para inicializar utilizando inicializadores designados, puede poner los por separado en un archivo .c compilado como C99, por ejemplo:

// 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 Martillos para clavos, destornilladores para tornillos. –

+2

Lo he considerado y, finalmente, puedo ir por esa ruta. Lamentablemente, hay una plataforma a la que nos gustaría conectar que utiliza una biblioteca C++ para proporcionar soporte periférico. Proporcionan acceso a un compilador en línea, pero lo han limitado a un modo solo de C++ (por simplicidad). He intentado agarrar la biblioteca precompilado y usando una cadena de herramientas GCC local, pero hay algunos símbolos que no se resuelven de su biblioteca (Utilizan Keil/ARM RealView) cuando se enlaza con GCC y NEWLIB. Pude precompilar todo el código C99 localmente y vincularlo en línea. Solo intento mantener las cosas simples :-) –

+7

Esto no resuelve el problema en C++. –

1

El siguiente código se compila sin problemas 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; 
} 

aquí está el resultado:

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

la única cosa con la inicializadores C++ es que se necesita para inicializar todos los elementos de la estructura o el resto será initiali zed con ceros. No puedes elegir. Pero eso aún debería estar bien para su caso de uso.

Un punto adicional de nota: una razón clave para el uso de inicializador designado se initalizing como NOT el primer miembro de un sindicato.

Para eso tiene que utilizar la "solución alternativa" que se muestra en el ejemplo donde configuré el miembro "flotante" proporcionando el valor int equivalente. Es un poco complicado, pero si resuelve tu problema.

+0

Eso es solo porque 42L se convierte implícitamente a un número entero. Si desea inicializar el miembro flotante, por ejemplo, 3.5, no puede hacer eso en C++. –

+0

No puede inicializar más de un miembro de una unión.Es una unión después de todo ;-) Sin embargo, si quieres inicializar la parte "flotante" necesitarás inicializarla con el equivalente entero (probablemente como un número hexadecimal) – lothar

+6

Sí - pero los inicializadores designados de C99 te permiten inicializar un elemento de una unión que no sea la primera sin recurrir a hacks como calcular el equivalente entero definido por la implementación de float. –

0

informe agujero seco:

Dado

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

intenté derivar S1 de S, donde constructor por defecto de línea S1 invoca S (int) y pasa un valor codificado ...

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

... y luego compilado con gcc 4.0.1 -O2 -S. La esperanza era que el optimizador vería que s1.mB sería necesariamente 22 y asignar el valor que en tiempo de compilación, pero desde el ensamblador ...

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

... parece que el código generado hace el inicialización en tiempo de ejecución antes de main. Incluso si hubiera funcionado, difícilmente podría ser compilable como C99 y tendría el riesgo de derivar una clase para cada objeto que quisiera inicializar; entonces, no te molestes

+1

Gracias. No necesariamente tiene que compilarse como C99. Los inicializadores están en varios lugares, pero están definidos con algunas macros, por lo que puedo hacer ifdef __cplusplus con una modificación mínima, eso también está bien. –

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 

En C++ union también pueden tener constructores. ¿Puede ser que esto es lo que querías?

+0

¿La inicialización se realiza en tiempo de ejecución o compilación? Eso es, supongo, el problema crítico aquí. –

+0

Tendré que consultar el estándar holly para estar seguro, pero de repente, creo que todas las variables estáticas globales se inicializan y almacenan en la sección de datos de la imagen ejecutable. Su mejor opción es probarlo con su compilador y ver qué hace. –

+1

No, el espacio se asignará en la imagen ejecutable, se inicializará en ceros y se llamará al constructor en el tiempo de ejecución. Sin embargo, para agregar a la diversión, los sistemas integrados pueden ser un poco inconsistentes en este último punto: en teoría, el enlazador recopila una lista de llamadas de constructor estáticas y luego libgcc las llama durante el arranque del proceso, pero dependiendo de su plataforma no puede suceder, o solo sucede si selecciona las opciones de compilación correctas. – Tom

2

Esto es algo así tanto una respuesta y una pregunta. Me doy cuenta de que este hilo está muerto, pero es exactamente lo que estaba buscando esta noche.

He hurgado un poco y lo más cercano que puedo llegar a lo que quiero (que es similar a lo que quieres ... He estado trabajando con fotos y no necesito usar C++, pero tengo curiosidad de cómo se podría hacer) es el primer ejemplo de código:

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

Esto tiene un aspecto muy similar a los inicializadores designados estilo C99 con una advertencia que mencionaré más adelante. (. Es probable que terminar con esto en __cplusplus #ifdef si quería la estructura a ser compilado por cualquiera) La segunda versión del código Miré es la siguiente:

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

Básicamente, de mirar el desmontaje, parece el primer ejemplo es en realidad más lento. Miré la salida de la asamblea y, bueno, debo estar un poco oxidado. Tal vez alguien podría darme una idea. La salida de montaje de la primera CPP compilado y parecí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 

El segundo ejemplo parecí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 $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 

Ambos fueron generados con un comando g++ -O0 -S main.cpp. Claramente, el ejemplo intuitivamente menos eficiente generó un código de operación más eficiente en términos de número de instrucciones. Por otro lado, hay pocos casos en los que pueda imaginar que las pocas instrucciones sean críticas. (Por otra parte, realmente no tengo problemas para entender el montaje no está escrito por los seres humanos, así que tal vez me estoy perdiendo algo ...) Creo que esto proporciona una solución, aunque con retraso, a la pregunta formulada James. Lo próximo que debería probar es si se permite la misma inicialización en C99; si eso funciona, creo que aborda completamente el problema de James.

de responsabilidad: No tengo idea de si esto funciona o comporta de manera similar a cualquier otro compiladores distintos g ++.

+0

Funciona para mí, no en una sección de ruta rápida de todos modos. ¡Mejor solución que usar otro archivo C! ¡Voto! – Sam

+5

Nuevamente con llegar tarde, pero esto simplemente no funciona si cambia jmcd.x y jmcd.y en la primera solución. Esto se debe a que no es una construcción especial, solo es una inicialización regular con más expresiones. Entonces se ejecuta jmcd.x = 12, y luego el valor del resultado para esta expresión (12) se asigna al primer campo de la estructura (x). Lo mismo para y. Si los intercambia, ambos campos serán 12. – Asaf

+0

El soporte para inicializadores designados en C++ es una extensión GNU, y no parte de C++ 11. Incluso con la extensión GCC, la inicialización designada solo está permitida para los tipos POD. – chys

18

Sobre la base de la respuesta de Shing Yip, y con el beneficio del tiempo de 3 años, C++ 11 ahora puede garantizar el tiempo de compilación de inicialización:

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