2009-03-31 13 views
13

ich eine bekannte Vorlage bin mit Binärkonstanten ermöglichenC++ binäre Konstante/literal

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

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

So können Sie so etwas wie binäre < 101011011> :: Wert tun. Leider hat dies eine Grenze von 20 Ziffern für eine unsigned long long.

Hat jemand eine bessere Lösung?

+0

Die Grenze von 20 ist wahrscheinlich Compiler abhängig. Es hängt davon ab, wie viel Template-Rekursion es tolerieren wird. Bei einigen modernen Compilern können Sie ein Argument übergeben, um die maximale Template-Rekursionstiefe festzulegen. –

+3

Ich dachte, dass das Limit auf die Anzahl der Dezimalziffern zurückzuführen ist, die Sie in einem unsigned long long speichern könnten, da es im Grunde genommen die * dezimale * Zahl 101011011 nimmt und diese in eine binäre Zahl umwandelt, ja? – paxdiablo

+0

Pax: Ja, zumindest für GCC, die ich benutze. – Unknown

Antwort

25

Ist diese Arbeit, wenn Sie haben eine führende Null auf dem binären Wert? Eine führende Null macht die Oktalkonstante statt der Dezimalzahl.

Das führt zu einem Weg, um ein paar mehr Ziffern aus dieser Lösung zu quetschen - immer starten Sie Ihre Binärkonstante mit einer Null! Dann ersetzen Sie die 10 in Ihrer Vorlage durch 8.

+2

Ooh, das ist hinterhältig :-) Bravo. – paxdiablo

+1

Danke Jungs, du hast meine Nacht gemacht! –

4

C++ 0x hat user-defined literals, die verwendet werden könnte, um zu implementieren, worüber Sie sprechen.

Ansonsten weiß ich nicht, wie man diese Vorlage verbessert.

+0

Können Sie ein Beispiel geben. – Unknown

+0

sicher, hier ist ein Beispiel: http://StackOverflow.com/Questions/537303/Binary-Literals/538101#538101 –

5

Die Ansätze, die ich immer benutzt habe, obwohl wie bei Ihnen nicht so elegant:

1/Nur hex verwenden. Nach einer Weile erfahren Sie, welche Hexadezimalziffern welche Bitmuster darstellen.

2/Konstante verwenden und ODER hinzufügen. Zum Beispiel (kann Qualifier auf dem Bitmuster müssen sie ohne Vorzeichen oder lang machen):

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

unsigned long x = b2 | b7 

3/Wenn die Leistung nicht kritisch ist und die Lesbarkeit wichtig ist, können Sie es nur zur Laufzeit mit einer Funktion wie "x = fromBin (" 101011011 ");".

4/Als hinterhältige Lösung könnten Sie einen Pre-Pre-Prozessor schreiben, der Ihre * .cppme-Dateien durchläuft und die * .cpp-Dateien erstellt, indem alle Strings vom Typ "0b101011011" durch die entsprechenden "0x15b" ersetzt werden. Zeichenfolgen). Ich würde das nicht leichtfertig machen, da es viele knifflige Kombinationen von Syntax gibt, um die Sie sich sorgen müssen. Aber es würde Ihnen erlauben, Ihre Zeichenfolge so zu schreiben, wie Sie wollen, ohne sich um die Launen des Compilers kümmern zu müssen, und Sie könnten die Syntaxtricks durch sorgfältiges Codieren einschränken.

Natürlich ist der nächste Schritt, nachdem das würde GCC werden Patchen „0b“ Konstanten zu erkennen, aber das kann zuviel des Guten :-)

+0

lustig, dass Sie den letzten Teil erwähnt. Ich benutzte auch Bitset <> (string (str)). To_ulong() – Unknown

+2

Ich frage mich, was die Situation ist, die die Verwendung der 'binären Vorlage' besser als nur einfache Hexadezimalkonstanten oder 'or-ing' zusammen enums mit Eigennamen für die Bits, wenn Sie Hardware oder Kommunikationsprotokolle modellieren? –

+0

Tatsächlich unterstützt GCC 0b Konstanten. –

3

Sie können mehr nicht-Typ Template-Parameter zu „simulieren“ zusätzliche Bits:

// 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 } ; 
}; 

Sie diese nach wie vor verwenden können, zum Beispiel:

binary<1001101>::value 

Sie können aber auch die folgenden Befehle verwenden äquivalente Formen:

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

Grundsätzlich sind die zusätzlichen Parameter geben Ihnen weitere 20 Bits zu spielen. Sie könnten bei Bedarf noch weitere Parameter hinzufügen.

Da der Stellenwert der zweiten Zahl verwendet wird, um herauszufinden, wie weit nach links die erste Zahl verschoben werden muss, muss die zweite Zahl mit einer 1 beginnen. (Dies ist ohnehin erforderlich, da sie mit a beginnt 0 würde dazu führen, dass die Zahl als Oktalzahl interpretiert wird.

)
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; 
}; 

erlauben:

Bin :: O :: I :: I :: O :: O :: Wert

viel ausführlicher, aber keine Grenzen gesetzt (bis Sie die Größe getroffen von einem unsigned int natürlich).

+0

Sneaky! Aber wird das nicht ein bisschen zu viel sein, anstatt nur hex einzugeben? – LiraNuna

+4

Offensichtliche Erweiterung wäre "Bin :: OOOO :: IIOO :: IIIO", die auch viel leichter zu lesen ist. – MSalters

3

Technisch ist es nicht C noch C++, ist es eine spezifische Erweiterung GCC, aber GCC ermöglicht binary constants wie gesehen here:

The following statements are identical: 

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

Hoffnung, das hilft. Einige Intel-Compiler und ich bin sicher, andere implementieren einige der GNU-Erweiterungen. Vielleicht hast du Glück.

2

Eine einfache #define funktioniert sehr gut:

#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) 

Nicht meine Erfindung, ich sah es auf einem Forum vor langer Zeit.