Pregunta

Recientemente he estado trabajando en algunos dispositivos embebidos, donde tenemos algunas estructuras y uniones que necesitan ser inicializado en tiempo de compilación para que podamos mantener ciertas cosas en flash o ROM que no necesitan ser modificados, y ahorrar un poco de flash o SRAM en un poco de un costo de rendimiento. Actualmente el código se compila como válido C99, pero sin este ajuste se utiliza para compilar código C ++ como así, y sería genial para apoyar las cosas que están siendo recopilados de esa manera también. Una de las principales cosas que impide esto es que estamos utilizando inicializadores designados C99 que no funcionan dentro del subconjunto C de C ++. No soy mucho de piel de ante un C ++, por lo que estoy preguntando qué formas sencillas podrían existir para que esto suceda, ya sea en C ++ C compatible o en C ++ que todavía permiten la inicialización en tiempo de compilación para que las estructuras y uniones no necesitan ser inicializado después del inicio del programa en la SRAM.

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. Además, se pegue a la norma ANSI C ++ o C es un punto a favor con el fin de mantener la compatibilidad con otros compiladores (que sé sobre las extensiones de GNU que proporcionan algo así como inicializadores designados sin C99).

¿Fue útil?

Solución

No estoy seguro de que puede hacerlo en C ++. Para las cosas que usted necesita para inicializar utilizando inicializadores designados, puede poner los por separado en un archivo compilado .c 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

Otros consejos

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

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

extern constexpr Bar bar1(1);
extern constexpr Bar bar2(1.234f);

Asamblea:

    .globl  _bar1                   ## @bar1
    .p2align    2
_bar1:
    .long   1                       ## 0x1

    .globl  _bar2                   ## @bar2
    .p2align    2
_bar2:
    .long   1067316150              ## float 1.23399997
#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 la unión de C ++ pueden tener constructores también. Puede ser esto es lo que quería?

Esto es algo así tanto una respuesta y una pregunta. Soy consciente de que el hilo está muerto, pero es exactamente lo que estaba buscando en esta noche.

Hice un poco de hurgar y lo más cercano que puedo llegar a lo que quiero (que es similar a lo que quiere ... He estado trabajando con las fotografías y no tienen necesidad de utilizar C ++, pero tengo curiosidad cómo se 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 estilo C99 designados 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, aparece el primer ejemplo es en realidad más lento. He mirado en la salida de montaje y, así, debo ser un poco oxidado. Tal vez alguien me puede dar una idea. La salida de montaje de la primera CPP compila y se parecía 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 se parecía 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 genera código de operación más eficiente en términos de número de instrucciones. Por otro lado, hay algunos casos en los que podía imaginar las pocas instrucciones ser crítico. (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 siguiente que debe probar es si se permite que la misma inicialización en C99; si funciona, creo que atienda de manera integral el problema de James.

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

Informe agujero seco:

Dado

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

I intentado derivar S1 de S, donde constructor predeterminado inline de S1 invoca S (int) y pasa a 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 la inicialización en tiempo de ejecución antes de la principal. Incluso si hubiera funcionado, difícilmente sería compilables como C99 y tendría la kludge de derivar una clase para cada objeto que quería para inicializar; Por lo tanto, no se moleste.

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 los inicializadores de C ++ es que se necesita para inicializar todos los elementos de la estructura o el resto se inicializará con ceros. No se puede escoger y elegir. Pero eso debe todaví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 una unión

Para que usted necesita para usar la "solución" que se muestra en el ejemplo en el que me puse el miembro de "flotar", proporcionando el valor int equivalente. Es un poco de un truco, pero si resuelve su problema.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top