2014-09-27 27 views
8

Mam kilka krótkich funkcji constexpr w moich bibliotekach, które wykonują proste obliczenia. Używam ich zarówno w czasie wykonywania, jak i podczas kompilacji.Alternatywa dla funkcji constexpr

chciałbym wykonać pewne twierdzenia w organizmie tych funkcji, jednak assert(...) nie jest ważny w constexpr funkcji i static_assert(...) nie może być używana do sprawdzania parametrów funkcji.

Przykład:

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

Czy istnieje sposób wyboru czy funkcja jest wykonywany w czasie wykonywania lub w czasie kompilacji stała i wykonać assert tylko jeśli jest wykonywane w czasie wykonywania ?

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: Chodzi o to, że 'static_assert' [nie ma sensu w tym przypadku] (http://ideone.com/6yjdAE). –

+0

(Zastrzeżenie: jestem noobem i nigdy nie używałem constexpr w prawdziwym życiu.) Na podstawie mojej początkowej wyszukiwarki Google, chyba że Twój kompilator obsługuje [N3652] (http://www.open-std.org/jtc1/sc22/ wg21/docs/papers/2013/n3652.html), który rozluźnia "constexpr" w C++ 11, nie może zrobić tego, o co prosisz. Gdy to będzie możliwe, będziesz mógł rzucić wyjątek, taki jak 'std :: range_error' zamiast' static_assert'. Możesz spróbować swoich sił używając [Clang 3.4 ze std = C++ 14] (http://clang.llvm.org/cxx_status.html). – rwong

+0

Twoje roszczenie nie jest prawdziwe w przypadku Clanga w trybie '-std = C++ 1y', assert będzie działało dobrze.powinieneś odwołać się do 'C++ 11', jeśli jesteś ograniczony do tego standardu. – TemplateRex

Odpowiedz

4

Wierzę, że assert będzie działać dla ciebie, gdy g ++ implementuje N3652, Relaxing constraints on constexpr functions. Obecnie this status page wskazuje, że nie zostało to jeszcze zaimplementowane.

assert działa (w funkcjach constexpr) na bieżącym kompilatorze clang dostarczanym przez Apple, z -std=c++1y.

W tej chwili nie widzę niczego w standardzie, który zapewnia, że ​​assert będzie działać w funkcjach constexpr, a takie zapewnienie byłoby mile widzianym dodatkiem do standardu (przynajmniej przeze mnie).

Aktualizacja

Richard Smith zwrócił moją uwagę na LWG 2234 przekazywanych przez Daniel Krügler który próbuje utworzyć pewność odsyłam do wyżej.

+1

+1 Używam twierdzeń na całym kodzie constexpr i jestem bardzo z niego zadowolony – TemplateRex

+0

Nie, to nie zadziała, ponieważ makro 'assert' musi ostatecznie wywołać' abort' (zwykle poprzez pośrednie funkcje, takie jak __assert_fail), które nie jest funkcją constexpr. – o11c

+0

@ o11c Jeśli 'assert' zawsze nazywa się' abort', byłoby całkiem zepsute. – Potatoswatter

8

Throwing wyjątek może być przydatna jako kompilator zignoruje część run-time, kiedy wie, w czasie kompilacji, że wyjątek nie zostanie rzucony.

#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

Po pewnym pobycie (próbując znaleźć sposób na przeciążenie pracy 'constexpr'), myślę, że jest to najlepszy/jedyny sposób, aby to zrobić. Ale myśl o rzucaniu z funkcji zacisku przeraża mnie ... –

5

Udoskonalenie na odpowiedź Daniela Frey jest wykorzystanie noexcept funkcję constexpr obrócić błąd wykonania na wezwanie do std::terminate. Niepowodzenia asercji są nieodwracalne; powinni natychmiast zatrzymać proces. Przekształcenie ich w wyjątki to bardzo zły pomysł.

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

Runtime error dla mnie wygląda następująco:

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

nadzieję, że pomoże.

+0

+1 Wyraźnie usunąłem "noexcept", ale ma on swoje zastosowania, więc dziękuję za wskazanie tego. Oczywiście, jest to decyzja, którą musisz podjąć, w moim zwykłym środowisku pracy, zakończenie procesu jest czymś, czego staramy się unikać, ale mam dużo współczucia dla niepowodzenia wcześnie, nieudane :) –

Powiązane problemy