Skip to content
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
32 changes: 31 additions & 1 deletion docs/ct_format.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

== `ct_format.hpp`

=== ct_format

https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/ct_format.hpp[`ct_format.hpp`]
provides `ct_format`, a compile-time function for formatting strings.

Expand All @@ -20,7 +22,7 @@ auto s = stdx::ct_format<"Hello {} {}">(42, 17);
----

When format arguments are available at compile-time, wrapping them in
`CX_VALUE(...)` means they will get compile-time formatted.
xref:utility.adoc#_cx_value[`CX_VALUE(...)`] means they will get compile-time formatted.
[source,cpp]
----
auto s = stdx::ct_format<"Hello {} {}">(CX_VALUE(42), 17);
Expand Down Expand Up @@ -69,3 +71,31 @@ constexpr static auto a = stdx::ct_format<"The answer is {}">(42);
constexpr static auto q = stdx::ct_format<"{}. But what is the question?">(CX_VALUE(a));
// q is stdx::format_result{"The answer is {}. But what is the question?"_ctst, stdx::tuple{42}}
----

=== STDX_CT_FORMAT

The macro `STDX_CT_FORMAT` will automatically wrap compile-time-friendly
arguments, so manual wrapping with `CX_VALUE` is not required.
[source,cpp]
----
auto s = STDX_CT_FORMAT("Hello {} {}", 42, int);
// equivalent to stdx::ct_format<"Hello {} {}">(CX_VALUE(42), CX_VALUE(int));
// s is stdx::format_result{"Hello 42 int"_ctst, stdx::tuple{}}
----

If any arguments are _not_ available at compile time, they will be "regular" runtime format arguments.
[source,cpp]
----
auto x = 42;
auto s = STDX_CT_FORMAT("Hello {} {}", x, int);
// equivalent to stdx::ct_format<"Hello {} {}">(x, CX_VALUE(int));
// s is stdx::format_result{"Hello {} int"_ctst, stdx::tuple{42}}
----

Things that are "compile-time-friendly" include:

* `constexpr static` variables
* `const` integral variables
* template arguments
* literals
* types
16 changes: 16 additions & 0 deletions docs/ct_string.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,19 @@ constexpr auto s = "hello"_ctst;
// s is a stdx::cts_t<"hello">
----
Think of `cts_t` relating to `ct_string` as `std::integral_constant` relates to `int`.

A `cts_t` is implicitly convertible to a `ct_string`, or can be explicitly
converted with unary `operator+`:


[source,cpp]
----
using namespace stdx::literals;

template <stdx::ct_string S>
constexpr bool always_true = true;

constexpr auto s = "hello"_ctst;
static_assert(always_true<s>); // implicit conversion
static_assert(always_true<+s>); // explicit conversion with +
----
18 changes: 14 additions & 4 deletions docs/static_assert.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@
template <typename T>
constexpr auto f() {
STATIC_ASSERT(std::is_integral<T>,
"f() must take an integral type, received {}", CX_VALUE(T));
"f() must take an integral type, received {}", 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`] if they are not admissible as template
arguments.
NOTE: The arguments to be formatted must be compile-time, of course.

The output from this (which varies by compiler) will contain the formatted
string, and could be something like:
Expand All @@ -33,3 +31,15 @@ include/stdx/static_assert.hpp:16:18: note: because

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

After C++26,
https://en.cppreference.com/w/cpp/language/static_assert.html[`static_assert`]
in the language means that formatted `STATIC_ASSERT` produces a slightly nicer
error message; something like:

[source,bash]
----
main.cpp:14:27: error: static assertion failed: f() must take an integral type, received float
16 | STATIC_ASSERT(std::is_integral<T>,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
----
30 changes: 10 additions & 20 deletions include/stdx/static_assert.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,41 +22,31 @@ template <> struct ct_check_t<true> {
}
};
template <bool B> constexpr auto ct_check = ct_check_t<B>{};

namespace detail {
template <ct_string Fmt, auto... Args> constexpr auto static_format() {
constexpr auto make_ct = []<auto V>() {
if constexpr (fmt_cx_value<decltype(V)>) {
return V;
} else {
return CX_VALUE(V);
}
};
return ct_format<Fmt>(make_ct.template operator()<Args>()...).str.value;
}
} // namespace detail

} // namespace v1
} // namespace stdx

#if __cpp_static_assert >= 202306L

// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define STATIC_ASSERT(cond, ...) \
[]<bool B>() -> bool { \
STDX_PRAGMA(diagnostic push) \
STDX_PRAGMA(diagnostic ignored "-Wunknown-warning-option") \
STDX_PRAGMA(diagnostic ignored "-Wc++26-extensions") \
static_assert( \
B, std::string_view{stdx::detail::static_format<__VA_ARGS__>()}); \
constexpr auto S = STDX_CT_FORMAT(__VA_ARGS__); \
static_assert(B, std::string_view{+S.str}); \
STDX_PRAGMA(diagnostic pop) \
return B; \
}.template operator()<cond>()

#else

// 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; \
#define STATIC_ASSERT(cond, ...) \
[]<bool B>() -> bool { \
constexpr auto S = STDX_CT_FORMAT(__VA_ARGS__); \
stdx::ct_check<B>.template emit<S>(); \
return B; \
}.template operator()<cond>()
#endif

Expand Down
2 changes: 1 addition & 1 deletion test/fail/static_assert_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// EXPECT: hello world int 123

template <typename T> constexpr auto f() {
STATIC_ASSERT(false, "hello {} {} {}", CX_VALUE("world"), CX_VALUE(T), 123);
STATIC_ASSERT(false, "hello {} {} {}", "world", T, 123);
}

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