2009-03-31 7 views
13

binaire J'utilise un modèle bien connu pour permettre des constantes binairesC++ constante/literal

template< unsigned long long N > 
struct binary 
{ 
    enum { value = (N % 10) + 2 * binary<N/10> :: value } ; 
}; 

template<> 
struct binary<0> 
{ 
    enum { value = 0 } ; 
}; 

vous pouvez donc faire quelque chose comme binaire < 101011011> :: valeur. Malheureusement, cela a une limite de 20 chiffres pour un long non signé.

Est-ce que quelqu'un a une meilleure solution?

+0

La limite de 20 dépend probablement du compilateur. Cela dépend de la quantité de récursion que le modèle peut tolérer. Certains compilateurs modernes vous permettront de passer un argument pour définir la profondeur de récursivité maximale du modèle. –

+3

Je pensais que la limite était due au nombre de chiffres décimaux que vous pourriez stocker dans un long sans signe, puisqu'il prend en gros le * décimal * numéro 101011011 et le transforme en binaire, oui? – paxdiablo

+0

Pax: oui, au moins pour GCC que j'utilise. – Unknown

Répondre

25

Est-ce que ce travail si vous avez un zéro sur votre valeur binaire? Un zéro en tête fait l'octal constant plutôt que la décimale.

Ce qui conduit à un moyen d'extraire quelques chiffres de plus de cette solution - toujours démarrer votre constante binaire avec un zéro! Puis remplacez les 10 dans votre modèle avec 8.

+2

Ooh, c'est sournois :-) Bravo. – paxdiablo

+1

Merci les gars, vous avez fait ma nuit! –

4

C++ 0x a user-defined literals, ce qui pourrait être utilisé pour implémenter ce dont vous parlez.

Sinon, je ne sais pas comment améliorer ce modèle.

+0

Pouvez-vous donner un exemple. – Unknown

+0

bien sûr, voici un exemple: http://stackoverflow.com/questions/537303/binary-literals/538101#538101 –

5

Les approches que je l'ai toujours utilisé, mais pas aussi élégant que le vôtre:

1/Il suffit d'utiliser hex. Après un moment, vous venez de savoir quels chiffres hexadécimaux représentent quels modèles de bits.

2/Utilisez les constantes et OU ou ajoutez-les. Par exemple (peut avoir besoin de qualification sur les modèles de bits pour les rendre non signé ou long):

#define b0 0x00000001 
#define b1 0x00000002 
: : : 
#define b31 0x80000000 

unsigned long x = b2 | b7 

3/Si les performances ne sont pas critiques et la lisibilité est importante, il vous suffit de le faire lors de l'exécution d'une fonction telle que "x = fromBin (" 101011011 ");". 4/Comme une solution sournoise, vous pouvez écrire un pré-pré-processeur qui passe par vos fichiers * .cppme et crée les * .cpp en remplaçant toutes les chaînes de type "0b101011011" par leur équivalent "0x15b" cordes). Je ne ferais pas cela à la légère car il y a toutes sortes de combinaisons de syntaxe que vous pourriez avoir à s'inquiéter. Mais cela vous permettrait d'écrire votre chaîne comme vous voulez sans avoir à vous soucier des caprices du compilateur, et vous pourriez limiter la complexité de la syntaxe en codant soigneusement.

Bien sûr, l'étape suivante après serait PATCHING GCC pour reconnaître les constantes de « 0b », mais cela peut être un :-) surpuissant

+0

drôle que vous avez mentionné la dernière partie. J'utilisais aussi bitset <> (string (str)). To_ulong() – Unknown

+2

Je me demande quelle est la situation qui fait que l'utilisation du 'modèle binaire' vaut mieux que de simples constantes hexadécimales ou des énumérations 'ou -ing' avec des noms propres pour les bits si vous modélisez du matériel ou des protocoles de communication? –

+0

En fait, GCC supporte les constantes 0b. –

3

Vous pouvez ajouter des paramètres de modèle non-type de "simuler" bits supplémentaires:

// Utility metafunction used by top_bit<N>. 
template <unsigned long long N1, unsigned long long N2> 
struct compare { 
    enum { value = N1 > N2 ? N1 >> 1 : compare<N1 << 1, N2>::value }; 
}; 

// This is hit when N1 grows beyond the size representable 
// in an unsigned long long. It's value is never actually used. 
template<unsigned long long N2> 
struct compare<0, N2> { 
    enum { value = 42 }; 
}; 

// Determine the highest 1-bit in an integer. Returns 0 for N == 0. 
template <unsigned long long N> 
struct top_bit { 
    enum { value = compare<1, N>::value }; 
}; 

template <unsigned long long N1, unsigned long long N2 = 0> 
struct binary { 
    enum { 
     value = 
      (top_bit<binary<N2>::value>::value << 1) * binary<N1>::value + 
      binary<N2>::value 
    }; 
}; 

template <unsigned long long N1> 
struct binary<N1, 0> { 
    enum { value = (N1 % 10) + 2 * binary<N1/10>::value }; 
}; 

template <> 
struct binary<0> { 
    enum { value = 0 } ; 
}; 

Vous pouvez l'utiliser comme avant, par exemple:

binary<1001101>::value 

Mais vous pouvez aussi utiliser les éléments suivants formes équivalentes:

binary<100,1101>::value 
binary<1001,101>::value 
binary<100110,1>::value 

Fondamentalement, le paramètre supplémentaire vous donne 20 autres bits à jouer avec. Vous pouvez ajouter encore plus de paramètres si nécessaire. Comme la valeur de position du second nombre est utilisée pour déterminer à quelle distance vers la gauche le premier nombre doit être décalé, le deuxième nombre doit commencer par un 1. (Ceci est nécessaire de toute façon, depuis le début avec un 0 ferait que le nombre soit interprété comme un nombre octal.)

3
template<unsigned int p,unsigned int i> struct BinaryDigit 
{ 
    enum { value = p*2+i }; 
    typedef BinaryDigit<value,0> O; 
    typedef BinaryDigit<value,1> I; 
}; 
struct Bin 
{ 
    typedef BinaryDigit<0,0> O; 
    typedef BinaryDigit<0,1> I; 
}; 

Permettre:

Bin :: O :: I :: I :: :: O O :: valeur

beaucoup plus bavard, mais pas de limites (jusqu'à atteindre la taille d'un int non signé bien sûr).

+0

Sournois! mais ne sera-ce pas un peu trop au lieu de simplement taper hex? – LiraNuna

+4

L'extension évidente serait 'Bin :: OOOO :: IIOO :: IIIO', ce qui est sans doute beaucoup plus facile à lire. – MSalters

3

Techniquement, il est pas C ni C++, il est une extension spécifique GCC, mais GCC permet binary constants comme on le voit here:

The following statements are identical: 

i =  42; 
i =  0x2a; 
i =  052; 
i = 0b101010; 

espoir qui aide. Certains compilateurs Intel et j'en suis sûr, d'autres implémentent certaines des extensions GNU. Peut-être que tu as de la chance.

2

Un #define simple fonctionne très bien:

#define HEX__(n) 0x##n##LU 

#define B8__(x) ((x&0x0000000FLU)?1:0)\ 
       +((x&0x000000F0LU)?2:0)\ 
       +((x&0x00000F00LU)?4:0)\ 
       +((x&0x0000F000LU)?8:0)\ 
       +((x&0x000F0000LU)?16:0)\ 
       +((x&0x00F00000LU)?32:0)\ 
       +((x&0x0F000000LU)?64:0)\ 
       +((x&0xF0000000LU)?128:0) 

#define B8(d) ((unsigned char)B8__(HEX__(d))) 
#define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) + B8(dlsb)) 
#define B32(dmsb,db2,db3,dlsb) (((unsigned long)B8(dmsb)<<24) + ((unsigned long)B8(db2)<<16) + ((unsigned long)B8(db3)<<8) + B8(dlsb)) 

B8(011100111) 
B16(10011011,10011011) 
B32(10011011,10011011,10011011,10011011) 

Pas mon invention, je l'ai vu sur un forum il y a longtemps.