2014-09-27 8 views
8

Ich habe mehrere kurze constexpr Funktionen in meinen Bibliotheken, die einige einfache Berechnungen durchführen. Ich benutze sie sowohl in Laufzeit- als auch in Kompilierungskontexten.Alternative zu Constexpr Funktionen

ich einige Behauptungen im Körper dieser Funktionen ausführen möchte jedoch assert(...) in einer constexpr Funktion nicht gültig ist und static_assert(...) nicht verwendet werden kann, Funktionsparameter zu überprüfen.

Beispiel:

constexpr int getClamped(int mValue, int mMin, int mMax) noexcept 
{ 
    assert(mMin <= mMax); // does not compile! 
    return mValue < mMin ? mMin : (mValue > mMax ? mMax : mValue); 
} 

Gibt es eine Möglichkeit zu Check, ob die Funktion in einer Laufzeit oder kompilieren-Zeitkonstante und führen Sie die assert nur dann ausgeführt wird, wenn es während der Laufzeit ausgeführt wird, ist ?

constexpr int getClamped(int mValue, int mMin, int mMax) noexcept 
{ 
    assert_if_runtime(mMin <= mMax); 
    return mValue < mMin ? mMin : (mValue > mMax ? mMax : mValue); 
} 
+1

@dasblinkenlight: Was ich meinte ist, dass 'static_assert' [macht keinen Sinn in dieser Gelegenheit] (http://ideone.com/6yjdAE). –

+0

(Haftungsausschluss: Ich bin ein Noob und nie ein conetexpr im wirklichen Leben verwendet.) Basierend auf meiner ersten Google-Suche, es sei denn, Ihr Compiler unterstützt [N3652] (http://www.open-std.org/jtc1/sc22/ wg21/docs/papers/2013/n3652.html) die C++ 11 "constexpr" entspannt, kann es nicht tun, wonach Sie fragen. Sobald dies verfügbar ist, können Sie eine Ausnahme wie 'std :: range_error' anstelle eines' static_assert' auslösen. Sie können Ihre Hand mit [Clang 3.4 mit std = C++ 14] versuchen (http://clang.llvm.org/cxx_status.html). – rwong

+0

Ihr Anspruch ist nicht wahr für Clang im '-std = C++ 1y'-Modus, Assert wird gut funktionieren.Sie sollten auf "C++ 11" zurückschreiben, wenn Sie auf diesen Standard beschränkt sind. – TemplateRex

Antwort

4

Ich glaube, dass assert wird für Sie arbeiten einmal g ++ implementiert N3652, Relaxing constraints on constexpr functions. Derzeit zeigt this status page an, dass dies noch nicht implementiert wurde.

assert funktioniert (in conexpr Funktionen) auf dem aktuellen Compiler von Apple, mit -std=c++1y.

Zu diesem Zeitpunkt sehe ich nichts in der Norm, die sicherstellt, dass in constexpr Funktionen funktioniert, und eine solche Zusicherung wäre eine willkommene Ergänzung des Standards (zumindest von mir).

aktualisieren

Richard Smith lenkte meine Aufmerksamkeit auf LWG 2234 von Daniel Krügler vorgelegt, die die Sicherheit, ich oben verweisen zu erzeugen versucht.

+1

+1 Ich habe verwendet behauptet über constexpr code und bin sehr glücklich mit ihm – TemplateRex

+0

Nein, es wird nicht funktionieren, weil das '' '' '-Makiv schließlich" abbrechen "(in der Regel über Zwischenfunktionen wie __assert_fail) aufrufen muss, was keine constexpr-Funktion ist. – o11c

+0

@ o11c Wenn 'assert' immer' abort' genannt wird, wäre es ziemlich kaputt. – Potatoswatter

8

eine Ausnahme werfen könnte nützlich sein, wie der Compiler die Laufzeit Teil ignorieren, wenn es zum Zeitpunkt der Kompilierung weiß, dass die Ausnahme nicht ausgelöst.

#include <cassert> 

constexpr int getClamped(int mValue, int mMin, int mMax) 
{ 
    return (mMin <= mMax) ? 
      (mValue < mMin ? mMin : (mValue > mMax ? mMax : mValue)) : 
      throw "mMin must be less than or equal to mMax"; 
} 

int main(int argc, char** argv) 
{ 
    // These two work: 
    static_assert(getClamped(42, 0, 100) == 42, "CT"); 
    assert(getClamped(argc, 0, 100) == argc); 

    // Fails at compile-time: 
    // static_assert(getClamped(42, 100, 0) == 42, "CT"); 

    // Fails at run-time: 
    // assert(getClamped(argc, 100, 0) == argc); 
} 

Live example

+0

Nach einigem Herumspielen (Versuch, einen Weg zu finden, die constexpr-Arbeit zu überladen), denke ich, dass dies der beste/einzige Weg ist, dies zu tun. Aber der Gedanke, von einer Klammerfunktion zu werfen, erschreckt mich ... –

5

Eine Verfeinerung Antwort Daniel Frey ist noexcept auf der constexpr Funktion zu verwenden, um die Laufzeitfehler in einen Aufruf an std::terminate einzuschalten. Assertionsfehler sind nicht wiederherstellbar. Sie sollten den Prozess sofort stoppen. Sie zu Ausnahmen zu machen, ist eine sehr schlechte Idee.

#include <exception> 
#include <stdexcept> 

struct assert_failure 
    : std::logic_error 
{ 
    explicit assert_failure(const char *sz) 
     : std::logic_error(sz) 
    {} 
}; 

constexpr bool in_range(int i, int j, int k) noexcept 
{ 
    return (i <= j && j <= k) ? true : throw assert_failure("input not in range"); 
} 

int main(int argc, char* argv[]) 
{ 
    constexpr bool b1 = in_range(0, 4, 5); // OK! 
    constexpr bool b2 = in_range(0, 6, 5); // Compile-time error! 
    bool b3 = in_range(0, 4, argc);  // May or may not terminate the process 
} 

Der Laufzeitfehler für mich wie folgt aussieht:

terminate called after throwing an instance of 'assert_failure' 
    what(): input not in range 
Aborted (core dumped) 

Hoffnung, das hilft.

+0

+1 Ich habe das 'noexcept' explizit entfernt, aber es hat seinen Nutzen, also danke, dass du es herausgebracht hast. Natürlich, es ist eine Entscheidung, die Sie treffen müssen, in meiner gewohnten Arbeitsumgebung, die Beendigung des Prozesses ist etwas, was wir vermeiden wollen, aber ich habe viel Sympathie für scheitern früh, scheitern hart :) –