Digamos que eu tenha um conjunto de sinalizadores, codificado em uint16_t flags
. Por exemplo AMAZING_FLAG = 0x02
,. Agora, eu tenho uma função. Essa função precisa verificar se eu quero alterar o sinalizador, porque se eu quiser fazer isso, preciso escrever para piscar. E isso é caro. Portanto, quero uma verificação que me diga se flags & AMAZING_FLAG
é igual a doSet
. Esta é a primeira ideia:
setAmazingFlag(bool doSet)
{
if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0)) {
// Really expensive thing
// Update flags
}
}
Esta não é uma declaração intuitiva. Eu sinto que deveria haver uma maneira melhor, algo como:
if ((flags & AMAZING_FLAG) != doSet){
}
Mas isso realmente não funciona, true
parece ser igual a 0x01
.
Então, existe uma maneira legal de comparar um pouco com um booleano?
c
gcc
boolean
bitwise-operators
Cheiron
fonte
fonte
(flags & AMAZING_FLAG) && doSet
:?Respostas:
Para converter qualquer número diferente de zero em 1 (verdadeiro), existe um truque antigo: aplique o
!
operador (não) duas vezes.fonte
(bool)(flags & AMAZING_FLAG) != doSet
- o que acho mais direto. (Embora isso sugira que o Sr. Microsoft tenha um problema com isso.) Além disso,((flags & AMAZING_FLAG) != 0)
provavelmente é exatamente o que compila e é absolutamente explícito.((bool)(flags & AMAZING_FLAG) != doSet)
tem o mesmo efeito(((flags & AMAZING_FLAG) != 0) != doSet)
e ambos provavelmente serão compilados exatamente para a mesma coisa. O sugerido(!!(flags & AMAZING_FLAG) != doSet)
é equivalente, e imagino que será compilado também. É inteiramente uma questão de gosto qual você acha que é mais clara: o(bool)
elenco exige que você se lembre de como os elencos e conversões para _Bool funcionam; isso!!
requer alguma outra ginástica mental, ou para você conhecer o "truque".Você precisa converter a máscara de bits em uma instrução booleana, que em C é equivalente a valores
0
ou1
.(flags & AMAZING_FLAG) != 0
. A maneira mais comum.!!(flags & AMAZING_FLAG)
. Um pouco comum, também é bom de usar, mas um pouco enigmático.(bool)(flags & AMAZING_FLAG)
. C moderno a partir de C99 e somente além.Pegue uma das alternativas acima e compare-a com o seu booleano usando
!=
or==
.fonte
Do ponto de vista lógico,
flags & AMAZING_FLAG
é apenas uma operação pouco mascarando todos os outros sinalizadores. O resultado é um valor numérico.Para receber um valor booleano, você usaria uma comparação
e agora pode comparar esse valor lógico com
doSet
.Em C, pode haver abreviações, devido às regras implícitas de conversão de números em valores booleanos. Então você também pode escrever
para escrever isso mais conciso. Mas a versão anterior é melhor em termos de legibilidade.
fonte
Você pode criar uma máscara com base no
doSet
valor:Agora seu cheque pode ficar assim:
Em algumas arquiteturas,
!!
pode ser compilado em uma ramificação e, com isso, você pode ter duas ramificações:!!(expr)
doSet
A vantagem da minha proposta é uma filial única garantida.
Nota: certifique-se de não introduzir um comportamento indefinido, deslocando a esquerda em mais de 30 (assumindo que o número inteiro seja 32 bits). Isso pode ser facilmente alcançado por um
static_assert(AMAZING_FLAG_IDX < sizeof(int)*CHAR_BIT-1, "Invalid AMAZING_FLAG_IDX");
fonte
AMAZING_FLAG
em termos deAMAZING_FLAG_IDX
(por exemplo#define AMAZING_FLAG ((uint16_t)(1 << AMAZING_FLAG_IDX))
), para não ter os mesmos dados definidos em dois locais, para que um possa ser atualizado (digamos, de0x4
para0x8
) enquanto o outro (IDX
de2
) permanece inalterado .Existem várias maneiras de executar este teste:
O operador ternário pode gerar saltos caros:
Você também pode usar a conversão booleana, que pode ou não ser eficiente:
Ou sua alternativa equivalente:
Se a multiplicação for barata, você pode evitar saltos com:
Se
flags
não estiver assinado e o compilador for muito inteligente, a divisão abaixo poderá ser compilada para uma mudança simples:Se a arquitetura usa a aritmética de complemento de dois, aqui está outra alternativa:
Como alternativa, pode-se definir
flags
como uma estrutura com campos de bits e usar uma sintaxe muito mais simples e legível:Infelizmente, essa abordagem geralmente é desaprovada, porque a especificação de campos de bits não permite controle preciso sobre a implementação no nível de bits.
fonte