Pergunta

Recentemente eu estive trabalhando em alguns dispositivos embarcados, onde temos algumas estruturas e sindicatos que precisam ser inicializado em tempo de compilação para que possamos manter certas coisas em flash ou ROM que não precisam ser modificados, e guardar um pouco de flash ou SRAM em um pouco de um custo de desempenho. Atualmente os compila o código como C99 válido, mas sem esse ajustamento, que usado para compilar como código C ++, bem como, e seria ótimo para apoiar coisas sendo compilado dessa forma também. Uma das coisas principais que impede isso é que estamos usando C99 designado initializers que não funcionam dentro do subconjunto C de C ++. Eu não sou muito de um C ++ lustre, então eu estou querendo saber o que maneiras simples que pode haver para fazer isso acontecer em qualquer C ++ C compatível, ou em C ++ que ainda permitem a inicialização em tempo de compilação para que as estruturas e os sindicatos não precisa ser inicializado após a inicialização do programa na SRAM.

Um ponto adicional de nota: a principal razão para designado initializer uso é initalizing como NOT o primeiro membro de uma união. Além disso, furando com padrão C ++ ou ANSI C é um plus, a fim de manter a compatibilidade com outros compiladores (eu sei sobre as extensões GNU que fornecem algo como inicializadores designados sem C99).

Foi útil?

Solução

Eu não estou certo que você pode fazê-lo em C ++. Para o material que você precisa para inicializar usando inicializadores designados, você pode colocar os separadamente em um arquivo .c compilado como C99, por exemplo:.

// 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

Outras dicas

Com base na resposta de Shing Yip, e com benefício de tempo 3 do ano, C ++ 11 pode agora garantir a inicialização tempo de compilação:

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

Assembleia:

    .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

união Em C ++ podem ter construtores também. Pode ser este é o que você queria?

Esta é uma espécie de ambos uma resposta e uma pergunta. Sei que esta discussão está morto, mas exatamente o que eu estava olhando para esta noite.

Eu fiz alguns picar ao redor ea coisa mais próxima que eu possa chegar ao que eu quero (que é semelhante ao que você quer ... Tenho vindo a trabalhar com as fotos e não tem necessidade de uso c ++, mas estou curioso como ele pode ser feito) é o primeiro exemplo 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;
}

Este tem uma aparência muito semelhante aos initializers estilo C99 designadas com uma ressalva vou falar mais tarde. (. Você provavelmente iria quebrar isso em __cplusplus #ifdef se você queria que a estrutura para ser compilado por um ou outro) A segunda versão do código que eu olhei é o seguinte:

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

Basicamente, de olhar para a desmontagem, parece que o primeiro exemplo é realmente mais lento. Eu olhei para a saída de montagem e, bem, eu devo estar um pouco enferrujado. Talvez alguém poderia me dar algumas dicas. A saída de montagem do primeiro cpp compilados e parecia:

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

O segundo exemplo parecia:

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 foram gerados com um comando g++ -O0 -S main.cpp. Claramente, o exemplo intuitivamente menos eficiente gerado código de operação mais eficiente em termos de número de instruções. Por outro lado, existem alguns casos em que eu poderia imaginar a algumas instruções ser crítico. (Por outro lado, eu realmente tenho dificuldade para entender montagem não escrito por seres humanos, então talvez eu estou faltando alguma coisa ...) Eu acho que isso fornece uma solução, embora tarde, à pergunta James perguntou. A próxima coisa que deve testar é se a mesma inicialização é permitido em C99; Se isso funciona, eu acho que aborda totalmente o problema de James.

Disclaimer:. Eu não tenho idéia se isso funciona ou funciona da mesma forma para qualquer outro que não g compiladores ++

relatório buraco seco:

Dada

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

Eu tentei derivando S1 de S, onde construtor invoca em linha padrão do S1 S (int) e passa um valor hard-coded ...

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

... e então compilado com gcc 4.0.1 -O2 -S. A esperança era que o otimizador veria que s1.mB seria necessariamente 22 e atribuir o valor que em tempo de compilação, mas a partir do montador ...

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

... parece que o código gerado faz a inicialização em tempo de execução antes da principal. Mesmo que tivesse funcionado, dificilmente seria compilable como C99 e teria o kludge de derivação de uma classe para cada objeto que você queria para inicializar; assim, não se incomode.

Os seguintes compila código sem problemas com 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;
}

Aqui está o resultado:

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

A única coisa com as initializers C ++ é que você precisa para inicializar todos os elementos da estrutura ou o resto será inicializado com zeros. Você não pode escolher. Mas isso ainda deve ser OK para o seu caso de uso.

Um ponto adicional de nota:. Uma razão chave para designado initializer uso é initalizing como NOT o primeiro membro de uma união

Por que você precisa usar a "solução alternativa" mostrado no exemplo onde eu definir o membro "float", fornecendo o valor int equivalente. É um pouco de um truque, mas se isso resolve o seu problema.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top