2009-10-18 10 views
20

Una domanda comune che si presenta di tanto in tanto nel mondo della programmazione C++ è la determinazione in fase di compilazione di endianness. Di solito questo è fatto con #ifdefs appena trasportabili. Ma la parola chiave C++ 11 constexpr insieme alla specializzazione del modello ci offre una soluzione migliore?constexpr and endianness

sarebbe legale C++ 11 a fare qualcosa di simile:

constexpr bool little_endian() 
{ 
    const static unsigned num = 0xAABBCCDD; 
    return reinterpret_cast<const unsigned char*> (&num)[0] == 0xDD; 
} 

E poi specializzarsi un modello per entrambi i tipi di Endian:

template <bool LittleEndian> 
struct Foo 
{ 
    // .... specialization for little endian 
}; 

template <> 
struct Foo<false> 
{ 
    // .... specialization for big endian 
}; 

e poi fare:

Foo<little_endian()>::do_something(); 

risposta

12

Supponendo che N2116 sia la dicitura che viene incorporata, il tuo esempio è mal formato (notare che il non c'è alcun concetto di "legale/illegale" in C++). Il testo proposto per [decl.constexpr]/3 dice

  • sua funzione-organismo è un composto-enunciato della forma { return expression; } cui espressione è un'espressione potenziale costante (5,19);

La funzione viola il requisito in quanto dichiara inoltre una variabile locale.

Modifica: questa limitazione può essere superata spostando num all'esterno della funzione. La funzione non sarebbe ancora ben formato, quindi, perché espressione deve essere un'espressione potenziale costante, che è definito come

Un'espressione è un'espressione potenziale costante se è una costante espressione quando tutte le occorrenze dei parametri di funzione sono sostituiti da espressioni costanti arbitrarie del tipo appropriato.

IOW, reinterpret_cast<const unsigned char*> (&num)[0] == 0xDD dovrebbe essere un'espressione costante. Tuttavia, non lo è: &num sarebbe una costante-espressione di indirizzo (5.19/4). L'accesso al valore di tale puntatore non è tuttavia consentito per un'espressione costante:

L'operatore di sottoscrizione [] e l'accesso di membro della classe. e gli operatori , gli operatori unari & e * e i valori di puntatore (eccetto dynamic_casts, 5.2.7) possono essere utilizzati nella creazione di un'espressione costante di indirizzo , ma il valore di un oggetto non deve essere raggiunto dall'uso di questi operatori.

Modifica: il testo precedente è di C++ 98. Apparentemente, C++ 0x è più permissivo su ciò che consente espressioni costanti.L'espressione implica una conversione lvalue a rvalue del riferimento matrice, che è vietata dalle espressioni costanti salvo

viene applicato ad un lvalue di tipo integrale efficace che si riferisce a una variabile const non volatile o statico membro dati inizializzato con espressioni costanti

non è chiaro se (&num)[0] "si riferisce a" una variabile const, o se solo un letterale num "si riferisce a" una tale variabile. Se (&num)[0] fa riferimento a tale variabile, non è chiaro se reinterpret_cast<const unsigned char*> (&num)[0] si riferisca ancora a "num".

+0

Non credo si applichi, qui. La variabile statica è costante stessa. – GManNickG

+0

La formulazione in 4.1 di N2116 afferma che il corpo della funzione deve avere solo un'istruzione (che è l'istruzione return). Intendiamoci, dal mio sguardo veloce sul testo, non vedo nulla che vieta il codice precedente se num è definito globalmente. – GRB

+0

@GMan: come dice GRB, la bozza è abbastanza chiara che deve avere solo una dichiarazione e una dichiarazione * è * una dichiarazione (C++ 98, 6.7, dichiarazione Dichiarazione). @GRB: modificherò la mia risposta per discutere di spostare la costante al di fuori della funzione. –

4

Questa è una domanda molto interessante.

Non sono un avvocato linguistico, ma potresti essere in grado di sostituire reinterpret_cast con un sindacato.

const union { 
    int int_value; 
    char char_value[4]; 
} Endian = { 0xAABBCCDD }; 

constexpr bool little_endian() 
{ 
    return Endian[0] == 0xDD; 
} 
+1

Inserire un valore in un'unione quindi accedere al sindacato tramite un altro membro non è valido. – GManNickG

+9

@GMan: è ben formato, ma richiama il comportamento non definito. "valido" non è una proprietà definita nello standard C++. –

+0

Sì, ho buttato giù la mia terminologia. Grazie per aver indicato i termini corretti. – GManNickG

-3

Se il vostro obiettivo è quello di assicurare che il compilatore ottimizza little_endian() in una costante vera o falsa a tempo di compilazione, senza che nessuno dei suoi contenuti liquidazione nel file eseguibile o in corso di esecuzione in fase di esecuzione, e solo la generazione di codice da il "corretto" uno dei tuoi due modelli , temo che tu abbia una delusione.

anche io non sono un avvocato di lingua, ma mi sembra constexpr è come inline o register: una parola chiave che avvisa lo scrittore compilatore per la presenza di un potenziale di ottimizzazione. Quindi spetta allo scrittore del compilatore se avvantaggiarlo o no. Le specifiche della lingua impongono tipicamente comportamenti, non ottimizzazioni.

Inoltre, avete effettivamente provato questo su una varietà di compilatori di reclami C++ 0x per vedere cosa succede? Direi che la maggior parte di loro potrebbe soffocare i tuoi doppi modelli, dal momento che non saranno in grado di capire quale usare se invocato con false.

+0

Non è esattamente la stessa cosa. Il risultato di una funzione 'constexpr' generalmente può essere usato dove è richiesta un'espressione costante, ad es. i limiti dell'array. Anche se credo che ci sia un margine di manovra nel caso di template di funzioni. –

6

Non è possibile determinare l'endianness in fase di compilazione utilizzando constexpr. reinterpret_cast è esplicitamente vietato da [expr.const] p2, come il suggerimento di iain di leggere da un membro non attivo di un sindacato.

+0

++ .. Sono arrivato a questo dopo aver esaminato [questa domanda] (http://stackoverflow.com/q/27053453/1708801). –

0

Questo può sembrare barare, ma si può sempre includere endian.h ... BYTE_ORDER == BIG_ENDIAN è un constexpr valida ...

9

sono stato in grado di scrivere questo:

#include <cstdint> 

class Endian 
{ 
private: 
    static constexpr uint32_t uint32_ = 0x01020304; 
    static constexpr uint8_t magic_ = (const uint8_t&)uint32_; 
public: 
    static constexpr bool little = magic_ == 0x04; 
    static constexpr bool middle = magic_ == 0x02; 
    static constexpr bool big = magic_ == 0x01; 
    static_assert(little || middle || big, "Cannot determine endianness!"); 
private: 
    Endian() = delete; 
}; 

L'ho provato con g ++ e si compila senza avvisi. Dà un risultato corretto su x64. Se possiedi un procesor big-endian o middle-endian, per favore, conferma che questo funzioni per te in un commento.

+0

cos'è 'const uint8_t &' – Nick

+0

@Nick È un riferimento a un numero intero senza segno a 8 bit costante. –

+0

capisco, ma qual è il vantaggio di tale cast? perché non solo uint8_t senza const e ref? – Nick