2014-09-27 10 views
8

Ho alcune brevi funzioni constexpr nelle mie librerie che eseguono alcuni semplici calcoli. Li uso entrambi in contesti run-time e in fase di compilazione.Alternativa alle affermazioni per le funzioni di constexpr

Desidero eseguire alcune affermazioni nel corpo di queste funzioni, tuttavia assert(...) non è valido in una funzione constexpr e static_assert(...) non può essere utilizzato per controllare i parametri di funzione.

Esempio:

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

C'è un modo per controllo se la funzione è stata eseguita in un runtime o fase di compilazione costante ed eseguire la assert solo se è in esecuzione in fase di esecuzione ?

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: Ciò che intendevo è che 'static_assert' [non ha senso in questa occasione] (http://ideone.com/6yjdAE). –

+0

(Disclaimer: Sono un noob e non ho mai utilizzato un constexpr nella vita reale.) Basato sulla mia ricerca iniziale di Google, a meno che il compilatore non supporti [N3652] (http://www.open-std.org/jtc1/sc22/ wg21/docs/papers/2013/n3652.html) che rilassa il 'constexpr' di C++ 11, non può fare quello che chiedi. Una volta disponibile, sarai in grado di lanciare un'eccezione come 'std :: range_error' al posto di un' static_assert'. Puoi provare la tua mano usando [Clang 3.4 con std = C++ 14] (http://clang.llvm.org/cxx_status.html). – rwong

+0

il tuo reclamo non è vero per Clang in modalità '-std = C++ 1y', asserirà che funzionerà bene.dovresti tornare a 'C++ 11' se sei limitato a quello standard. – TemplateRex

risposta

4

Credo che assert funzionerà per voi una volta che g ++ implementa N3652, Relaxing constraints on constexpr functions. Attualmente, this status page indica che questo non è stato ancora implementato.

assert funziona (in funzioni constexpr) sull'attuale compilatore clang spedito da Apple, con -std=c++1y.

In questo momento, non vedo nulla nello standard che assicuri che assert funzioni nelle funzioni di constexpr e tale garanzia sarebbe una gradita aggiunta allo standard (almeno per me).

Aggiornamento

Richard Smith ha attirato la mia attenzione LWG 2234 presentata da Daniel Krugler che sta cercando di creare la certezza mi riferisco a sopra.

+1

+1 Ho usato assert tutto il codice constexpr e sono molto felice con esso – TemplateRex

+0

No, non funzionerà, perché la macro 'assert' deve eventualmente chiamare' abort' (di solito tramite funzioni intermedie come __assert_fail), che non è una funzione di constexpr. – o11c

+0

@ o11c Se 'assert' ha sempre chiamato' abort', sarebbe abbastanza rotto. – Potatoswatter

8

un'eccezione potrebbe essere utile come il compilatore ignorerà la parte di run-time quando si sa al momento della compilazione che l'eccezione non viene generata.

#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

Dopo aver giocato un po '(cercando di trovare un modo per sovraccaricare il lavoro 'constexpr'), penso che questo sia il modo migliore/unico per farlo. Ma il pensiero di lanciare da una funzione morsetto mi fa orrore ... –

5

Un perfezionamento sulla risposta di Daniel Frey è quello di utilizzare noexcept sulla funzione constexpr di trasformare l'errore di runtime in una chiamata a std::terminate. I fallimenti delle asserzioni sono irrecuperabili; dovrebbero fermare immediatamente il processo. Trasformarli in eccezioni è una pessima idea.

#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 
} 

L'errore di runtime per me assomiglia:

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

Speranza che aiuta.

+0

+1 Ho rimosso esplicitamente il 'noexcept', ma ha i suoi usi quindi grazie per averlo indicato. Certo, è una decisione che devi prendere, nel mio solito ambiente di lavoro, terminare il processo è qualcosa che cerchiamo di evitare ma ho molta simpatia per fallire presto, fallire duro :) –

Problemi correlati