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

StackOverflow https://stackoverflow.com/questions/855996

  •  21-08-2019
  •  | 
  •  

Вопрос

Недавно я работал над некоторыми встроенными устройствами, где у нас есть некоторые структуры и объединения, которые необходимо инициализировать во время компиляции, чтобы мы могли сохранить определенные вещи во flash или ROM, которые не нужно изменять, и сэкономить немного flash или SRAM с небольшими затратами на производительность.В настоящее время код компилируется как допустимый C99, но без этой настройки он также компилировался как код на C ++, и было бы здорово поддерживать вещи, компилируемые таким образом.Одна из ключевых вещей, которая предотвращает это, заключается в том, что мы используем инициализаторы, обозначенные C99, которые не работают в подмножестве C 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_;
};

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

Сборка:

    .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

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

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

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

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

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

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

В принципе, при взгляде на разборку кажется, что первый пример на самом деле работает медленнее.Я посмотрел на выходные данные сборки и, ну, должно быть, я немного заржавел.Может быть, кто-нибудь мог бы дать мне некоторое представление.Выходные данные сборки первого 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 ++.

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

Данный

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)

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

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

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

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

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

Следует отметить еще один момент:ключевой причиной использования назначенного инициализатора является инициализация не как первого члена объединения.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top