Jak wydaje się być samotny korbowego w moim interesie w tej kwestii mam wygiętą się odpowiedź dla siebie, z plikiem nagłówkowym zasadzie jak ten:
exceptionalized_static_assert.h
#ifndef TEST__EXCEPTIONALIZE_STATIC_ASSERT_H
#define TEST__EXCEPTIONALIZE_STATIC_ASSERT_H
/* Conditionally compilable apparatus for replacing `static_assert`
with a runtime exception of type `exceptionalized_static_assert`
within (portions of) a test suite.
*/
#if TEST__EXCEPTIONALIZE_STATIC_ASSERT == 1
#include <string>
#include <stdexcept>
namespace test {
struct exceptionalized_static_assert : std::logic_error
{
exceptionalized_static_assert(char const *what)
: std::logic_error(what){};
virtual ~exceptionalized_static_assert() noexcept {}
};
template<bool Cond>
struct exceptionalize_static_assert;
template<>
struct exceptionalize_static_assert<true>
{
explicit exceptionalize_static_assert(char const * reason) {
(void)reason;
}
};
template<>
struct exceptionalize_static_assert<false>
{
explicit exceptionalize_static_assert(char const * reason) {
std::string s("static_assert would fail with reason: ");
s += reason;
throw exceptionalized_static_assert(s.c_str());
}
};
} // namespace test
// A macro redefinition of `static_assert`
#define static_assert(cond,gripe) \
struct _1_test \
: test::exceptionalize_static_assert<cond> \
{ _1_test() : \
test::exceptionalize_static_assert<cond>(gripe){}; \
}; \
_1_test _2_test
#endif // TEST__EXCEPTIONALIZE_STATIC_ASSERT == 1
#endif // EOF
Ten nagłówek jest uwzględniany tylko w zestawie testowym, a następnie spowoduje, że będzie widoczny jako definicja redefinicji static_assert
widoczna tylko wtedy, gdy zestaw testów zostanie zbudowany z
`-DTEST__EXCEPTIONALIZE_STATIC_ASSERT=1`
Zastosowanie tego urządzenia może być zarysowane biblioteki zabawka Szablon:
my_template.h
#ifndef MY_TEMPLATE_H
#define MY_TEMPLATE_H
#include <type_traits>
template<typename T>
struct my_template
{
static_assert(std::is_pod<T>::value,"T must be POD in my_template<T>");
explicit my_template(T const & t = T())
: _t(t){}
// ...
template<int U>
static int increase(int i) {
static_assert(U != 0,"I cannot be 0 in my_template<T>::increase<I>");
return i + U;
}
template<int U>
static constexpr int decrease(int i) {
static_assert(U != 0,"I cannot be 0 in my_template<T>::decrease<I>");
return i - U;
}
// ...
T _t;
// ...
};
#endif // EOF
Spróbuj sobie wyobrazić, że kod jest wystarczająco duże i skomplikowane, że cię Nie możesz tego porzucić, po prostu wypytaj i wybierz static_assert
s oraz , aby się upewnić, że wiesz, dlaczego one są i czy spełniają one ich celów projektowych. Zaufałeś testom regresyjnym.
Tutaj następnie jest zabawką regresji Zestaw testów dla my_template.h
:
test.cpp
#include "exceptionalized_static_assert.h"
#include "my_template.h"
#include <iostream>
template<typename T, int I>
struct a_test_template
{
a_test_template(){};
my_template<T> _specimen;
//...
bool pass = true;
};
template<typename T, int I>
struct another_test_template
{
another_test_template(int i) {
my_template<T> specimen;
auto j = specimen.template increase<I>(i);
//...
(void)j;
}
bool pass = true;
};
template<typename T, int I>
struct yet_another_test_template
{
yet_another_test_template(int i) {
my_template<T> specimen;
auto j = specimen.template decrease<I>(i);
//...
(void)j;
}
bool pass = true;
};
using namespace std;
int main()
{
unsigned tests = 0;
unsigned passes = 0;
cout << "Test: " << ++tests << endl;
a_test_template<int,0> t0;
passes += t0.pass;
cout << "Test: " << ++tests << endl;
another_test_template<int,1> t1(1);
passes += t1.pass;
cout << "Test: " << ++tests << endl;
yet_another_test_template<int,1> t2(1);
passes += t2.pass;
#if TEST__EXCEPTIONALIZE_STATIC_ASSERT == 1
try {
// Cannot instantiate my_template<T> with non-POD T
using type = a_test_template<int,0>;
cout << "Test: " << ++tests << endl;
a_test_template<type,0> specimen;
}
catch(test::exceptionalized_static_assert const & esa) {
++passes;
cout << esa.what() << endl;
}
try {
// Cannot call my_template<T>::increase<I> with I == 0
cout << "Test: " << ++tests << endl;
another_test_template<int,0>(1);
}
catch(test::exceptionalized_static_assert const & esa) {
++passes;
cout << esa.what() << endl;
}
try {
// Cannot call my_template<T>::decrease<I> with I == 0
cout << "Test: " << ++tests << endl;
yet_another_test_template<int,0>(1);
}
catch(test::exceptionalized_static_assert const & esa) {
++passes;
cout << esa.what() << endl;
}
#endif // TEST__EXCEPTIONALIZE_STATIC_ASSERT == 1
cout << "Passed " << passes << " out of " << tests << " tests" << endl;
cout << (passes == tests ? "*** Success :)" : "*** Failure :(") << endl;
return 0;
}
// EOF
można skompilować test.cpp
przynajmniej gcc 6.1, brzękiem 3.8 i opcja -std=c++14
lub VC++ 19.10.24631.0 i opcja /std:c++latest
. Zrób to najpierw bez definiowania TEST__EXCEPTIONALIZE_STATIC_ASSERT
(lub definiowania go jako 0). Następnie należy uruchomić i wyjście powinno być:
Test: 1
Test: 2
Test: 3
Passed 3 out of 3 tests
*** Success :)
Jeśli następnie powtórzyć, ale skompilować z -DTEST__EXCEPTIONALIZE_STATIC_ASSERT=1
,
Test: 1
Test: 2
Test: 3
Test: 4
static_assert would fail with reason: T must be POD in my_template<T>
Test: 5
static_assert would fail with reason: I cannot be 0 in my_template<T>::increase<I>
Test: 6
static_assert would fail with reason: I cannot be 0 in my_template<T>::decrease<I>
Passed 6 out of 6 tests
*** Success :)
Wyraźnie powtarzające kodowanie try/catch
bloków w statycznej-dochodzić przypadków testowych jest nudny , ale w warunkach prawdziwego i godnego szacunku modelu testu jednostkowego można się spodziewać, że zapakuje on aparat do testowania wyjątków w celu generowania takich rzeczy poza zasięgiem wzroku. W googletest, na przykład, jesteś w stanie napisać jak się z:
TYPED_TEST(t_my_template,insist_non_zero_increase)
{
ASSERT_THROW(TypeParam::template increase<0>(1),
exceptionalized_static_assert);
}
teraz mogę wrócić do moich obliczeń daty Armagedonu :)
Moja osobista przeczucie, że statyczny twierdzi powinien złapać błędy w programowaniu * przed * przeprowadzasz nawet testy. Nie widzę natychmiast pilnego powodu, dla którego powinni oni zostać przetestowani ... –
Co mówi Kerrek. Jeśli twoje środowisko kompilacji nie bierze "testów nie zbudowało" jako niepowodzenia, to jest problem. – Xeo
Nie widzę lepiej niż tworzenie makra, które wyprowadza albo static_assert, albo wyjątek środowiska wykonawczego w trybie testowym. W przypadku metaprogramowania, może to sprawić, że przetestujesz swoje asercje kompilacji. –