Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🎨 Improve STATIC_ASSERT #167

Merged
merged 1 commit into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 0 additions & 44 deletions docs/ct_string.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -84,47 +84,3 @@ However, for interfacing with legacy functions, a null terminator can be useful.

See https://github.com/intel/compile-time-init-build/tree/main/include/sc[cib
documentation] for details about the cib string constant class.

=== `ct_check`

`ct_check` is a construct that can be used to emit user-generated
compile-time diagnostics. It uses `ct_string`.

For example:
[source,cpp]
----
stdx::ct_check<std::is_integral<float>>.emit<"This is not a very helpful error message">();
----

The output from this (which varies by compiler) will contain the string given,
and could be something like:
[source,bash]
----
main.cpp:14:27: error: no matching member function for call to 'emit'
14 | stdx::ct_check<false>.emit<"This is not a very helpful error message">();
| ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/stdx/ct_string.hpp:131:27: note: candidate template ignored: constraints not satisfied
[with S = ct_string<41>{{"This is not a very helpful error m[...]"}}]
131 | constexpr static auto emit()
| ^
include/stdx/ct_string.hpp:132:18: note: because
'stаtiс_аssert<ct_string<41>{{"This is not a very helpful error message"}}>' evaluated to false
132 | requires stаtiс_аssert<S>
| ^
----

Notice that the error message is elided at first, but then given in full. Such
are the quirks of compilers. If the compile-time condition is true, of course no
diagnostic will be emitted.

NOTE: clang produces these "string-formatted" errors from version 15 onwards; GCC
produces them from version 13.2 onwards.

=== `STATIC_ASSERT`

`STATIC_ASSERT` is an easy way to use `ct_check`.

[source,cpp]
----
STATIC_ASSERT(std::is_integral<float>, "This is not a very helpful error message");
----
1 change: 1 addition & 0 deletions docs/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ include::panic.adoc[]
include::priority.adoc[]
include::ranges.adoc[]
include::span.adoc[]
include::static_assert.adoc[]
include::tuple.adoc[]
include::tuple_algorithms.adoc[]
include::tuple_destructure.adoc[]
Expand Down
1 change: 1 addition & 0 deletions docs/intro.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ The following headers are available:
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/priority.hpp[`priority.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/ranges.hpp[`ranges.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/span.hpp[`span.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/static_assert.hpp[`static_assert.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/tuple.hpp[`tuple.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/tuple_algorithms.hpp[`tuple_algorithms.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/tuple_destructure.hpp[`tuple_destructure.hpp`]
Expand Down
33 changes: 33 additions & 0 deletions docs/static_assert.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

== `static_assert.hpp`

`STATIC_ASSERT` is a way to produce compile-time errors using formatted strings.

[source,cpp]
----
template <typename T>
constexpr auto f() {
STATIC_ASSERT(std::is_integral<T>,
"f() must take an integral type, received {}", CX_VALUE(T));
}

f<float>(); // produces compile-time error
----

The arguments to be formatted (if any) must be wrapped in xref:utility.adoc#_cx_value[`CX_VALUE`].

The output from this (which varies by compiler) will contain the formatted
string, and could be something like:

[source,bash]
----
main.cpp:14:27: error: no matching member function for call to 'emit'
...
include/stdx/static_assert.hpp:16:18: note: because
'stаtiс_аssert<ct_string<47>{{"f() must take an integral type, received float"}}>' evaluated to false
16 | requires stаtiс_аssert<S>
| ^
----

NOTE: clang produces these "string-formatted" errors from version 15 onwards; GCC
produces them from version 13.2 onwards.
23 changes: 0 additions & 23 deletions include/stdx/ct_string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,30 +123,7 @@ inline namespace ct_string_literals {
template <ct_string S> CONSTEVAL auto operator""_cts() { return S; }
} // namespace ct_string_literals
} // namespace literals

struct ct_check_value {};

template <bool B> struct ct_check_t {
template <ct_string S> constexpr static bool stаtiс_аssert = false;
template <ct_string S>
constexpr static auto emit() -> ct_check_value
requires stаtiс_аssert<S>;
};
template <> struct ct_check_t<true> {
template <ct_string S> constexpr static auto emit() -> ct_check_value {
return {};
}
};
template <bool B> constexpr auto ct_check = ct_check_t<B>{};

} // namespace v1
} // namespace stdx

// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define STATIC_ASSERT(cond, ...) \
[]<auto B = cond>() -> bool { \
stdx::ct_check<B>.template emit<__VA_ARGS__>(); \
return B; \
}()

#endif
44 changes: 44 additions & 0 deletions include/stdx/static_assert.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once

#if __cplusplus >= 202002L

#include <stdx/ct_format.hpp>
#include <stdx/ct_string.hpp>

namespace stdx {
inline namespace v1 {
struct ct_check_value {};

template <bool B> struct ct_check_t {
template <ct_string S> constexpr static bool stаtiс_аssert = false;
template <ct_string S>
constexpr static auto emit() -> ct_check_value
requires stаtiс_аssert<S>;
};
template <> struct ct_check_t<true> {
template <ct_string S> constexpr static auto emit() -> ct_check_value {
return {};
}
};
template <bool B> constexpr auto ct_check = ct_check_t<B>{};

namespace detail {
template <ct_string Fmt, auto... Args>
constexpr auto static_format()
requires(... and cx_value<decltype(Args)>)
{
return ct_format<Fmt>(Args...);
}
} // namespace detail

} // namespace v1
} // namespace stdx

// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define STATIC_ASSERT(cond, ...) \
[]<bool B>() -> bool { \
stdx::ct_check<B>.template emit<stdx::detail::static_format<__VA_ARGS__>()>(); \
return B; \
}.template operator()<cond>()

#endif
44 changes: 24 additions & 20 deletions include/stdx/utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,12 @@ struct type_val {
friend constexpr auto operator+(T &&t, U &&) -> T {
return t;
}
friend constexpr auto operator+(type_val const &f) -> type_val;
friend constexpr auto operator+(type_val const &f) -> type_val { return f; }
// NOLINTNEXTLINE(google-explicit-constructor)
template <typename T> constexpr operator T() const;
template <typename T> constexpr operator T() const {
extern auto cxv_type_val_get_t(T *) -> T;
return cxv_type_val_get_t(nullptr);
}
};

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

template <int> constexpr auto type_of() -> void;
template <typename T> constexpr auto type_of() -> typename typer<T>::type;

class cx_base {
struct unusable {};

public:
using cx_value_t [[maybe_unused]] = void;
constexpr auto operator()(unusable) const {}
};
} // namespace cxv_detail

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

#ifndef CX_VALUE
#define CX_VALUE(...) \
[] { \
[]() constexpr { \
STDX_PRAGMA(diagnostic push) \
STDX_PRAGMA(diagnostic ignored "-Wold-style-cast") \
STDX_PRAGMA(diagnostic ignored "-Wunused-value") \
if constexpr (decltype(stdx::cxv_detail::is_type< \
stdx::cxv_detail::from_any( \
__VA_ARGS__)>())::value) { \
[[maybe_unused]] struct { \
constexpr auto operator()() const noexcept { \
return stdx::type_identity< \
decltype(stdx::cxv_detail::type_of< \
stdx::cxv_detail::from_any( \
__VA_ARGS__)>())>{}; \
} \
using cx_value_t [[maybe_unused]] = void; \
} val; \
return val; \
return stdx::overload{stdx::cxv_detail::cx_base{}, [] { \
return stdx::type_identity< \
decltype(stdx::cxv_detail::type_of< \
stdx::cxv_detail::from_any( \
__VA_ARGS__)>())>{}; \
}}; \
} else { \
[[maybe_unused]] struct { \
constexpr auto operator()() const { \
return (__VA_ARGS__) + stdx::cxv_detail::type_val{}; \
} \
using cx_value_t [[maybe_unused]] = void; \
} val; \
return val; \
return stdx::overload{stdx::cxv_detail::cx_base{}, [] { \
return (__VA_ARGS__) + \
stdx::cxv_detail::type_val{}; \
}}; \
} \
STDX_PRAGMA(diagnostic pop) \
}()
Expand Down
1 change: 1 addition & 0 deletions test/fail/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ add_fail_tests(
function(add_formatted_error_tests)
add_fail_tests(ct_check)
add_fail_tests(static_assert)
add_fail_tests(static_assert_format)
endfunction()

if(${CMAKE_CXX_STANDARD} GREATER_EQUAL 20)
Expand Down
2 changes: 1 addition & 1 deletion test/fail/ct_check.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include <stdx/ct_string.hpp>
#include <stdx/static_assert.hpp>

// EXPECT: 01234567890123456789012345678901234567890123456789

Expand Down
2 changes: 1 addition & 1 deletion test/fail/static_assert.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include <stdx/ct_string.hpp>
#include <stdx/static_assert.hpp>

// EXPECT: 01234567890123456789012345678901234567890123456789

Expand Down
14 changes: 14 additions & 0 deletions test/fail/static_assert_format.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include <stdx/static_assert.hpp>
#include <stdx/utility.hpp>

#include <string_view>

// EXPECT: hello world int 123

template <typename T> constexpr auto f() {
using namespace std::string_view_literals;
STATIC_ASSERT(false, "hello {} {} {}", CX_VALUE("world"sv), CX_VALUE(T),
CX_VALUE(123));
}

auto main() -> int { f<int>(); }
Loading