C ++ эквивалентен назначенным инициализаторам?

Недавно я работал над некоторыми встроенными устройствами, где у нас есть некоторые структуры и союзы, которые необходимо инициализировать во время компиляции, чтобы мы могли хранить определенные вещи во флеш-памяти или ПЗУ, которые не нуждаются в изменении, и сэкономить немного flash или SRAM с небольшими затратами на производительность. В настоящее время код компилируется как действительный C99, но без этой настройки он также использовался для компиляции как кода на C ++, и было бы здорово поддерживать то, что было скомпилировано таким же образом. Одна из ключевых моментов, которые мешают этому, заключается в том, что мы используем назначенные инициализаторы C99, которые не работают в подмножестве C на C ++. Я не очень похож на C ++, поэтому мне интересно, какие простые способы могут произойти в C ++ или C ++, которые до сих пор разрешают инициализацию во время компиляции, чтобы структуры и объединения не нуждались в инициализируется после запуска программы в SRAM.

Еще одно примечание: ключевая причина для использования назначенного инициализатора – инициализация как НЕ первого члена профсоюза. Кроме того, придерживаться стандартного C ++ или ANSI C является плюсом, чтобы поддерживать совместимость с другими компиляторами (я знаю об расширениях GNU, которые предоставляют что-то вроде назначенных инициализаторов без C99).

    Я не уверен, что вы можете сделать это на C ++. Для того, что вам нужно инициализировать с помощью назначенных инициализаторов, вы можете поместить их отдельно в файл .c скомпилированный как C99, например:

     // 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-х лет, C ++ 11 теперь может гарантировать инициализацию времени компиляции:

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

    Это как ответ, так и вопрос. Я понимаю, что эта нить мертва, но это именно то, что я смотрел сегодня вечером.

    Я сделал несколько попыток, и самое близкое, что я могу получить к тому, что хочу (что похоже на то, что вы хотите … Я работал с фото и не нуждаюсь в использовании c ++, но мне любопытно, как это можно сделать ) является первым примером кода:

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

    Это очень похоже на обозначенные инициализаторы стиля C99 с предостережением, о котором я расскажу позже. (Вероятно, вы могли бы обернуть это в #ifdef __cplusplus, если хотите, чтобы структура была скомпилирована.) Вторая версия кода, на которую я смотрел, такова:

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

    В принципе, глядя на parsingку, кажется, что первый пример на самом деле медленнее. Я посмотрел на сборку, и, должно быть, я немного ржавый. Может быть, кто-то может дать мне некоторое представление. Вывод сборки первого cpp скомпилирован и выглядит так:

     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 

    Второй пример выглядел так:

     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 

    Оба они были созданы с помощью команды g++ -O0 -S main.cpp . Очевидно, что интуитивно менее эффективный пример генерировал более эффективный код операции с точки зрения количества инструкций. С другой стороны, есть несколько случаев, когда я мог представить, что некоторые инструкции являются критическими. (С другой стороны, мне действительно трудно понять сборку, не написанную людьми, поэтому, возможно, мне что-то не хватает ...) Я думаю, что это решение, хотя и поздно, на вопрос, заданный Джеймсом. Следующее, что я должен проверить, - это то, что в C99 разрешена такая же инициализация; если это сработает, я думаю, что он полностью решает проблему Джеймса.

    Отказ от ответственности: я понятия не имею, работает ли это или ведет себя аналогично для других компиляторов, отличных от g ++.

    Следующий код компилируется без проблем с 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; } 

    Вот результат:

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

    Единственное, что связано с инициализаторами C ++, это то, что вам нужно инициализировать все элементы структуры, а остальные будут инициализированы нулями. Вы не можете выбирать. Но это должно быть хорошо для вашего случая использования.

    Еще одно примечание: ключевая причина для использования назначенного инициализатора - инициализация как НЕ первого члена профсоюза.

    Для этого вам нужно использовать «обходной путь», показанный в примере, где я устанавливаю член «float», предоставляя эквивалентное значение int. Это немного взломать, но если он решает вашу проблему.

     #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 

    В C ++ союз тоже может иметь конструкторы. Может быть, это то, что вы хотели?

    Отчет о сухом отверстии:

    Данный

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

    Я попытался получить S1 из S, где встроенный конструктор по умолчанию S1 вызывает S (int) и передает жестко заданное значение …

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

    … и затем скомпилирован с gcc 4.0.1 -O2 -S. Надеюсь, что оптимизатор увидит, что s1.mB обязательно будет 22 и присвойте значение ему во время компиляции, но из ассемблера …

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

    … похоже, что сгенерированный код выполняет инициализацию во время выполнения до основного. Даже если бы это сработало, вряд ли это было бы скомпилировано как C99 и было бы клочом для получения classа для каждого объекта, который вы хотели инициализировать; так что не беспокойтесь.

    Давайте будем гением компьютера.