mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-30 10:47:35 +02:00
Integrate new format string compilation
This commit is contained in:
@ -232,7 +232,10 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
|
|||||||
}
|
}
|
||||||
} // namespace cf
|
} // namespace cf
|
||||||
|
|
||||||
template <typename S, typename = void> struct compiled_format_base {
|
struct basic_compiled_format {};
|
||||||
|
|
||||||
|
template <typename S, typename = void>
|
||||||
|
struct compiled_format_base : basic_compiled_format {
|
||||||
using char_type = char_t<S>;
|
using char_type = char_t<S>;
|
||||||
using parts_container = std::vector<internal::format_part<char_type>>;
|
using parts_container = std::vector<internal::format_part<char_type>>;
|
||||||
|
|
||||||
@ -279,7 +282,8 @@ template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename S>
|
template <typename S>
|
||||||
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>> {
|
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
|
||||||
|
: basic_compiled_format {
|
||||||
using char_type = char_t<S>;
|
using char_type = char_t<S>;
|
||||||
|
|
||||||
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
|
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
|
||||||
@ -345,9 +349,10 @@ using get_type = typename get_type_impl<N, T>::type;
|
|||||||
|
|
||||||
template <typename Char> struct text {
|
template <typename Char> struct text {
|
||||||
basic_string_view<Char> data;
|
basic_string_view<Char> data;
|
||||||
|
using char_type = Char;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... Args>
|
||||||
OutputIt format(OutputIt out, const Args&...) {
|
OutputIt format(OutputIt out, const Args&...) const {
|
||||||
// TODO: reserve
|
// TODO: reserve
|
||||||
return copy_str<Char>(data.begin(), data.end(), out);
|
return copy_str<Char>(data.begin(), data.end(), out);
|
||||||
}
|
}
|
||||||
@ -381,8 +386,10 @@ OutputIt format_default(OutputIt out, const Char* value) {
|
|||||||
|
|
||||||
// A replacement field that refers to argument N.
|
// A replacement field that refers to argument N.
|
||||||
template <typename Char, typename T, int N> struct field {
|
template <typename Char, typename T, int N> struct field {
|
||||||
|
using char_type = Char;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... Args>
|
||||||
OutputIt format(OutputIt out, const Args&... args) {
|
OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
// This ensures that the argument type is convertile to `const T&`.
|
// This ensures that the argument type is convertile to `const T&`.
|
||||||
const T& arg = get<N>(args...);
|
const T& arg = get<N>(args...);
|
||||||
return format_default<Char>(out, arg);
|
return format_default<Char>(out, arg);
|
||||||
@ -392,9 +399,10 @@ template <typename Char, typename T, int N> struct field {
|
|||||||
template <typename L, typename R> struct concat {
|
template <typename L, typename R> struct concat {
|
||||||
L lhs;
|
L lhs;
|
||||||
R rhs;
|
R rhs;
|
||||||
|
using char_type = typename L::char_type;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... Args>
|
||||||
OutputIt format(OutputIt out, const Args&... args) {
|
OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
out = lhs.format(out, args...);
|
out = lhs.format(out, args...);
|
||||||
return rhs.format(out, args...);
|
return rhs.format(out, args...);
|
||||||
}
|
}
|
||||||
@ -405,6 +413,8 @@ constexpr concat<L, R> make_concat(L lhs, R rhs) {
|
|||||||
return {lhs, rhs};
|
return {lhs, rhs};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct unknown {};
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
|
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
|
||||||
for (size_t size = str.size(); pos != size; ++pos) {
|
for (size_t size = str.size(); pos != size; ++pos) {
|
||||||
@ -417,29 +427,39 @@ template <typename Args, size_t POS, int ID, typename S>
|
|||||||
constexpr auto compile_format_string(S format_str);
|
constexpr auto compile_format_string(S format_str);
|
||||||
|
|
||||||
template <typename Args, size_t POS, int ID, typename T, typename S>
|
template <typename Args, size_t POS, int ID, typename T, typename S>
|
||||||
auto parse_tail(T head, S format_str) {
|
constexpr auto parse_tail(T head, S format_str) {
|
||||||
if constexpr (POS != to_string_view(format_str).size())
|
if constexpr (POS != to_string_view(format_str).size()) {
|
||||||
return make_concat(head, compile_format_string<Args, POS, ID>(format_str));
|
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
|
||||||
else
|
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>, unknown>())
|
||||||
|
return tail;
|
||||||
|
else
|
||||||
|
return make_concat(head, tail);
|
||||||
|
} else {
|
||||||
return head;
|
return head;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compiles a non-empty format string and returns the compiled representation
|
||||||
|
// or unknown() on unrecognized input.
|
||||||
template <typename Args, size_t POS, int ID, typename S>
|
template <typename Args, size_t POS, int ID, typename S>
|
||||||
constexpr auto compile_format_string(S format_str) {
|
constexpr auto compile_format_string(S format_str) {
|
||||||
using char_type = char_t<S>;
|
using char_type = typename S::char_type;
|
||||||
constexpr basic_string_view<char_type> str = format_str;
|
constexpr basic_string_view<char_type> str = format_str;
|
||||||
if constexpr (str.size() == 0) {
|
if constexpr (str[POS] == '{') {
|
||||||
return make_text(str, 0, 0);
|
|
||||||
} else if constexpr (str[POS] == '{') {
|
|
||||||
if (POS + 1 == str.size())
|
if (POS + 1 == str.size())
|
||||||
throw format_error("unmatched '{' in format string");
|
throw format_error("unmatched '{' in format string");
|
||||||
if constexpr (str[POS + 1] == '{') {
|
if constexpr (str[POS + 1] == '{') {
|
||||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
} else if constexpr (str[POS + 1] == '}') {
|
} else if constexpr (str[POS + 1] == '}') {
|
||||||
return parse_tail<Args, POS + 2, ID + 1>(
|
using type = get_type<ID, Args>;
|
||||||
field<char_type, get_type<ID, Args>, ID>(), format_str);
|
if constexpr (std::is_same<type, int>::value) {
|
||||||
|
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
|
||||||
|
format_str);
|
||||||
|
} else {
|
||||||
|
return unknown();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw format_error("invalid format string");
|
return unknown();
|
||||||
}
|
}
|
||||||
} else if constexpr (str[POS] == '}') {
|
} else if constexpr (str[POS] == '}') {
|
||||||
if (POS + 1 == str.size())
|
if (POS + 1 == str.size())
|
||||||
@ -451,20 +471,57 @@ constexpr auto compile_format_string(S format_str) {
|
|||||||
format_str);
|
format_str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <typename... Args, typename S> constexpr auto compile(S format_str) {
|
|
||||||
return compile_format_string<type_list<Args...>, 0, 0>(format_str);
|
|
||||||
}
|
|
||||||
#endif // __cpp_if_constexpr
|
#endif // __cpp_if_constexpr
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
#if FMT_USE_CONSTEXPR
|
#if FMT_USE_CONSTEXPR
|
||||||
|
# ifdef __cpp_if_constexpr
|
||||||
template <typename... Args, typename S,
|
template <typename... Args, typename S,
|
||||||
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||||
FMT_CONSTEXPR auto compile(S format_str)
|
constexpr auto compile(S format_str) {
|
||||||
-> internal::compiled_format<S, Args...> {
|
constexpr basic_string_view<typename S::char_type> str = format_str;
|
||||||
|
if constexpr (str.size() == 0) {
|
||||||
|
return internal::make_text(str, 0, 0);
|
||||||
|
} else {
|
||||||
|
constexpr auto result =
|
||||||
|
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
|
||||||
|
format_str);
|
||||||
|
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
|
||||||
|
internal::unknown>()) {
|
||||||
|
return internal::compiled_format<S, Args...>(to_string_view(format_str));
|
||||||
|
} else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CompiledFormat, typename... Args,
|
||||||
|
typename Char = typename CompiledFormat::char_type,
|
||||||
|
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
|
||||||
|
CompiledFormat>::value)>
|
||||||
|
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
using range = buffer_range<Char>;
|
||||||
|
using context = buffer_context<Char>;
|
||||||
|
cf.format(std::back_inserter(buffer), args...);
|
||||||
|
return to_string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||||
|
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
|
||||||
|
CompiledFormat>::value)>
|
||||||
|
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||||
|
const Args&... args) {
|
||||||
|
return cf.format(out, args...);
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
template <typename... Args, typename S,
|
||||||
|
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||||
|
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
|
||||||
return internal::compiled_format<S, Args...>(to_string_view(format_str));
|
return internal::compiled_format<S, Args...>(to_string_view(format_str));
|
||||||
}
|
}
|
||||||
#endif
|
# endif // __cpp_if_constexpr
|
||||||
|
#endif // FMT_USE_CONSTEXPR
|
||||||
|
|
||||||
// Compiles the format string which must be a string literal.
|
// Compiles the format string which must be a string literal.
|
||||||
template <typename... Args, typename Char, size_t N>
|
template <typename... Args, typename Char, size_t N>
|
||||||
@ -475,7 +532,9 @@ auto compile(const Char (&format_str)[N])
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args,
|
template <typename CompiledFormat, typename... Args,
|
||||||
typename Char = typename CompiledFormat::char_type>
|
typename Char = typename CompiledFormat::char_type,
|
||||||
|
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
|
||||||
|
CompiledFormat>::value)>
|
||||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
using range = buffer_range<Char>;
|
using range = buffer_range<Char>;
|
||||||
@ -485,7 +544,9 @@ std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
|||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args>
|
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||||
|
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
|
||||||
|
CompiledFormat>::value)>
|
||||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
using char_type = typename CompiledFormat::char_type;
|
using char_type = typename CompiledFormat::char_type;
|
||||||
|
@ -186,7 +186,7 @@ FMT_END_NAMESPACE
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_NUMERIC_ALIGN
|
#ifndef FMT_NUMERIC_ALIGN
|
||||||
# define FMT_NUMERIC_ALIGN 1
|
# define FMT_NUMERIC_ALIGN 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
@ -2791,8 +2791,9 @@ void internal::basic_writer<Range>::write_double(T value,
|
|||||||
bool use_grisu = USE_GRISU &&
|
bool use_grisu = USE_GRISU &&
|
||||||
(specs.type != 'a' && specs.type != 'A' &&
|
(specs.type != 'a' && specs.type != 'A' &&
|
||||||
specs.type != 'e' && specs.type != 'E') &&
|
specs.type != 'e' && specs.type != 'E') &&
|
||||||
internal::grisu_format(static_cast<double>(value), buffer,
|
internal::grisu_format(
|
||||||
precision, handler.fixed ? internal::grisu_options::fixed : 0, exp);
|
static_cast<double>(value), buffer, precision,
|
||||||
|
handler.fixed ? internal::grisu_options::fixed : 0, exp);
|
||||||
char* decimal_point_pos = nullptr;
|
char* decimal_point_pos = nullptr;
|
||||||
if (!use_grisu)
|
if (!use_grisu)
|
||||||
decimal_point_pos = internal::sprintf_format(value, buffer, specs);
|
decimal_point_pos = internal::sprintf_format(value, buffer, specs);
|
||||||
|
Reference in New Issue
Block a user