Như tôi dường như là một tay quay duy nhất trong quan tâm của tôi trong câu hỏi này, tôi đã cranked ra một câu trả lời cho bản thân mình, với một tập tin header cơ bản như thế này:
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
Tiêu đề này chỉ để bao gồm trong một bộ thử nghiệm, và sau đó nó sẽ làm cho hiển thị định nghĩa lại vĩ mô của static_assert
chỉ hiển thị khi bộ kiểm tra được xây dựng với
`-DTEST__EXCEPTIONALIZE_STATIC_ASSERT=1`
Việc sử dụng các thiết bị này có thể được phác thảo với một món đồ chơi mẫu thư viện:
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
Hãy thử tưởng tượng rằng mã là đủ lớn và phức tạp mà bạn không thể ở một chiếc mũ chỉ khảo sát và chọn ra số static_assert
s và thỏa mãn bản thân rằng bạn biết lý do tại sao họ ở đó và họ đáp ứng mục đích thiết kế của họ. Bạn đặt niềm tin vào thử nghiệm hồi quy.
đây sau đó là một món đồ chơi hồi quy kiểm tra bộ cho 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
Bạn có thể biên dịch test.cpp
với ít nhất gcc 6.1, kêu vang 3.8 và tùy chọn -std=c++14
hoặc VC++ 19.10.24631.0 và tùy chọn /std:c++latest
. Làm như vậy trước tiên mà không cần xác định TEST__EXCEPTIONALIZE_STATIC_ASSERT
(hoặc xác định nó = 0). Sau đó chạy và đầu ra nên là:
Test: 1
Test: 2
Test: 3
Passed 3 out of 3 tests
*** Success :)
Nếu bạn sau đó lặp lại, nhưng biên dịch với -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 :)
Rõ ràng mã hóa lặp đi lặp lại của try/catch
khối trong tĩnh khẳng định trường hợp thử nghiệm là tẻ nhạt , nhưng trong bối cảnh của một khung công tác kiểm tra đơn vị thực sự và đáng kính, sẽ mong đợi nó đóng gói bộ máy kiểm tra ngoại lệ để tạo ra những thứ như vậy trong tầm nhìn của bạn. Trong googletest, ví dụ, bạn có thể viết những thứ tương tự của:
TYPED_TEST(t_my_template,insist_non_zero_increase)
{
ASSERT_THROW(TypeParam::template increase<0>(1),
exceptionalized_static_assert);
}
Bây giờ tôi có thể lấy lại tính toán của tôi về ngày Armageddon :)
cảm giác ruột cá nhân của tôi là tĩnh khẳng định nên bắt lỗi lập trình * trước * bạn thậm chí chạy bất kỳ thử nghiệm nào. Tôi không thấy ngay lập tức một lý do bức xúc tại sao họ nên tự kiểm tra ... –
Những gì Kerrek nói. Nếu môi trường xây dựng của bạn không có "kiểm tra không xây dựng" như là một thất bại, đó là một vấn đề. – Xeo
Tôi không thấy tốt hơn là tạo macro xuất ra static_assert hoặc ngoại lệ thời gian chạy trong chế độ thử nghiệm. Trong trường hợp lập trình meta, nó có thể tạo cảm giác để kiểm tra các xác nhận biên dịch của bạn. –