Como criar método estático, que avalia a variável local estática de uma vez?
-
21-09-2019 - |
Pergunta
Eu tenho uma classe com método estático que tem uma variável local estática.Eu quero que a variável a ser calculada/avaliadas de uma vez (a 1ª vez que chamar a função) e para quaisquer subsequentes invocação, que não é avaliada mais.Como fazer isso?Aqui está a minha classe:
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;
}
};
EDITAR:
Obrigado a todos pela sua pronta ajuda.+1 para todos.Eu escolhi Tyler McHenry a resposta de porque ele não precisa de qualquer comparação, puramente estática função de avaliação.Eu vou precisar deste código, para alocador de forma a evitar outro "se" deve ser melhor.Obrigado novamente!
EDITAR:
gf resposta acabou por ser o melhor, de como ela lida com a atribuição durante o tempo de compilação e salva o programa de thread-safe dor de cabeça e explícitas de inicialização.No entanto, eu respeito o anterior melhor resposta.Vou dar crédito aqui em vez de alterar a marca de escala.Obrigado a todos por ajudar!
Solução
Fazer outra função estática que faz o cálculo, e usar isso para a inicialização da variável, exemplo:
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;
}
Variáveis estáticas são garantidos para ser inicializado exatamente uma vez (a primeira vez que a função que as contenham, é usado).
Editar: Mas isso é não thread-safe. Esta página explica por que, em grande detalhe.
Para torná-lo thread-safe, não é suficiente para quebrar a inicialização do totalSize
(a chamada computeSize
) em uma seção crítica, porque variável estática inicialização do compilador "magia", e pode ser que a variável passa a inicialização, a qualquer momento durante a chamada para getSize
antes de ser utilizado, mesmo antes de a função da primeira instrução.O que você precisa fazer é impedir que mais de um segmento de mesmo chamar getSize
ao mesmo tempo, o que pode ser feito com outro nível de indireção, e.g.
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;
}
Isso impede que duas threads a partir até mesmo de entrar na função que contém a variável estática, ao mesmo tempo, e garantir que a inicialização irá ocorrer dentro de uma seção crítica.
Outras dicas
Mova o cálculo para uma função auxiliar:
static uint32_t totalSize = calculateTotalSize();
A função auxiliar será invocada apenas quando totalSize
é inicializado.
Um pouco tarde, mas por que você está fazendo um cálculo de tempo de execução (potencialmente) aqui? Use constantes de tempo de compilação e você nunca pode ter problemas de rosqueamento:
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;
}
Algo como:
static uint32_t getSize()
{
static uint32_t totalSize = 0;
static bool computed = 0;
if(computed)
return totalSize;
computed = 1;
// ... go on with your computation
faria o truque. Observe que não é seguro para threads.
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
}