Info
-
Did you know Non-Type Template Parameters (NTTP) with User-Defined Literals (UDL) can be used to get compile-time strings?
Example
template<auto Size>
struct fixed_string {
char data[Size + 1]{};
static constexpr auto size = Size;
constexpr explicit(false) fixed_string(char const* str) { std::copy_n(str, Size + 1, data); }
constexpr explicit(false) operator std::string_view() const { return {data, Size}; }
};
template<auto Size> fixed_string(char const (&)[Size]) -> fixed_string<Size - 1>;
template<fixed_string Str> constexpr auto operator""_s() { return Str; }
static_assert(sizeof("Quantlab") == sizeof("Quantlab"_s));
static_assert("Quantlab"sv == std::string_view{"Quantlab"_s});
Puzzle
- Can you implement
fixed_format
subroutine which produces minimal size compile-timefixed_string
?
constexpr auto fixed_format(auto format, auto... args); /* TODO */
static_assert(fixed_format(""_fmt) == ""sv);
static_assert(sizeof("") == sizeof(fixed_format(""_fmt)));
static_assert(fixed_format("{}"_fmt, "Quantlab"_s) == "Quantlab"sv);
static_assert(sizeof("Quantlab") == sizeof(fixed_format("{}"_fmt, "Quantlab"_s)));
static_assert(fixed_format("|{}|{}|"_fmt, "Quant"_s, "lab"_s) == "|Quant|lab|"sv);
static_assert(sizeof("|Quant|lab|") == sizeof(fixed_format("|{}|{}|"_fmt, "Quant"_s, "lab"_s)));
static_assert(fixed_format("{}+{}={}"_fmt, "1"_s, "2"_s, "3"_s) == "1+2=3"sv);
static_assert(sizeof("1+2=3") == sizeof(fixed_format("{}+{}={}"_fmt, "1"_s, "2"_s, "3"_s)));
Solutions
constexpr const std::string_view field{"{}"};
constexpr void fixed_format_arg(auto& first, const auto& last, auto& d_first, const auto arg) {
constexpr const auto size = decltype(arg)::size;
const auto next = std::search(first, last, field.cbegin(), field.cend());
const auto dist = std::distance(first, next);
std::copy(first, next, d_first);
std::copy_n(arg.data, size, d_first + dist);
std::advance(first, dist + field.size());
std::advance(d_first, dist + size);
}
constexpr auto fixed_format(const auto fmt, const auto... args) {
fixed_string<(decltype(fmt)::size + ... + (decltype(args)::size - field.size()))> out;
auto first = fmt.data;
const auto last = first + sizeof fmt.data;
auto d_first = out.data;
(fixed_format_arg(first, last, d_first, args), ...);
std::copy(first, last, d_first);
return out;
}
constexpr auto fixed_format(auto format, auto... args) {
constexpr auto spec = "{}"sv;
constexpr auto spec_size = std::size(spec);
constexpr auto formatted_size = std::size(format) - sizeof...(args)*spec_size + (std::size(args) + ... + 0);
fixed_string<formatted_size> ret{};
auto write_ptr = std::data(ret);
std::string_view s = format;
([&] (const auto& arg) {
const auto arg_pos = s.find(spec);
write_ptr = std::copy_n(std::data(s), arg_pos, write_ptr);
write_ptr = std::copy_n(std::data(arg), std::size(arg), write_ptr);
s.remove_prefix(arg_pos + spec_size);
}(args), ...);
std::copy_n(std::data(s), std::size(s), write_ptr);
return ret;
}
constexpr auto fixed_format(auto format, auto... args) {
constexpr auto len = format.size - 2*sizeof...(args) + (args.size + ... + 0);
std::array<char, len+1> fstr{};
std::array<std::string_view, sizeof...(args)> argView{ std::string_view(args)...};
char* in_it = format.data;
char* out_it = fstr.data();
uint argIdx = 0;
while(in_it < format.data+format.size)
if (*in_it != '{')
*out_it++ = *in_it++;
else {
std::copy_n(argView[argIdx].data(), argView[argIdx].size(), out_it);
out_it += argView[argIdx].size();
in_it += 2;
argIdx++;
}
return fixed_string<len>(fstr.data());
}
constexpr auto fixed_format(auto format, auto... args) {
constexpr auto to_replace = "{}"sv;
constexpr auto replace_len = std::size(to_replace) * sizeof...(args);
constexpr auto args_sum_len = (std::size(args) + ... + 0);
constexpr auto output_len = std::size(format) - replace_len + args_sum_len;
auto ret = fixed_string<output_len>{};
auto loc = std::begin(ret);
auto s = std::string_view(format);
([] (auto& s, auto& loc, const auto& to_replace, const auto& arg) {
const auto pos = s.find(to_replace);
loc = std::copy(std::begin(s), std::begin(s) + pos, loc);
loc = std::copy(std::begin(arg), std::begin(arg) + std::size(arg), loc);
s.remove_prefix(pos + std::size(to_replace));
}(s, loc, to_replace, args), ...);
std::copy(std::begin(s), std::begin(s) + std::size(s), loc);
return ret;
}
constexpr auto replace_unnamed_arg(const auto format, const auto replacement) {
constexpr std::string_view unnamed_arg_str = "{}";
constexpr auto new_size =
format.size - std::size(unnamed_arg_str) + replacement.size;
auto replace_start = std::distance(
std::begin(format.data),
std::find(std::begin(format.data), std::end(format.data), '{'));
auto replace_end = replace_start + std::size(unnamed_arg_str);
fixed_str<new_size> output{};
auto write_loc = std::copy_n(format.data, replace_start, output.data);
write_loc = std::copy_n(replacement.data, replacement.size, write_loc);
std::copy_n(format.data + replace_end, format.size - replace_end, write_loc);
return output;
}
constexpr auto apply_formatting(const auto format) { return format; }
constexpr auto apply_formatting(const auto format, const auto arg, auto... args) {
return apply_formatting(replace_unnamed_arg(format, arg), args...);
}
constexpr auto fixed_format(auto format, auto... args) {
return apply_formatting(format, args...);
}