C++ Equivalent to Designated Initializers?

后端 未结 6 1851
萌比男神i
萌比男神i 2020-11-29 05:31

Recently I\'ve been working on some embedded devices, where we have some structs and unions that need to be initialized at compile time so that we can keep certain things in

相关标签:
6条回答
  • 2020-11-29 06:23
    #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 C++ union can have constructors too. May be this is what you wanted?

    0 讨论(0)
  • 2020-11-29 06:27

    I'm not sure you can do it in C++. For the stuff that you need to initialize using designated initializers, you can put those separately in a .c file compiled as C99, e.g.:

    // 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
    
    0 讨论(0)
  • 2020-11-29 06:32

    This is sort of both an answer and a question. I realize this thread is dead, but it exactly what I was looking into tonight.

    I did some poking around and the closest thing I can get to what I want (which is similar to what you want... I have been working with pics and have no need to use c++, but I am curious how it might be done) is the first code example:

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

    This has a very similar appearance to the C99 style designated initializers with a caveat I will mention later. (You would probably wrap this in #ifdef __cplusplus if you wanted the struct to be compiled by either.) The second version of code I looked at is this:

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

    Basically, from looking at the disassembly, it appears the first example is actually slower. I have looked at the assembly output and, well, I must be a little rusty. Maybe someone could give me some insight. The assembly output of the first cpp compiled and looked like:

    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
    

    The second example looked like:

    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
    

    Both of these were generated with a g++ -O0 -S main.cpp command. Clearly, the intuitively less efficient example generated more efficient opcode in terms of number of instructions. On the other hand, there are few cases where I could imagine the few instructions being critical. (On the other hand, I really have trouble understanding assembly not written by humans, so maybe I am missing something... ) I think this provides a solution, albeit late, to the question James asked. The next thing I should test is if the same initialization is allowed in C99; if that works, I think it fully addresses James's problem.

    Disclaimer: I have no idea if this works or behaves similarly for any other compilers other than g++.

    0 讨论(0)
  • 2020-11-29 06:34

    Dry hole report:

    Given

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

    I tried deriving S1 from S, where S1's inline default constructor invokes S(int) and passes a hard-coded value ...

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

    ... and then compiled with gcc 4.0.1 -O2 -S. The hope was that the optimizer would see that s1.mB would necessarily be 22 and assign the value it at compile time, but from the assembler ...

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

    ... it looks like the generated code does the initialization at runtime prior to main. Even if it had worked, it would hardly be compilable as C99 and would have the kludge of deriving a class for each object you wanted to initialize; so, don't bother.

    0 讨论(0)
  • 2020-11-29 06:35

    The following code compiles without problems with 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;
    }
    

    Here's the result:

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

    The only thing with the C++ initializers is that you need to initialize all elements of the struct or the rest will be initialized with zeros. You can not pick and choose. But that should still be OK for your use case.

    One additional point of note: a key reason for designated initializer usage is initalizing as NOT the first member of a union.

    For that you need to use the "workaround" shown in the example where I set the "float" member by providing the equivalent int value. It's a bit of a hack, but if it solves your problem.

    0 讨论(0)
  • 2020-11-29 06:36

    Building on Shing Yip's answer, and with benefit of 3 year's time, C++11 can now guarantee compile time initialization:

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

    Assembly:

        .globl  _bar1                   ## @bar1
        .p2align    2
    _bar1:
        .long   1                       ## 0x1
    
        .globl  _bar2                   ## @bar2
        .p2align    2
    _bar2:
        .long   1067316150              ## float 1.23399997
    
    0 讨论(0)
提交回复
热议问题