Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Champ de bits type safe

33 réponses
Avatar
Guillaume GOURDIN
Bonjour à tous,

il est assez commun je crois d'utiliser des champs de bits, du style

const int PROPERTY1 = 1<<0;
const int PROPERTY2 = 1<<1;
const int PROPERTY3 = 1<<2;
const int PROPERTY4 = 1<<3;

class item
{
void set_property(int property);
}

item i;
i.set_properties(PROPERTY1|PROPERTY2|PROPERTY4);

La chose qui me dérange c'est que tout ça n'est pas très type safe, et
rien n'empêche de taper par exemple 'i.set_properties(RAND_MAX)'.

Ma question est donc: quelles sont les solutions que vous avez
développées pour rendre ce genre de choses plus robustes?

Merci pour vos réflexions.

- Guillaume -

10 réponses

1 2 3 4
Avatar
James Kanze
On Mar 30, 10:21 pm, Fabien LE LEZ wrote:
On Mon, 30 Mar 2009 19:39:15 +0000 (UTC), Marc
:



>C'est peut-être un peu strict comme réponse. On peut créer
>une classe propriété (qui contient un int), définir un
>operateur | sur les objets de cette classe, contrôler les
>constructeurs pour que toute propriété soit soit une des 4 de
>base soit une copie soit le résultat de |.



En gros, tu proposes une solution compliquée pour faire
fonctionner un abus, alors qu'il existe une solution simple et
légitime au problème.



Sauf que la solution est plus simple que celle que tu proposes.
L'opérateur|, par exemple, s'implémente avec quelque cast ;
avec ta solution, il faudrait traiter chaque champ
explicitement.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
Michel Decima
Falk Tannhäuser a écrit :
Michel Decima wrote:
Falk Tannhäuser a écrit :

enum Properties
{
PROPERTY1 = 1<<0,
PROPERTY2 = 1<<1,
PROPERTY3 = 1<<2,
PROPERTY4 = 1<<3
};

inline Properties operator|(Properties a, Properties b)
{ // Note: Unary '+' forces integral promotion
return static_cast<Properties>(+a | +b);
}



Ca ne serait pas plus explicite d'utiliser static_cast<int> pour
la conversion en entier ? J'avoue que sans le commentaire, je
n'aurais pas compris tout de suite...



Je trouve que la solution avec le static_cast<int> a deux défauts : un
mineur (ça fait plus de texte à taper :-)) et un majeur au niveau de la
maintenance - le jour où quelqu'un ajoute un 'PROPERTY42 = 1ULL << 42'
dans l'enum, int ne convient plus et on risque d'oublier de mettre à
jour le cast, tandis qu'avec le +, le compilateur se débrouille tout
seul pour trouver le type intégral qui convient (chaque type énuméré
possède un type intégral sous-jacent, suffisamment grand pour
représenter les valeurs énumérées).



Pour le mineur, pas de commentaire, c'est une histoire de gout.

Pour le majeur, c'est plus serieux. James mentionne dans un autre
post la possibilite de determiner le type entier sous-jacent avec
des templates (et je viens de decouvrir boost::integral_promotion),
mais c'est interessant de savoir qu'un simple '+' evite de sortir
l'artillerie lourde.
Avatar
James Kanze
On Mar 30, 10:09 pm, Falk Tannhäuser
wrote:
Guillaume GOURDIN schrieb:



[...]
inline Properties operator|(Properties a, Properties b)
{ // Note: Unary '+' forces integral promotion
return static_cast<Properties>(+a | +b);



Et est assez subtile qu'il faut un commentaire. Et donc, plus de
text qu'une conversion explicite.

En revanche, il évite le problème de nommer le type soujacent.
Qui dépend des valeurs dans l'enum et les tailles des types
entiers sur la machine en question. Je crois que je vais
l'adopter:-).

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
James Kanze
On Mar 31, 10:03 am, Michel Decima
wrote:
Falk Tannhäuser a écrit :



> enum Properties
> {
> PROPERTY1 = 1<<0,
> PROPERTY2 = 1<<1,
> PROPERTY3 = 1<<2,
> PROPERTY4 = 1<<3
> };



> inline Properties operator|(Properties a, Properties b)
> { // Note: Unary '+' forces integral promotion
> return static_cast<Properties>(+a | +b);
> }



Ca ne serait pas plus explicite d'utiliser static_cast<int>
pour la conversion en entier ?



Et si on ajoute un « propertyx = 1ULL << 48 » ?

J'avoue que sans le commentaire, je n'aurais pas compris tout
de suite...



Moi non plus. C'est sans doute pourquoi il a mis le commentaire.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
Michel Decima
James Kanze a écrit :
On Mar 31, 10:03 am, Michel Decima
wrote:
Falk Tannhäuser a écrit :



enum Properties
{
PROPERTY1 = 1<<0,
PROPERTY2 = 1<<1,
PROPERTY3 = 1<<2,
PROPERTY4 = 1<<3
};





inline Properties operator|(Properties a, Properties b)
{ // Note: Unary '+' forces integral promotion
return static_cast<Properties>(+a | +b);
}





Ca ne serait pas plus explicite d'utiliser static_cast<int>
pour la conversion en entier ?



Et si on ajoute un « propertyx = 1ULL << 48 » ?



On doit corriger controler et eventuellement corriger tous
les operateurs redefinis. Effectivement, on voudrait s'en passer.
Avatar
Michel Decima
James Kanze a écrit :
On Mar 30, 10:09 pm, Falk Tannhäuser
wrote:
Guillaume GOURDIN schrieb:



[...]
inline Properties operator|(Properties a, Properties b)
{ // Note: Unary '+' forces integral promotion
return static_cast<Properties>(+a | +b);



Et est assez subtile qu'il faut un commentaire. Et donc, plus de
text qu'une conversion explicite.

En revanche, il évite le problème de nommer le type soujacent.
Qui dépend des valeurs dans l'enum et les tailles des types
entiers sur la machine en question. Je crois que je vais
l'adopter:-).



Si on voulait nommer le type sous-jacent, pour une variable
temporaire par exemple, il faudrait sortir la solution a
base de template que tu as proposee, donc quelque chose
comme ceci :

Properties a = ... ;
boost::integral_promotion<Properties> i = a ;

au moins, c'est lisible. Mais si on combine l'astuce
du '+' avec le decltype de c++0x, on pourrait faire :

decltype( +a ) i = a ;

qui est tres sympathique pour ceux qui vont relire
le code plus tard ;)
Avatar
pjb
Michel Decima writes:

James Kanze a écrit :
On Mar 31, 10:03 am, Michel Decima
wrote:
Falk Tannhäuser a écrit :



enum Properties
{
PROPERTY1 = 1<<0,
PROPERTY2 = 1<<1,
PROPERTY3 = 1<<2,
PROPERTY4 = 1<<3
};





inline Properties operator|(Properties a, Properties b)
{ // Note: Unary '+' forces integral promotion
return static_cast<Properties>(+a | +b);
}





Ca ne serait pas plus explicite d'utiliser static_cast<int>
pour la conversion en entier ?



Et si on ajoute un « propertyx = 1ULL << 48 » ?



On doit corriger controler et eventuellement corriger tous
les operateurs redefinis. Effectivement, on voudrait s'en passer.



Franchement, est ce vraiment utile? Je ne vois pas l'intérêt
d'atteindre un tel niveau de fort typage, vu l'ampleur du travail que
ça demande.

Ne peut on se contenter d'un test:

void set_properties(int props){
if(props&(~ PROPERTY1|PROPERTY2|PROPERTY3|PROPERTY4|)){
throw("Unexpected bits in propos");
}
// ...
}

ou même simplement ignorer les bits non significatifs?

--
__Pascal Bourguignon__
Avatar
pjb
(Pascal J. Bourguignon) writes:
Franchement, est ce vraiment utile? Je ne vois pas l'intérêt
d'atteindre un tel niveau de fort typage, vu l'ampleur du travail que
ça demande.

Ne peut on se contenter d'un test:

void set_properties(int props){
if(props&(~ PROPERTY1|PROPERTY2|PROPERTY3|PROPERTY4|)){



Il manque peut être une paire de parentheses ici. Mettons:
if(props&(~(PROPERTY1|PROPERTY2|PROPERTY3|PROPERTY4|))){
pour être sur.

throw("Unexpected bits in propos");
}
// ...
}

ou même simplement ignorer les bits non significatifs?



--
__Pascal Bourguignon__
Avatar
Michel Decima
Pascal J. Bourguignon a écrit :
Michel Decima writes:

James Kanze a écrit :
On Mar 31, 10:03 am, Michel Decima
wrote:
Falk Tannhäuser a écrit :
enum Properties
{
PROPERTY1 = 1<<0,
PROPERTY2 = 1<<1,
PROPERTY3 = 1<<2,
PROPERTY4 = 1<<3
};
inline Properties operator|(Properties a, Properties b)
{ // Note: Unary '+' forces integral promotion
return static_cast<Properties>(+a | +b);
}


Ca ne serait pas plus explicite d'utiliser static_cast<int>
pour la conversion en entier ?


Et si on ajoute un « propertyx = 1ULL << 48 » ?


On doit corriger controler et eventuellement corriger tous
les operateurs redefinis. Effectivement, on voudrait s'en passer.



Franchement, est ce vraiment utile? Je ne vois pas l'intérêt
d'atteindre un tel niveau de fort typage, vu l'ampleur du travail que
ça demande.
Ne peut on se contenter d'un test:

void set_properties(int props){
if(props&(~ PROPERTY1|PROPERTY2|PROPERTY3|PROPERTY4|)){
throw("Unexpected bits in propos");
}
// ...
}



Avec ce prototype de fonction, l'argument props est un int, qui est
(souvent) trop petit pour contenir le PROPERTY48 suggere par James,
donc il faut revoir le prototype si on ajoute une telle valeur.
Si l'argument etait un Properties, on n'aurait pas eu le probleme.

On peut se contenter d'un typedef, mais ca laisse la possibilite
de faire n'importe quoi (genre +, -), et on ne peut plus surcharger
l'operateur d'affichage.

Apres, est-ce que ca vaut la peine, c'est une autre histoire.
Avatar
Mickaël Wolff
Guillaume GOURDIN wrote:


std::bitset avec enum peuvent ^etre tes amis.

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
1 2 3 4