2015-03-20 17 views
5

Sto cercando di fare una funzione constexpr che converte una stringa UUID come "f6ece560-cc3b-459a-87f1-22331582216e" ad una classe qualcosa di simile:costante parametro stringa espressione in funzione 11 constexpr C++

class UUID { 
public: 
     explicit UUID(uint8_t bytes[]); // Must be 16 byte array. 

Questo è quello che ho ottenuto finora:

// Compile time hex conversion of a single character into a nibble (half-byte). 
constexpr uint8_t hexToNibble(char a) 
{ 
    // Does not work: 
// static_assert(a >= '0' && a <= '9' || a >= 'a' && a <= 'f' || a >= 'A' && a <= 'F', "Invalid hex character"); 
    return a >= '0' && a <= '9' ? (a - '0') : 
      a >= 'a' && a <= 'f' ? (a - 'a' + 10) : 
      a >= 'A' && a <= 'F' ? (a - 'A' + 10) : 0; 
} 

// Compile time hex conversion of two characters into a byte. 
constexpr uint8_t hexToByte(char a, char b) 
{ 
    return (hexToNibble(a) << 4) + hexToNibble(b); 
} 

// Compile time string length. 
constexpr int strlenConst(const char* str) 
{ 
    return *str ? 1 + strlenConst(str + 1) : 0; 
} 

// Convert a UUID string to an array of bytes. 
// Example: "f6ece560-cc3b-459a-87f1-22331582216e". 
constexpr std::array<uint8_t, 16> UUIDFromString(const char* str) 
{ 
    // This does not work: 
// static_assert(strlenConst(str) == 36, "Invalid GUID length"); 

    return std::array<uint8_t, 16>{ 
     hexToByte(str[0], str[1]), 
     hexToByte(str[2], str[3]), 
     hexToByte(str[4], str[5]), 
     hexToByte(str[6], str[7]), 
     hexToByte(str[9], str[10]), 
     hexToByte(str[11], str[12]), 
     hexToByte(str[14], str[15]), 
     hexToByte(str[16], str[17]), 
     hexToByte(str[19], str[20]), 
     hexToByte(str[21], str[22]), 
     hexToByte(str[24], str[25]), 
     hexToByte(str[26], str[27]), 
     hexToByte(str[28], str[29]), 
     hexToByte(str[30], str[31]), 
     hexToByte(str[32], str[33]), 
     hexToByte(str[34], str[35]), 
    }; 
} 

#define MAKE_UUID(var, str) \ 
    static_assert(strlenConst(str) == 36, "Invalid GUID length for " #var); \ 
    const UUID var(UUIDFromString(str).data()); 

// Works but doesn't check string length. 
const UUID UUID_1(UUIDFromString("f6ece560-cc3b-459a-87f1-22331582216e").data()); 

// Checks string length but uses an ugly macro. 
MAKE_UUID(UUID_2, "f6ece560-cc3b-459a-87f1-22331582216e") 

Come potete vedere c'è un problema - a quanto pare è impossibile avere parametri di funzionalità che sono espressioni costanti in funzione constexpr, quindi non è possibile fare static_assert s sui parametri, anche se il valore che w come passato era un'espressione costante.

Quindi ho fatto ricorso a una macro per controllare la lunghezza della stringa e ho rinunciato a controllare i caratteri.

C'è un modo per aggirare questo? Inoltre, come posso garantire che questa funzione venga effettivamente valutata al momento della compilazione?

Modifica: Questo non è lo stesso di C++11 - static_assert within constexpr function? - o almeno le stesse risposte non funzionano - vedere i commenti di seguito.

Modifica 2: la risposta eccellente di Shafik funziona per il problema delle dimensioni, ma non per controllare i caratteri esadecimali. Per quanto mi riguarda posso dire che è impossibile - anche se si utilizza questo ...

// Compile time hex conversion of a single character into a nibble (half-byte). 
template<char a> 
constexpr uint8_t hexToNibble() 
{ 
    static_assert(a >= '0' && a <= '9' || a >= 'a' && a <= 'f' || a >= 'A' && a <= 'F', "Invalid hex character"); 
    return a >= '0' && a <= '9' ? (a - '0') : 
      a >= 'a' && a <= 'f' ? (a - 'a' + 10) : 
      a >= 'A' && a <= 'F' ? (a - 'A' + 10) : 0; 
} 

// Compile time hex conversion of two characters into a byte. 
template<char a, char b> 
constexpr uint8_t hexToByte() 
{ 
    return (hexToNibble<a>() << 4) + hexToNibble<b>(); 
} 

Questo non funziona:

// Convert a UUID string to an array of bytes. 
// Example: "f6ece560-cc3b-459a-87f1-22331582216e". 
template <size_t N> 
constexpr std::array<uint8_t, 16> UUIDFromString(const char (&str)[N]) 
{ 
    // Note you have to include the null byte. 
    static_assert(N == 37, "Invalid GUID length."); 

    return std::array<uint8_t, 16>{ 
     hexToByte<str[0], str[1]>(), 
     hexToByte<str[2], str[3]>(), 

Perché str[0] non è un'espressione costante.

+2

Eventuali duplicati di [C++ 11 - static_assert nella funzione constexpr?] (http://stackoverflow.com/questions/8626055/c11-static-assert-within-constexpr-function) Controlla la seconda risposta che utilizza i modelli. – AndyG

+0

@AndyG non funziona per questo caso poiché l'OP utilizza string letterali e non possono essere associati a template non paramter –

+0

@AndyG Considererei questo in realtà un problema molto più specifico del caso generale che si collega a. –

risposta

4

Come AndyG points out la domanda C++11 - static_assert within constexpr function? ci dice che un modo per farlo è utilizzare argomenti modello non di tipo che devono essere disponibili in fase di compilazione.

Il problema di questa soluzione è che il PO utilizza letterali stringa che can not be bound to non-type arguments:

In particolare, questo implica che stringhe, indirizzi degli elementi di matrice, e indirizzi di membri non statici non può essere utilizzato come argomenti del modello per creare un'istanza di modelli i cui parametri modello non di tipo corrispondente sono puntatori agli oggetti.

Un work-around per questo non è utilizzare direttamente la stringa letterale, ma l'uso della proprietà che è importante per il problema che è la lunghezza della matrice, come segue:

template <size_t N> 
constexpr std::array<uint8_t, 16> UUIDFromString(const char (&str)[N]) 
{ 
    static_assert(N == 36, "Invalid GUID length"); 

    //.... 
} 
+0

Funziona! Ho dovuto cambiare da 36 a 37 poiché suppongo che includa il terminatore null. Non riesco ancora a controllare i caratteri esadecimali, anche se utilizzo 'template uint8_t hexToNibble() {' e così via, perché non riesco a fare 'hexToByte (); 'perché' str [1] 'non è ancora un'espressione costante. Forse è impossibile. – Timmmm

+0

@Timmmm Mi scuso, ho completamente perso il primo 'static_assert', proverò a guardarlo più avanti oggi, ma forse non è risolvibile. –

2

Se vuoi il static_assert s, non hai altra scelta che parametrizzare il modello gli argomenti pertinenti.

È stato osservato che non è possibile associare un parametro stringa letterale a modello non di tipo , che è legalisticamente vero, ma è possibile farlo, con una piccola circonlocuzione.

Ecco uno schizzo:

#include <cstddef> 
#include <array> 

constexpr char const f6ece560_cc3b_459a_87f1_22331582216e[] = 
    "f6ece560-cc3b-459a-87f1-22331582216e"; 

constexpr int strlenConst(const char* str) 
{ 
    return *str ? 1 + strlenConst(str + 1) : 0; 
} 

template<char const * Hex> 
struct UUID { 
    static constexpr char const * hex = Hex; 
    static constexpr std::size_t len = strlenConst(hex); 
    static_assert(len == 36,"Invalid GUID length"); 
    template<char Ch> 
    static constexpr uint8_t hexToNibble() { 
     static_assert((Ch >= '0' && Ch <= '9') || (Ch >= 'a' && Ch <= 'f') 
        || (Ch >= 'A' && Ch <= 'F'), "Invalid hex character"); 
     return Ch >= '0' && Ch <= '9' ? (Ch - '0') : 
       Ch >= 'a' && Ch <= 'f' ? (Ch - 'a' + 10) : 
       Ch >= 'A' && Ch <= 'F' ? (Ch - 'A' + 10) : 0; 
    } 
    template<char First, char Second> 
    static constexpr uint8_t hexToByte() 
    { 
     return (hexToNibble<First>() << 4) + hexToNibble<Second>(); 
    } 
    static constexpr std::array<uint8_t, 16> get() { 
     return std::array<uint8_t, 16>{{ 
      hexToByte<hex[0], hex[1]>(), 
      hexToByte<hex[2], hex[3]>(), 
      hexToByte<hex[4], hex[5]>(), 
      hexToByte<hex[6], hex[7]>(), 
      hexToByte<hex[9], hex[10]>(), 
      hexToByte<hex[11], hex[12]>(), 
      hexToByte<hex[14], hex[15]>(), 
      hexToByte<hex[16], hex[17]>(), 
      hexToByte<hex[19], hex[20]>(), 
      hexToByte<hex[21], hex[22]>(), 
      hexToByte<hex[24], hex[25]>(), 
      hexToByte<hex[26], hex[27]>(), 
      hexToByte<hex[28], hex[29]>(), 
      hexToByte<hex[30], hex[31]>(), 
      hexToByte<hex[32], hex[33]>(), 
      hexToByte<hex[34], hex[35]>() 
     }}; 
    } 
}; 

#include <iostream> 

using namespace std; 

int main() 
{ 
    using some_uuid_t = UUID<f6ece560_cc3b_459a_87f1_22331582216e>; 
    cout << some_uuid_t::hex << endl; 
    auto uuid = some_uuid_t::get(); 
    for (auto const & byte : uuid) { 
     cout << int(byte); 
    } 
    cout << endl; 
    return 0; 
} 

(gcc 4.9.2/3.5.2 clang -std = C++ 11 -Wall -pedantic)

Problemi correlati