C ++ эквивалентно назначенным инициализаторам? - PullRequest
34 голосов
/ 13 мая 2009

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

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

Ответы [ 6 ]

21 голосов
/ 13 мая 2009

Я не уверен, что вы можете сделать это в 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
17 голосов
/ 21 июля 2012

Опираясь на ответ Shing Yip, и благодаря 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
2 голосов
/ 12 июля 2011

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

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

1 голос
/ 13 мая 2009
#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 также может иметь конструкторы. Может быть, это то, что вы хотели?

0 голосов
/ 13 мая 2009

Отчет о сухих скважинах:

Дано

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

0 голосов
/ 13 мая 2009

Следующий код компилируется без проблем с 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. Это что-то вроде хака, но если это решит твою проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...