一度ローカル静的変数を評価する静的メソッドを作成する方法?
-
21-09-2019 - |
質問
私は、ローカル静的変数を持つ静的メソッドを持つクラスを持っています。私は、その変数が(1回目は、私は関数を呼び出す)一度評価/計算すると、後続の呼び出しのために、それはもはや評価されませんします。どうやってするか?ここに私のクラスがあります:
template<
typename T1 = int, unsigned N1 = 1,
typename T2 = int, unsigned N2 = 0,
typename T3 = int, unsigned N3 = 0,
typename T4 = int, unsigned N4 = 0,
typename T5 = int, unsigned N5 = 0,
typename T6 = int, unsigned N6 = 0,
typename T7 = int, unsigned N7 = 0,
typename T8 = int, unsigned N8 = 0,
typename T9 = int, unsigned N9 = 0,
typename T10 = int, unsigned N10 = 0,
typename T11 = int, unsigned N11 = 0,
typename T12 = int, unsigned N12 = 0,
typename T13 = int, unsigned N13 = 0,
typename T14 = int, unsigned N14 = 0,
typename T15 = int, unsigned N15 = 0,
typename T16 = int, unsigned N16 = 0>
struct GroupAlloc
{
static const uint32_t sizeClass;
static uint32_t getSize()
{
static uint32_t totalSize = 0;
totalSize += sizeof(T1)*N1;
totalSize += sizeof(T2)*N2;
totalSize += sizeof(T3)*N3;
totalSize += sizeof(T4)*N4;
totalSize += sizeof(T5)*N5;
totalSize += sizeof(T6)*N6;
totalSize += sizeof(T7)*N7;
totalSize += sizeof(T8)*N8;
totalSize += sizeof(T9)*N9;
totalSize += sizeof(T10)*N10;
totalSize += sizeof(T11)*N11;
totalSize += sizeof(T12)*N12;
totalSize += sizeof(T13)*N13;
totalSize += sizeof(T14)*N14;
totalSize += sizeof(T15)*N15;
totalSize += sizeof(T16)*N16;
totalSize = 8*((totalSize + 7)/8);
return totalSize;
}
};
EDIT:
すべてのあなたの迅速なご協力に感謝いたします。みんなに+1。それは任意の比較、純粋に静的な関数の評価を必要としないので、私はタイラーマクヘンリーの答えを選びました。別のを避けるように、良いはず「場合」私はアロケータのために、このコードが必要になります。おかげで再び!
EDIT:
GFの答えは、それがコンパイル時の間に割り当てを扱うとスレッドセーフ頭痛および明示的な初期化からプログラムを保存して最適なものであることが判明しました。しかし、私は、以前の最良の答えを尊重します。私は、目盛りを変更するのではなく、ここに信用を与えるでしょう。助けてくれてありがとうみんな!
解決
例えば、別の静的な計算を行う機能、および変数の初期化のためのその使用を確認します。
static uint32_t computeSize()
{
uint32_t init_totalSize;
// Lots of code
return init_totalSize;
}
static uint32_t getSize()
{
static uint32_t totalSize = computeSize();
return totalSize;
}
静的変数は(それらを含む関数を使用した1回目)一度正確に初期化されることが保証されます。
の編集をしかし、これはのないのスレッドセーフです。 このページでは、非常に詳細のに理由を説明します。
静的変数の初期化は、「コンパイラの魔法」であるため、は、クリティカルセクションでtotalSize
(computeSize
への呼び出し)の初期化をラップするのに十分ではない、それはスレッドセーフにするには、それができることを受けると、変数それはでも、関数の最初の文の前に、使用する前にgetSize
への通話中に任意の時点で初期化。何がも例えば間接の別のレベル、まだを用いて達成することができると同時に、でgetSize
を呼び出してから、複数のスレッドを防ぐ行う必要があります。
static uint32_t computeSize()
{
uint32_t init_totalSize;
// Lots of code
return init_totalSize;
}
static uint32_t real_getSize()
{
static uint32_t totalSize = computeSize();
return totalSize;
}
static uint32_t getSize()
{
uint32_t totalSize;
/* --- Enter Critical Section (acquire lock) -- */
totalSize = real_getSize();
/* --- Exit Critical Section (release lock) -- */
return totalSize;
}
この防止二つのスレッドも同時に静的変数を含む関数を入力から、その初期化は、クリティカルセクション内で発生することを確認します。
他のヒント
ヘルパー関数に計算を移動します:
static uint32_t totalSize = calculateTotalSize();
totalSize
が初期化されるとき、ヘルパー関数のみが呼び出されます。
Aは少し遅れますが、なぜあなたはここにすべての(潜在的に)実行時の計算をしているの?使用コンパイル時定数とあなたも任意のスレッドの問題を持つことはできません。
template<
typename T1, unsigned N1,
typename T2, unsigned N2,
/* ... */
>
struct totalSize {
static const uint32_t sum =
sizeof(T1)*N1
+ sizeof(T2)*N2
/* ... */
;
static const uint32_t value =
8*((sum + 7)/8);
};
uint32_t GroupAlloc::getSize() {
return totalSize<T1,N1,T2,N2,/*...*/>::value;
}
のような何かます:
static uint32_t getSize()
{
static uint32_t totalSize = 0;
static bool computed = 0;
if(computed)
return totalSize;
computed = 1;
// ... go on with your computation
トリックを行うだろう。それは、スレッドセーフではないことに注意してください。
static uint32_t totalSize = 0; // initialisation performed once only
if ( totalSize == 0 ) {
totalSize += sizeof(T1)*N1;
totalSize += sizeof(T2)*N2;
totalSize += sizeof(T3)*N3;
totalSize += sizeof(T4)*N4;
// etc
}