Skip to content

Commit

Permalink
✨ Allow more compile-time formatting use cases
Browse files Browse the repository at this point in the history
Formatting of:
 - type-encoded strings
 - already-computed format results
  • Loading branch information
elbeno committed May 20, 2024
1 parent fd60999 commit 40c87e9
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 16 deletions.
45 changes: 36 additions & 9 deletions include/stdx/ct_format.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ template <std::size_t N> CONSTEVAL auto split_specifiers(std::string_view fmt) {
}

template <typename T>
concept cx_value = requires { typename T::cx_value_t; };
concept cx_value = requires { typename T::cx_value_t; } or
requires(T t) { ct_string_from_type(t); };

CONSTEVAL auto arg_value(auto a) { return a; }

Expand All @@ -96,7 +97,9 @@ template <typename T> CONSTEVAL auto arg_value(type_identity<T>) {
}

CONSTEVAL auto arg_value(cx_value auto a) {
if constexpr (std::is_enum_v<decltype(a())>) {
if constexpr (requires { ct_string_from_type(a); }) {
return ct_string_from_type(a);
} else if constexpr (std::is_enum_v<decltype(a())>) {
return enum_as_string<a()>();
} else {
return arg_value(a());
Expand All @@ -109,6 +112,7 @@ CONSTEVAL auto operator+(format_result<T, U> r, S s) {
}

template <typename S, typename T, typename U>
requires(is_specialization_of<S, ct_string>().value)
CONSTEVAL auto operator+(S s, format_result<T, U> r) {
return format_result{s + r.str, r.args};
}
Expand All @@ -124,6 +128,14 @@ template <std::size_t Sz> CONSTEVAL auto to_ct_string(std::string_view s) {
return ct_string<Sz + 1>{s.data(), s.size()};
}

CONSTEVAL auto convert_input(auto s) {
if constexpr (requires { ct_string_from_type(s); }) {
return ct_string_from_type(s);
} else {
return s;
}
}

template <ct_string S,
template <typename T, T...> typename Output = detail::null_output>
CONSTEVAL auto convert_output() {
Expand All @@ -139,15 +151,30 @@ template <ct_string Fmt,
typename Arg>
CONSTEVAL auto format1(Arg arg) {
if constexpr (cx_value<Arg>) {
constexpr auto str = [&] {
constexpr auto a = arg_value(arg);
constexpr auto result = [&] {
constexpr auto fmtstr = FMT_COMPILE(std::string_view{Fmt});
constexpr auto sz = fmt::formatted_size(fmtstr, a);
ct_string<sz + 1> cts{};
fmt::format_to(cts.begin(), fmtstr, a);
return cts;
constexpr auto a = arg_value(arg);
if constexpr (is_specialization_of_v<std::remove_cv_t<decltype(a)>,
format_result>) {
constexpr auto s = convert_input(a.str);
constexpr auto sz = fmt::formatted_size(fmtstr, s);
ct_string<sz + 1> cts{};
fmt::format_to(cts.begin(), fmtstr, s);
return format_result{cts, a.args};
} else {
constexpr auto sz = fmt::formatted_size(fmtstr, a);
ct_string<sz + 1> cts{};
fmt::format_to(cts.begin(), fmtstr, a);
return cts;
}
}();
return convert_output<str, Output>();
if constexpr (is_specialization_of_v<std::remove_cv_t<decltype(result)>,
format_result>) {
return format_result{convert_output<result.str, Output>(),
result.args};
} else {
return convert_output<result, Output>();
}
} else {
return format_result{convert_output<Fmt, Output>(), tuple{arg}};
}
Expand Down
17 changes: 10 additions & 7 deletions include/stdx/utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ using sized16 = sized<std::uint16_t>;
using sized32 = sized<std::uint32_t>;
using sized64 = sized<std::uint64_t>;

namespace detail {
namespace cxv_detail {
struct from_any {
// NOLINTNEXTLINE(google-explicit-constructor)
template <typename... Ts> constexpr from_any(Ts const &...) {}
Expand All @@ -144,8 +144,9 @@ struct from_any {
};

struct type_val {
template <typename T>
friend constexpr auto operator+(T const &t, type_val const &) -> T {
template <typename T, typename U,
typename = std::enable_if_t<same_as_unqualified<type_val, U>>>
friend constexpr auto operator+(T &&t, U &&) -> T {
return t;
}
friend constexpr auto operator+(type_val const &f) -> type_val;
Expand All @@ -163,7 +164,7 @@ 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;
} // namespace detail
} // namespace cxv_detail
} // namespace v1
} // namespace stdx

Expand All @@ -184,12 +185,14 @@ template <typename T> constexpr auto type_of() -> typename typer<T>::type;
STDX_PRAGMA(diagnostic push) \
STDX_PRAGMA(diagnostic ignored "-Wold-style-cast") \
STDX_PRAGMA(diagnostic ignored "-Wunused-value") \
if constexpr (decltype(stdx::detail::is_type<stdx::detail::from_any( \
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::detail::type_of<stdx::detail::from_any( \
decltype(stdx::cxv_detail::type_of< \
stdx::cxv_detail::from_any( \
__VA_ARGS__)>())>{}; \
} \
using cx_value_t [[maybe_unused]] = void; \
Expand All @@ -198,7 +201,7 @@ template <typename T> constexpr auto type_of() -> typename typer<T>::type;
} else { \
[[maybe_unused]] struct { \
constexpr auto operator()() const { \
return (__VA_ARGS__) + stdx::detail::type_val{}; \
return (__VA_ARGS__) + stdx::cxv_detail::type_val{}; \
} \
using cx_value_t [[maybe_unused]] = void; \
} val; \
Expand Down
34 changes: 34 additions & 0 deletions test/ct_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,19 @@ TEST_CASE("format multiple mixed arguments", "[ct_format]") {
stdx::make_tuple(42, "B"sv)});
}

TEST_CASE("format a formatted string", "[ct_format]") {
static_assert(stdx::ct_format<"The value is {}.">(
CX_VALUE(stdx::ct_format<"(year={})">(2022))) ==
stdx::format_result{"The value is (year={})."_cts,
stdx::make_tuple(2022)});
}

TEST_CASE("format a ct-formatted string", "[ct_format]") {
constexpr static auto cts = stdx::ct_format<"(year={})">(CX_VALUE(2024));
static_assert(stdx::ct_format<"The value is {}.">(CX_VALUE(cts)) ==
"The value is (year=2024)."_cts);
}

namespace {
template <typename T, T...> struct string_constant {
private:
Expand All @@ -127,3 +140,24 @@ TEST_CASE("format_to a different type", "[ct_format]") {
stdx::format_result{string_constant<char, '{', '}'>{},
stdx::make_tuple(42)});
}

TEST_CASE("format a string-type argument", "[ct_format]") {
static_assert(stdx::ct_format<"Hello {}!">(string_constant<char, 'A'>{}) ==
"Hello A!"_cts);
}

TEST_CASE("format a formatted string with different type", "[ct_format]") {
static_assert(stdx::ct_format<"A{}D", string_constant>(CX_VALUE(
stdx::ct_format<"B{}C", string_constant>(2022))) ==
stdx::format_result{
string_constant<char, 'A', 'B', '{', '}', 'C', 'D'>{},
stdx::make_tuple(2022)});
}

TEST_CASE("format a ct-formatted string with different type", "[ct_format]") {
constexpr static auto cts =
stdx::ct_format<"B{}C", string_constant>(CX_VALUE(2024));
static_assert(
stdx::ct_format<"A{}D", string_constant>(CX_VALUE(cts)) ==
string_constant<char, 'A', 'B', '2', '0', '2', '4', 'C', 'D'>{});
}

0 comments on commit 40c87e9

Please sign in to comment.