2015-05-11 7 views
6

Ich bin auf der Suche nach einer Standardbibliothek oder Boost-Funktion, die verlustfrei eine Nummer in einen anderen primitiven Typ umwandeln kann und mich irgendwie darüber informiert, ob die Besetzung verlustfrei war (oder eine Ausnahme auslöst). Hier sind einige Beispiele:Bietet Boost oder die Standardbibliothek eine Möglichkeit, zu überprüfen, ob ein Cast verlustfrei ist?

auto x = lossless_cast<double>(1u); // ok, double can represent 1 
auto x = lossless_cast<int>(1.2); // fail, int can't represent 1.2 
auto x = lossless_cast<int>(1E200); // fail, int can't represent 1E200 

boost::numeric_cast in der Nähe kommt, dass es Abgüsse abholen, die aus dem numerischen Bereich des Zieltyps fallen, aber nicht, wenn sie lossless, aber innerhalb der Zieltyp (siehe meine 2. Beispiel).

Es gibt eine SO question for the C language which provides some hand-rolled solutions to this problem, aber ich bin nach einer boost oder Standard Library-Lösung grundsätzlich mit folgenden Funktionen:

template <typename out, typename in> 
out lossless_cast(in in_value) 
{ 
    out out_value = static_cast<out>(in_value); 

    if (static_cast<in>(out_value) != in_value) 
    throw; // some exception 

    return out_value; 
} 

Existiert diese Funktionalität?

+4

Schreiben Sie eine Funktion, die den Cast ausführt, und vergleichen Sie Vorher und Nachher für die Gleichheit. –

+3

Wenn Sie eine Lösung ohne Guss-Roundtrips erwarten, könnte dies ein schwierigeres Problem sein, als Sie feststellen. Ein 'float' kann zum Beispiel nicht 16.777.217 darstellen. – zneak

+2

@IgorTandetnik: keine gute Idee - in einigen Fällen gibt das * undefined Verhalten * (z. B. für die 'Lossless_cast (1E200);' Fall in der Frage). –

Antwort

3

Ich bin mir ziemlich sicher, dass im Standard nichts vorgedreht ist, und nichts von Boost bemerkt, aber es ist eine große Bibliothek. Jede Implementierung mit Gießen muss vorsichtig sein, das undefinierte Verhaltens für Out-of-Range-Werte pro 4.9 [conv.fpint], sondern als boost::numeric_cast<> angeblich Handles, könnten Sie verwenden:

template <typename U, typename T> 
inline U is_lossless(T t) 
{ 
    U u = boost::numeric_cast<U>(t); 
    T t2 = boost::numeric_cast<T>(u); // throw if now out-of-range 
    if (t != t2) 
     throw whatever-you-like; 
    return u; 
} 

Die Notwendigkeit numeric_cast<> wenn t2 erholt ist am wenigsten offensichtlich: es wird sichergestellt, dass u noch in Reichweite ist, wie es für einen Wert Besetzung von etwa int64_t x-double y um erfolgreich zu sein, aber mit einem Integralwert in y angenähert wird als ist außerhalb des zulässigen Bereichs für ein int64_t, wie möglich ist, dass Rückübertragung von double hat undefiniertes Verhalten.

Die Legalität/Robustheit des Obigen erfordert, dass boost::numeric_cast<> korrekt undefiniertes Verhalten vermeidet, was ich nicht verifiziert habe.

+0

Ich bin ziemlich sicher, dass die Absicht von' boost :: numeric_cast' ist um die UB Fälle zu vermeiden. Ob es in der aktuellen Implementierung tatsächlich gelingt, ist schwerer zu sagen; http://StackOverflow.com/q/25857843/ – Nemo

+0

@Nemo: Sie würden hoffen, dass dies ihre Absicht war, obwohl es möglicherweise schnell (im Sinne von CPU-Zyklen) schwer zu tun ist und sie die undefinierten Verhaltensweisen gefunden haben könnten auf den spezifischen CPUs, die Boost-Targeted waren akzeptabel für ihre Portabilität Anforderungen, ohne Standard-mandatierte portable über alle möglichen Ziele. Ich habe gerade den Code auf meinem PC gelesen und werde Ihren Link überprüfen - Prost. –

+1

Da der Typ von 'T' aus dem Parameter abgeleitet werden kann, würde es einen Wert geben, um' typename U' zum * ersten * Template-Parameter zu machen? Das würde eine "Casting" -Syntax ermöglichen, die ähnlich aussieht wie Boost und die Kernsprache, denke ich. –

Verwandte Themen