Skip to content

✨ Add operator+ for format_result conversion to ct_string #240

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

Merged
merged 1 commit into from
Jun 26, 2025
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
8 changes: 7 additions & 1 deletion docs/ct_format.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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 <stdx::ct_string S> auto f() { ... };

f<stdx::ct_format<"Hello {}">(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]
----
Expand Down
2 changes: 2 additions & 0 deletions docs/ct_string.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
28 changes: 28 additions & 0 deletions docs/utility.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, 42>{}

enum struct E { A, B, C };
constexpr auto y = stdx::ct<E::A>(); // std::integral_constant<E, E::A>{}
----

Or, when given a type, it produces a `stdx::type_identity`:

[source,cpp]
----
constexpr auto z = stdx::ct<int>(); // stdx::type_identity<int>{}
----

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
Expand Down
12 changes: 12 additions & 0 deletions include/stdx/ct_format.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ template <typename Str, typename Args> 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;
Expand Down
6 changes: 6 additions & 0 deletions test/ct_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ TEST_CASE("empty format_result can implicitly convert to ct_string",
STATIC_REQUIRE(conversion_success<stdx::ct_format<"Hello">()>);
}

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 <typename T, T...> struct string_constant {
private:
Expand Down