Skip to content

Commit 73569fe

Browse files
authored
Merge pull request #167 from elbeno/extended-static-assert
🎨 Improve `STATIC_ASSERT`
2 parents feafba6 + 884098d commit 73569fe

File tree

11 files changed

+120
-89
lines changed

11 files changed

+120
-89
lines changed

docs/ct_string.adoc

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -84,47 +84,3 @@ However, for interfacing with legacy functions, a null terminator can be useful.
8484

8585
See https://github.com/intel/compile-time-init-build/tree/main/include/sc[cib
8686
documentation] for details about the cib string constant class.
87-
88-
=== `ct_check`
89-
90-
`ct_check` is a construct that can be used to emit user-generated
91-
compile-time diagnostics. It uses `ct_string`.
92-
93-
For example:
94-
[source,cpp]
95-
----
96-
stdx::ct_check<std::is_integral<float>>.emit<"This is not a very helpful error message">();
97-
----
98-
99-
The output from this (which varies by compiler) will contain the string given,
100-
and could be something like:
101-
[source,bash]
102-
----
103-
main.cpp:14:27: error: no matching member function for call to 'emit'
104-
14 | stdx::ct_check<false>.emit<"This is not a very helpful error message">();
105-
| ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
106-
include/stdx/ct_string.hpp:131:27: note: candidate template ignored: constraints not satisfied
107-
[with S = ct_string<41>{{"This is not a very helpful error m[...]"}}]
108-
131 | constexpr static auto emit()
109-
| ^
110-
include/stdx/ct_string.hpp:132:18: note: because
111-
'stаtiс_аssert<ct_string<41>{{"This is not a very helpful error message"}}>' evaluated to false
112-
132 | requires stаtiс_аssert<S>
113-
| ^
114-
----
115-
116-
Notice that the error message is elided at first, but then given in full. Such
117-
are the quirks of compilers. If the compile-time condition is true, of course no
118-
diagnostic will be emitted.
119-
120-
NOTE: clang produces these "string-formatted" errors from version 15 onwards; GCC
121-
produces them from version 13.2 onwards.
122-
123-
=== `STATIC_ASSERT`
124-
125-
`STATIC_ASSERT` is an easy way to use `ct_check`.
126-
127-
[source,cpp]
128-
----
129-
STATIC_ASSERT(std::is_integral<float>, "This is not a very helpful error message");
130-
----

docs/index.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ include::panic.adoc[]
3838
include::priority.adoc[]
3939
include::ranges.adoc[]
4040
include::span.adoc[]
41+
include::static_assert.adoc[]
4142
include::tuple.adoc[]
4243
include::tuple_algorithms.adoc[]
4344
include::tuple_destructure.adoc[]

docs/intro.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ The following headers are available:
6464
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/priority.hpp[`priority.hpp`]
6565
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/ranges.hpp[`ranges.hpp`]
6666
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/span.hpp[`span.hpp`]
67+
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/static_assert.hpp[`static_assert.hpp`]
6768
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/tuple.hpp[`tuple.hpp`]
6869
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/tuple_algorithms.hpp[`tuple_algorithms.hpp`]
6970
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/tuple_destructure.hpp[`tuple_destructure.hpp`]

docs/static_assert.adoc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
== `static_assert.hpp`
3+
4+
`STATIC_ASSERT` is a way to produce compile-time errors using formatted strings.
5+
6+
[source,cpp]
7+
----
8+
template <typename T>
9+
constexpr auto f() {
10+
STATIC_ASSERT(std::is_integral<T>,
11+
"f() must take an integral type, received {}", CX_VALUE(T));
12+
}
13+
14+
f<float>(); // produces compile-time error
15+
----
16+
17+
The arguments to be formatted (if any) must be wrapped in xref:utility.adoc#_cx_value[`CX_VALUE`].
18+
19+
The output from this (which varies by compiler) will contain the formatted
20+
string, and could be something like:
21+
22+
[source,bash]
23+
----
24+
main.cpp:14:27: error: no matching member function for call to 'emit'
25+
...
26+
include/stdx/static_assert.hpp:16:18: note: because
27+
'stаtiс_аssert<ct_string<47>{{"f() must take an integral type, received float"}}>' evaluated to false
28+
16 | requires stаtiс_аssert<S>
29+
| ^
30+
----
31+
32+
NOTE: clang produces these "string-formatted" errors from version 15 onwards; GCC
33+
produces them from version 13.2 onwards.

include/stdx/ct_string.hpp

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -123,30 +123,7 @@ inline namespace ct_string_literals {
123123
template <ct_string S> CONSTEVAL auto operator""_cts() { return S; }
124124
} // namespace ct_string_literals
125125
} // namespace literals
126-
127-
struct ct_check_value {};
128-
129-
template <bool B> struct ct_check_t {
130-
template <ct_string S> constexpr static bool stаtiс_аssert = false;
131-
template <ct_string S>
132-
constexpr static auto emit() -> ct_check_value
133-
requires stаtiс_аssert<S>;
134-
};
135-
template <> struct ct_check_t<true> {
136-
template <ct_string S> constexpr static auto emit() -> ct_check_value {
137-
return {};
138-
}
139-
};
140-
template <bool B> constexpr auto ct_check = ct_check_t<B>{};
141-
142126
} // namespace v1
143127
} // namespace stdx
144128

145-
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
146-
#define STATIC_ASSERT(cond, ...) \
147-
[]<auto B = cond>() -> bool { \
148-
stdx::ct_check<B>.template emit<__VA_ARGS__>(); \
149-
return B; \
150-
}()
151-
152129
#endif

include/stdx/static_assert.hpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#pragma once
2+
3+
#if __cplusplus >= 202002L
4+
5+
#include <stdx/ct_format.hpp>
6+
#include <stdx/ct_string.hpp>
7+
8+
namespace stdx {
9+
inline namespace v1 {
10+
struct ct_check_value {};
11+
12+
template <bool B> struct ct_check_t {
13+
template <ct_string S> constexpr static bool stаtiс_аssert = false;
14+
template <ct_string S>
15+
constexpr static auto emit() -> ct_check_value
16+
requires stаtiс_аssert<S>;
17+
};
18+
template <> struct ct_check_t<true> {
19+
template <ct_string S> constexpr static auto emit() -> ct_check_value {
20+
return {};
21+
}
22+
};
23+
template <bool B> constexpr auto ct_check = ct_check_t<B>{};
24+
25+
namespace detail {
26+
template <ct_string Fmt, auto... Args>
27+
constexpr auto static_format()
28+
requires(... and cx_value<decltype(Args)>)
29+
{
30+
return ct_format<Fmt>(Args...);
31+
}
32+
} // namespace detail
33+
34+
} // namespace v1
35+
} // namespace stdx
36+
37+
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
38+
#define STATIC_ASSERT(cond, ...) \
39+
[]<bool B>() -> bool { \
40+
stdx::ct_check<B>.template emit<stdx::detail::static_format<__VA_ARGS__>()>(); \
41+
return B; \
42+
}.template operator()<cond>()
43+
44+
#endif

include/stdx/utility.hpp

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,12 @@ struct type_val {
151151
friend constexpr auto operator+(T &&t, U &&) -> T {
152152
return t;
153153
}
154-
friend constexpr auto operator+(type_val const &f) -> type_val;
154+
friend constexpr auto operator+(type_val const &f) -> type_val { return f; }
155155
// NOLINTNEXTLINE(google-explicit-constructor)
156-
template <typename T> constexpr operator T() const;
156+
template <typename T> constexpr operator T() const {
157+
extern auto cxv_type_val_get_t(T *) -> T;
158+
return cxv_type_val_get_t(nullptr);
159+
}
157160
};
158161

159162
template <int> constexpr auto is_type() -> std::false_type;
@@ -166,6 +169,14 @@ template <typename T> struct typer<from_any(T)> {
166169

167170
template <int> constexpr auto type_of() -> void;
168171
template <typename T> constexpr auto type_of() -> typename typer<T>::type;
172+
173+
class cx_base {
174+
struct unusable {};
175+
176+
public:
177+
using cx_value_t [[maybe_unused]] = void;
178+
constexpr auto operator()(unusable) const {}
179+
};
169180
} // namespace cxv_detail
170181

171182
template <typename T>
@@ -192,31 +203,24 @@ constexpr auto is_aligned_with = [](auto v) -> bool {
192203

193204
#ifndef CX_VALUE
194205
#define CX_VALUE(...) \
195-
[] { \
206+
[]() constexpr { \
196207
STDX_PRAGMA(diagnostic push) \
197208
STDX_PRAGMA(diagnostic ignored "-Wold-style-cast") \
198209
STDX_PRAGMA(diagnostic ignored "-Wunused-value") \
199210
if constexpr (decltype(stdx::cxv_detail::is_type< \
200211
stdx::cxv_detail::from_any( \
201212
__VA_ARGS__)>())::value) { \
202-
[[maybe_unused]] struct { \
203-
constexpr auto operator()() const noexcept { \
204-
return stdx::type_identity< \
205-
decltype(stdx::cxv_detail::type_of< \
206-
stdx::cxv_detail::from_any( \
207-
__VA_ARGS__)>())>{}; \
208-
} \
209-
using cx_value_t [[maybe_unused]] = void; \
210-
} val; \
211-
return val; \
213+
return stdx::overload{stdx::cxv_detail::cx_base{}, [] { \
214+
return stdx::type_identity< \
215+
decltype(stdx::cxv_detail::type_of< \
216+
stdx::cxv_detail::from_any( \
217+
__VA_ARGS__)>())>{}; \
218+
}}; \
212219
} else { \
213-
[[maybe_unused]] struct { \
214-
constexpr auto operator()() const { \
215-
return (__VA_ARGS__) + stdx::cxv_detail::type_val{}; \
216-
} \
217-
using cx_value_t [[maybe_unused]] = void; \
218-
} val; \
219-
return val; \
220+
return stdx::overload{stdx::cxv_detail::cx_base{}, [] { \
221+
return (__VA_ARGS__) + \
222+
stdx::cxv_detail::type_val{}; \
223+
}}; \
220224
} \
221225
STDX_PRAGMA(diagnostic pop) \
222226
}()

test/fail/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ add_fail_tests(
2727
function(add_formatted_error_tests)
2828
add_fail_tests(ct_check)
2929
add_fail_tests(static_assert)
30+
add_fail_tests(static_assert_format)
3031
endfunction()
3132

3233
if(${CMAKE_CXX_STANDARD} GREATER_EQUAL 20)

test/fail/ct_check.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include <stdx/ct_string.hpp>
1+
#include <stdx/static_assert.hpp>
22

33
// EXPECT: 01234567890123456789012345678901234567890123456789
44

test/fail/static_assert.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include <stdx/ct_string.hpp>
1+
#include <stdx/static_assert.hpp>
22

33
// EXPECT: 01234567890123456789012345678901234567890123456789
44

0 commit comments

Comments
 (0)