275.md
September 5, 2022 ยท View on GitHub
Info
-
Did you know what is the underlying type of NTTP string aka
fixed_string?
Example
template <std::size_t N> struct fixed_string final {
constexpr explicit(false) fixed_string(const char (&str)[N + 1]) {
std::copy_n(str, N + 1, std::data(data));
}
[[nodiscard]] constexpr auto operator<=>(const fixed_string &) const = default;
std::array<char, N + 1> data{};
};
template <std::size_t N>
fixed_string(const char (&str)[N]) -> fixed_string<N - 1>;
template<fixed_string> struct foo;
int main() {
what_is_my_type<"Quantlab">{}; // clang: 'what_is_my_type<{{"Quantlab"}}>'
// gcc: struct what_is_my_type<fixed_string<8>{std::array<char, 9>{"Quantlab"}}>
}
Puzzle
- Can you implement
to_string_twhich convertsfixed_stringintostring<char...>?
template <char... Cs> struct string {};
template <class T, T... Cs> [[nodiscard]] constexpr auto operator""_cs() { return string<Cs...>{}; }
template <std::size_t N>
struct fixed_string; // TODO
template <fixed_string Str>
using to_string_t; // TODO
static_assert(std::is_same_v<decltype(""_cs), to_string_t<"">>);
static_assert(std::is_same_v<decltype("Q"_cs), to_string_t<"Q">>);
static_assert(std::is_same_v<decltype("Foo"_cs), to_string_t<"Foo">>);
static_assert(std::is_same_v<decltype("Bar"_cs), to_string_t<"Bar">>);
static_assert(not std::is_same_v<decltype("fo"_cs), to_string_t<"foo">>);
static_assert(not std::is_same_v<decltype("foo"_cs), to_string_t<"Foo">>);
static_assert(not std::is_same_v<decltype("Foo"_cs), to_string_t<"Bar">>);
static_assert(not std::is_same_v<decltype("Bar"_cs), to_string_t<"Foo">>);
Solutions
template <std::size_t N>
struct fixed_string final {
constexpr explicit(false) fixed_string(const char (&str)[N + 1]) {
std::copy_n(str, N + 1, std::data(data));
}
[[nodiscard]] constexpr auto
operator<=>(const fixed_string &) const = default;
std::array<char, N + 1> data{};
};
template <std::size_t N>
fixed_string(const char (&str)[N]) -> fixed_string<N - 1>;
template <fixed_string Str>
using to_string_t = decltype([]<std::size_t... Is>(std::index_sequence<Is...>) {
return string<Str.data[Is]...>{};
}(std::make_index_sequence<Str.data.size() - 1>()))
#define FWD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
namespace detail {
template <auto Container, auto... Is>
[[nodiscard]] consteval decltype(auto) apply_impl(auto&& func,
std::index_sequence<Is...>) {
return func.template operator()<Container[Is]...>();
}
template <auto Container>
[[nodiscard]] consteval decltype(auto) apply(auto&& func) {
return detail::apply_impl<Container>(
FWD(func), std::make_index_sequence<std::size(Container)>{});
}
} // namespace detail
template <char... Cs>
struct string {};
template <class T, T... Cs>
[[nodiscard]] constexpr auto operator""_cs() {
return string<Cs...>{};
}
template <std::size_t N>
struct fixed_string {
constexpr explicit(false) fixed_string(const char (&str)[N + 1]) {
std::copy_n(str, N, std::data(chars));
}
std::array<char, N> chars;
};
template <std::size_t N>
fixed_string(const char (&str)[N]) -> fixed_string<N - 1>;
template <fixed_string Str>
using to_string_t = decltype(detail::apply<Str.chars>(
[]<char... Cs>() { return string<Cs...>{}; }));
template <fixed_string Str>
using to_string_t = decltype([]<auto... Is>(std::index_sequence<Is...>) {
return string<Str[Is]...>{};
}(std::make_index_sequence<std::size(Str) - 1>{}));