2016-06-16 10 views
12

Ich möchte eine constexpr Funktion erstellen, die die endianness des Systems gibt, etwa so:Endianness in constexpr

constexpr bool IsBigEndian() 
{ 
    constexpr int32_t one = 1; 
    return (reinterpret_cast<const int8_t&>(one) == 0); 
} 

Nun, da die Funktion wird eher bei der Kompilierung ausgeführt werden sollen, als auf der tatsächlichen Zielmaschine, Welche Garantie gibt die C++ - Spezifikation, um sicherzustellen, dass das korrekte Ergebnis zurückgegeben wird?

+0

Gute Frage! Dies war ein Problem (andere Compiler für andere Sprachen) w.r.t. Gleitkommakonstanten/Ausdrücke und Kreuzkompilierer (damals noch, als es verschiedene Gleitkommaformate gab). Ich würde gerne die C++ Standardantwort kennen. – davidbak

+0

Harte praktische Bedeutung. Ich kann mir zwei Architekturen nicht vorstellen, die dieselbe kompilierte Quelle ausführen können, sich aber in der Endianz unterscheiden. – SergeyA

+1

@ πάντα ῥεῖ: Das ist nicht diese Frage überhaupt. Hier geht es um einen spezifischen Fall der Erkennung von Endian-Ness. –

Antwort

9

Keine. In der Tat ist das Programm schlecht geformt. Von [expr.const]:

Ein bedingter-Ausdruck e ist ein Kern konstanter Ausdruck, es sei denn die Auswertung von e, nach den Regeln der abstrakten Maschine (1,9), würde eine der folgenden Ausdrücke auswerten:
- [...]
- ein reinterpret_cast.
- [...]

Und von [dcl.constexpr]:

Für eine constexpr Funktion oder constexpr Konstruktor, der weder notleidenden noch eine Vorlage ist, wenn kein Argument Werte vorhanden sind so dass ein Aufruf der Funktion oder des Konstruktors ein ausgewerteter Unterausdruck von eines Kernkonstantenausdrucks (5.20) sein könnte, oder, für einen Konstruktor, eines Konstanteninitialisierers für ein Objekt (3.6.2), das Programm schlecht gebildet ist; keine Diagnose erforderlich.


Die Art und Weise, dies zu tun, ist nur zu hoffen, dass Ihr Compiler schön genug ist, Makros für die Endian Ihrer Maschine zur Verfügung zu stellen. Zum Beispiel auf gcc, konnte ich __BYTE_ORDER__ verwenden:

constexpr bool IsBigEndian() { 
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 
    return false; 
#else 
    return true; 
#endif 
} 
+0

Es ist nicht der Grund, warum es schlecht geformt ist. Der wahre Grund, warum es schlecht geformt ist, ist die Verletzung der strengen Aliasing-Regeln. Was du zitierst, bedeutet nur, dass "IsBigEndian" nicht "constexpr" ist. –

+3

@uhohsomebodyneedsapupper Ein Verstoß gegen das strikte Aliasing ist UB. Eine "constexpr" -Funktion, die nicht als const-Ausdruck ausgewertet werden kann, ist schlecht ausgebildet. – Barry

+3

@uhohsomebodyneedsapupper gibt es nicht unbedingt eine Verletzung. int8_t könnte ein Zeichen sein und Aliasing durch char * ist erlaubt. – SergeyA

2

Wie Barry erwähnt, Ihr Code ist nicht legal C++. Aber selbst wenn Sie den constexpr Teil weggenommen haben, wäre es immer noch nicht legal C++. Ihr Code verletzt strenge Aliasing-Regeln und repräsentiert daher undefiniertes Verhalten.

Tatsächlich gibt es in C++ keine Möglichkeit, die Endian-Eigenschaft eines Objekts zu erkennen, ohne undefiniertes Verhalten aufzurufen. Casting es zu einem char* funktioniert nicht, weil der Standard keine große oder kleine Endian-Reihenfolge erfordert. Während Sie also die Daten durch ein Byte lesen könnten, wären Sie nicht in der Lage, irgendetwas von diesem Wert abzuleiten.

Und geben Sie punning durch union schlägt fehl, weil Sie nicht über in C++ schreiben überhaupt Wortspiel dürfen. Und selbst wenn Sie es wieder getan haben, beschränkt C++ Implementierungen nicht auf große oder kleine Endian-Reihenfolge.

Was also C++ als Standard betrifft, gibt es keine Möglichkeit, dies zu erkennen, sei es zur Kompilierzeit oder zur Laufzeit.

+0

Aber warum? Es ist kein undefiniertes Verhalten! Es ist * nicht definiert * Verhalten, und diese sind unterschiedlich. OP tut genau das - er greift auf die zugrunde liegende Repräsentation zu (was erlaubt ist) und trifft dann eine Entscheidung, die auf Repräsentation basiert. Ich sehe UB hier nicht. – SergeyA

+3

@SergeyA: Verhalten, das nicht definiert ist, ist per Definition * undefiniert. Genau das bedeutet das Wort "undefiniert": nicht definiert. Außerdem stellt sich die Frage, was der * Standard * über einen solchen Code sagt. Die Antwort ist (abgesehen davon, dass sie nicht legal ist), dass der Standard das Verhalten dieser Funktion nicht definiert. –

+0

Ich stimme nicht zu. Der gute Test von undefined vs nicht definiert ist folgen - kann es Nasal Demons spawnen? (Oder, realistischer ausgedrückt, kann der Compiler einen Code erzeugen, der nicht logisch dem C++ - Code entspricht). Bei undefiniertem Verhalten kann es. Bei nicht definiertem Verhalten kann es nicht. Insbesondere würden im gegebenen Fall keine Nasen-Dämonen beschworen werden, stattdessen würde eine oder eine 0 basierend auf der Darstellung von Boolean zurückgegeben werden. – SergeyA