diff --git a/docs/ct_format.adoc b/docs/ct_format.adoc index 5be15ba..61b5349 100644 --- a/docs/ct_format.adoc +++ b/docs/ct_format.adoc @@ -33,14 +33,20 @@ If there are no runtime arguments, the result is a `format_result` with an empty auto s = stdx::ct_format<"Hello {} {}">(CX_VALUE(42), CX_VALUE(17)); // s is stdx::format_result{"Hello 42 17"_ctst, stdx::tuple{}} ---- -...and a `format_result` like this without an runtime arguments is implicitly convertible back to a `ct_string`: + +...and a `format_result` like this without an runtime arguments is implicitly convertible back +to a `ct_string`, or explicitly convertible using unary `operator+`: [source,cpp] ---- template auto f() { ... }; f(CX_VALUE(42))>(); // equivalent to f<"Hello 42">() ++stdx::ct_format<"Hello {}">(CX_VALUE(42)); // equivalent to stdx::ct_string{"Hello 42"} ---- +NOTE: Using `operator+` on a `format_result` that has a non-empty tuple of +runtime arguments is a compilation error. + Types and compile-time enumeration values are stringified thus: [source,cpp] ---- diff --git a/docs/ct_string.adoc b/docs/ct_string.adoc index 74a102d..f37d25c 100644 --- a/docs/ct_string.adoc +++ b/docs/ct_string.adoc @@ -85,6 +85,8 @@ 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. +=== `cts_t` + Sometimes it is useful to wrap a `ct_string` in a type to preserve compile-time properties. For this, there is `cts_t` and a corresponding user-defined literal. [source,cpp] diff --git a/docs/utility.adoc b/docs/utility.adoc index 0400871..62b7959 100644 --- a/docs/utility.adoc +++ b/docs/utility.adoc @@ -15,6 +15,34 @@ auto x = 1729; // int auto y = stdx::as_unsigned(x); // unsigned int ---- +=== `ct` + +`ct` is a function template that produces a +https://en.cppreference.com/w/cpp/types/integral_constant.html[`std::integral_constant`] +from a compile-time integral or enumeration type: + +[source,cpp] +---- +constexpr auto x = stdx::ct<42>(); // std::integral_constant{} + +enum struct E { A, B, C }; +constexpr auto y = stdx::ct(); // std::integral_constant{} +---- + +Or, when given a type, it produces a `stdx::type_identity`: + +[source,cpp] +---- +constexpr auto z = stdx::ct(); // stdx::type_identity{} +---- + +Or, when given a string literal, it produces a xref:ct_string.adoc#_cts_t[`stdx::cts_t`]: + +[source,cpp] +---- +constexpr auto s = stdx::ct<"Hello">(); // stdx::cts_t<"Hello">{} +---- + === `CX_VALUE` `CX_VALUE` is a macro that wraps its argument in a constexpr callable, which can diff --git a/include/stdx/ct_format.hpp b/include/stdx/ct_format.hpp index 09c6771..62006ae 100644 --- a/include/stdx/ct_format.hpp +++ b/include/stdx/ct_format.hpp @@ -32,6 +32,18 @@ template struct format_result { [[no_unique_address]] Str str; [[no_unique_address]] Args args{}; + friend constexpr auto operator+(format_result const &fr) + requires(decltype(ct_string_convertible())::value) + { + return ct_string{fr.str.value}; + } + + friend constexpr auto operator+(format_result const &) { + static_assert(decltype(ct_string_convertible())::value, + "Unary operator+ can only be used on a format_result " + "without any runtime arguments"); + } + private: friend constexpr auto operator==(format_result const &, format_result const &) -> bool = default; diff --git a/test/ct_format.cpp b/test/ct_format.cpp index b53fe9e..ed628af 100644 --- a/test/ct_format.cpp +++ b/test/ct_format.cpp @@ -160,6 +160,12 @@ TEST_CASE("empty format_result can implicitly convert to ct_string", STATIC_REQUIRE(conversion_success()>); } +TEST_CASE("empty format_result can explicitly convert to ct_string", + "[ct_format]") { + using namespace std::string_view_literals; + STATIC_REQUIRE(+stdx::ct_format<"Hello">() == "Hello"_cts); +} + namespace { template struct string_constant { private: