最近、私はいくつかの組み込みデバイスに取り組んでいます。そこでは、コンパイル時に初期化する必要があるいくつかの構造体とユニオンがあり、特定のものをフラッシュまたはROM変更する必要があり、パフォーマンスを少し犠牲にしてフラッシュまたはSRAMを少し節約する必要があります。現在、コードは有効なC99としてコンパイルされますが、この調整を行わないと、C++コードとしてもコンパイルされていたため、サポートが必要です。これを防ぐ重要な点の1つは、C++のCサブセット内では機能しないC99指定の初期化子を使用していることです。私はC++マニアではないので、疑問に思っています。これをC++互換のC、またはコンパイル時に初期化できるC++でこれを実現するための簡単な方法はありますか。そのため、SRAMでのプログラムの起動後に構造体と共用体を初期化する必要はありません。
もう1つの注意点:指定された初期化子を使用する主な理由は、ユニオンの最初のメンバーではないとして初期化することです。また、他のコンパイラとの互換性を維持するために、標準のC++またはANSICに固執することはプラスです(C99なしで指定された初期化子のようなものを提供するGNU拡張機能について知っています)。
C++で実行できるかどうかはわかりません。指定されたイニシャライザを使用して初期化する必要があるものについては、C99としてコンパイルされた.c
ファイルに個別に配置できます。例:
// 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
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
これは一種の答えと質問の両方です。このスレッドは死んでいることに気づきましたが、まさに今夜私が調べていたものです。
私はいくつかの突っ込みをして、私が望むものに到達できる最も近いものをしました(これはあなたが望むものに似ています...私は写真を扱っていて、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でラップします。)私が見たコードの2番目のバージョンは次のとおりです。
#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
2番目の例は次のようになりました。
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 ++以外の他のコンパイラでも同様に機能するか、動作するかはわかりません。
#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++では、共用体にコンストラクターを含めることもできます。これはあなたが望んでいたものかもしれませんか?
次のコードは、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++初期化子の唯一のことは、構造体のすべての要素を初期化する必要があることです。そうしないと、残りはゼロで初期化されます。選ぶことはできません。しかし、それでもユースケースには問題ないはずです。
もう1つの注意点:指定された初期化子を使用する主な理由は、ユニオンの最初のメンバーではないとして初期化することです。
そのためには、同等のint値を指定して「float」メンバーを設定した例に示されている「回避策」を使用する必要があります。それはちょっとしたハックですが、それがあなたの問題を解決するなら。
ドライホールレポート:
与えられた
struct S {
int mA;
int mB;
S() {}
S(int b} : mB(b) {} // a ctor that does partial initialization
};
SからS1を導出しようとしました。ここで、S1のインラインデフォルトコンストラクターはS(int)を呼び出し、ハードコードされた値を渡します...
struct S1 {
S1() : S(22) {}
} s1;
...そしてgcc4.0.1 -O2-Sでコンパイルされます。オプティマイザーがs1.mBが必ず22であることを確認し、コンパイル時に値を割り当てることを期待していましたが、アセンブラーから...
movl $22, 4+_s1-"L00000000002$pb"(%ebx)
...生成されたコードは、実行時にmainの前に初期化を行うようです。それが機能したとしても、C99としてコンパイルすることはほとんどできず、初期化するオブジェクトごとにクラスを導出するという応急修理が必要になります。だから、気にしないでください。