2016-05-16 18 views
0

Ich möchte eine einzelne Variable vom Typ U als ein Array von Typ-T-Elementen darstellen, zur Verwendung zur Kompilierzeit. sizeof (T) teilt die Größe von (U) perfekt, so dass k = sizeof (U)/sizeof (T) eine std::array<T, k> sein sollte.Wie sollte ich ein std :: Array <T, k> von einem U mit der Größe von (U) = k * sizeof (T) erstellen?

Problem ist, wie konstruiere ich es (noch einmal, zur Kompilierzeit)? Kann/soll ich verwenden Gießen, das heißt

* (reinterpret_cast<std::array<T,k> *>(&my_u)) 

oder vielleicht seine Elemente eine Art von rekursiven Funktionsaufruf Einstellung? Oder gibt es einen besseren/besseren Weg?

+1

Warum willst du das? – GManNickG

+0

Sound viel wie ein Bitfield oder vielleicht sogar eine Union. –

+0

Nicht möglich; Sie können zur Kompilierungszeit nicht 'interpretieren'. – ecatmur

Antwort

1

Der richtige Weg, um alias Wertdarstellungen ist fast immer memcpy zu verwenden:

std::array<T, k> my_t; 
static_assert(sizeof(my_t) == sizeof(my_u), "!!"); 
std::memcpy(my_t.data(), &my_u, sizeof(my_u)); 

Es gibt keine „schöner“ Art und Weise, wie Sie memcpy an einem gewissen Punkt verwenden, müssen strenge Aliasing Beschränkungen umgehen (es sei denn, T ist ein schmaler Zeichentyp).

Zur Kompilierzeit ist dies im Allgemeinen unmöglich, da der C++ Standard z. Beschreiben Sie das Layout von Bits innerhalb von Fließkommatypen oder ob ganzzahlige Typen Little-, Big- oder Mixed-Endian sind. Dies ist möglicherweise in einigen eingeschränkten Instanzen möglich, aber Sie können memcpy oder reinterpret_cast nicht verwenden, daher müssen Sie typspezifischen Code schreiben, um einzeln auf die Mitglieder von U zuzugreifen.

Der beste Weg, um Ihren Code strukturieren könnte sein, eine Sammlung von Funktionen zu schreiben, Werte vom Typ T von einem Wert vom Typ U bei einem spezifischen extra Offset:

constexpr T getValueOfTAtOffset(U u, std::integral_constant<std::size_t, 0>) { ... } 
constexpr T getValueOfTAtOffset(U u, std::integral_constant<std::size_t, 1>) { ... } 
// ... 

Sie würden sie rufen Sie dann ein index_sequence mit Schabloneninferenz:

template<std::size_t... I> 
constexpr std::array<T, sizeof...(I)> asArrayOfTImpl(U u, std::integer_sequence<std::size_t, I...>) { 
    return {getValueOfTAtOffset(u, std::integral_constant<std::size_t, I>{})}; 
} 
constexpr std::array<T, k> asArrayOfT(U u) { 
    return asArrayOfTImpl(u, std::make_index_sequence<k>{}); 
} 
+0

Siehe Bearbeiten - ich meinte zur Kompilierzeit. Kein 'memcpy()'. – einpoklum

+0

Der C++ - Standard geht nicht, aber man geht davon aus, dass es sich um nicht-portablen maschinenabhängigen Code handelt, und ich kenne das Bit-Layout und die Endianz. – einpoklum

+0

@einpoklum und es ist "constexpr"? Was ist die Unterschrift? – ecatmur

0

C++ (und C) hat eine Funktion, die als strenges Aliasing bekannt ist.

Strenges Aliasing besagt, dass ein Zeiger auf T und ein Zeiger auf U niemals Zeiger auf dieselben Daten sind.

Ein Grund für diese Regel ist, dass sie eine ganze Reihe von Optimierungen zulässt. Wenn Sie eine int haben und Sie eine short* dereferenzieren und sie ändern, können Sie wissen, dass die int nicht geändert wurde, ohne Zeiger verfolgen zu müssen.

Was bedeutet, dass Sie nur eine zu einem Y*werfen umdeuten kann, wenn der Zeiger ursprünglich eigentlich einY* war, oder eine der execptions gelten.

Diese Ausnahmen sind Y Typ char, oder die Layout-Kompatibilität garantiert, dass grundsätzlich in bestimmten Standard-Layout-Fällen über eine Struktur ein Präfix eines anderen zu sprechen.

Im allgemeinen Fall gelten diese nicht.Sie müssen also entweder eine dieser Ausnahmen suchen oder memcpy verwenden, um Ihre Daten mithilfe einer dieser Ausnahmen als standardkonform zu kennzeichnen.

Das Risiko besteht darin, dass aggressive Compiler-Optimierungen die augenscheinliche Bedeutung Ihres Codes bei offensichtlichem undefiniertem Verhalten erheblich verändern können. Selbst wenn Sie es testen und es funktioniert, ist die nächste Compiler-Version berechtigt, Ihren Code auf verschiedene Arten und Weisen zu brechen, oder ein harmloses Compiler-Option-Flag oder irgendetwas anderes.

Eine Option, die Sie tun können, ist eine union erstellen. Aber selbst dort ist der Zugriff auf den "falschen" Teil der Gewerkschaft ein undefiniertes Verhalten, abgesehen von den Standard-Layout-Garantien.

Ein spezifischerer Satz von Informationen über Ihre Daten könnte tatsächlich einen Weg dahin bieten - möglicherweise erfüllen Sie beispielsweise die Standard-Layout-Garantien -, aber sie sind leicht zu knacken.