2017-07-29 5 views
2

Ich möchte eine effiziente Möglichkeit schreiben, 0 und 1 in einem Byte (oder einem anderen Typ) zu schreiben.Wie schreibe ich eine Bitmaske in C++ 14 mit (so etwas wie?) Variadic Vorlagen

Zum Beispiel in C wir so etwas wie schreiben:

uint8_t x = 0x00; 
x|= (1 << 2) | (1 << 4); 

schreiben 1 in Bits 2 und 4 (natürlich, verwenden Sie nicht 2 und 4, aber Makros verwenden, um das erinnern Bedeutung der Bits 2 und 4).

Ich mag diese Vorgehensweise nicht, so schreibe ich die folgende variadische Vorlage:

template<typename T> 
T bitmask(T p0) 
{ 
    return (1 << p0); 
} 

template<typename T, typename...Position> 
T bitmask(T p0, Position... p1_n) 
{ 
    return (1 << p0)|bit_mask(p1_n...); 
} 



template<typename T, typename... Position> 
T& write_one(T& x, Position... pos0_n) 
{ 
    x|= bit_mask(pos0_n...); 

    return x; 
} 

und diese Arbeit gut. Sie können etwas wie dieses schreiben:

uint8_t x = 0x00; 
write_one(x, 2, 4); 

Aber ich hatte eine andere Lösung bevorzugt. Ich möchte so etwas schreiben:

write_one<uint8_t>(x, 2, 4); // if x is uint8_t 
write_one<uint16_t>(x, 2, 4); // if x is uint16_t 

Die Art der write_one ist die Art von x (ok, ich kenne Sie nicht die Art uint8_t und uint16_t schreiben müssen, ist es für mich Klarheit geschrieben). Die anderen Parameter sind immer Zahlen (tatsächlich sind sie uint8_t).

Wie kann ich diese erreichen?

Ich möchte Code wie folgt schreiben:

template<typename T> 
T bitmask(uint8_t p0) 
{ 
    return (1 << p0); 
} 

template<typename T> 
T bitmask(T p0, uint8_t... p1_n) 
{ 
    return (1 << p0)|bit_mask<T>(p1_n...); 
} 

template<typename T> 
T& write_one(T& x, uint8_t... pos0_n) 
{ 
    x|= bit_mask<T>(pos0_n...); 

    return x; 
} 

Ihnen sehr danken.

+0

haben Sie den Assembler Ausgang geprüft, was Sie bisher getan haben? Sie werden überrascht sein, wie gut der Compiler ist –

Antwort

3

Beide Methoden erzeugen genau die gleiche hochoptimierten Assembler:

#include <utility> 

template <int...bits, class Int> 
constexpr auto set_bits_a(Int i) 
{ 
    using expand = int[]; 
    void(expand{ 
     0, 
     ((i |= (Int(1) << bits)),0)... 
    }); 
    return i; 
} 

template <class Int, class...Bits> 
constexpr auto set_bits_b(Int i, Bits...bits) 
{ 
    using expand = int[]; 
    void(expand{ 
     0, 
     ((i |= (Int(1) << bits)),0)... 
    }); 
    return i; 
} 

int get_value(); 
volatile int x, y; 

int main() 
{ 
    x = set_bits_a<1, 3, 5>(get_value()); 
    y = set_bits_b(get_value(), 1, 3, 5); 
} 

Ausgang:

main: 
     sub  rsp, 8 
     call get_value() 
     or  eax, 42     ; <-- completely optimised 
     mov  DWORD PTR x[rip], eax 
     call get_value() 
     or  eax, 42     ; <-- completely optimised 
     mov  DWORD PTR y[rip], eax 
     xor  eax, eax 
     add  rsp, 8 
     ret 
y: 
x: 

https://godbolt.org/g/CeNRVw

+0

Vielen Dank. Aber ich verstehe nicht die Bedeutung von: void (expand { 0, ((i | = (Int (1) << Bits)), 0) ... }); Warum wird ein Casting ungültig? Und das ..., in diesem Fall, was ist es? – Antonio

+0

@Antonio die '...' heißt eine Elipsis. Seine Funktion besteht darin, das variadische Argument zu erweitern. Ich werfe in void um Compiler-Warnungen über nicht verwendete Ausdrücke zu vermeiden. Die Verwendung von 'expand' ist notwendig bis C++ 17, wenn wir Faltoperatoren haben werden. –

+0

Danke, Richard. – Antonio

Verwandte Themen