mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-30 02:37:36 +02:00
Move parsing to core
This commit is contained in:
@ -8,7 +8,8 @@
|
|||||||
#ifndef FMT_CORE_H_
|
#ifndef FMT_CORE_H_
|
||||||
#define FMT_CORE_H_
|
#define FMT_CORE_H_
|
||||||
|
|
||||||
#include <cstdio> // std::FILE
|
#include <climits> // INT_MAX
|
||||||
|
#include <cstdio> // std::FILE
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -1809,9 +1810,454 @@ template <typename Context> class basic_format_args {
|
|||||||
using format_args = basic_format_args<format_context>;
|
using format_args = basic_format_args<format_context>;
|
||||||
using wformat_args = basic_format_args<wformat_context>;
|
using wformat_args = basic_format_args<wformat_context>;
|
||||||
|
|
||||||
|
// We cannot use enum classes as bit fields because of a gcc bug
|
||||||
|
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414.
|
||||||
|
namespace align {
|
||||||
|
enum type { none, left, right, center, numeric };
|
||||||
|
}
|
||||||
|
using align_t = align::type;
|
||||||
|
|
||||||
FMT_MODULE_EXPORT_END
|
FMT_MODULE_EXPORT_END
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename Char> constexpr bool is_ascii_letter(Char c) {
|
||||||
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts a character to ASCII. Returns a number > 127 on conversion failure.
|
||||||
|
template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
|
||||||
|
constexpr Char to_ascii(Char value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
|
||||||
|
constexpr typename std::underlying_type<Char>::type to_ascii(Char value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR int code_point_length(const Char* begin) {
|
||||||
|
if (const_check(sizeof(Char) != 1)) return 1;
|
||||||
|
constexpr char lengths[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0};
|
||||||
|
int len = lengths[static_cast<unsigned char>(*begin) >> 3];
|
||||||
|
|
||||||
|
// Compute the pointer to the next character early so that the next
|
||||||
|
// iteration can start working on the next character. Neither Clang
|
||||||
|
// nor GCC figure out this reordering on their own.
|
||||||
|
return len + !len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses the range [begin, end) as an unsigned integer. This function assumes
|
||||||
|
// that the range is non-empty and the first character is a digit.
|
||||||
|
template <typename Char, typename ErrorHandler>
|
||||||
|
FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end,
|
||||||
|
ErrorHandler&& eh) {
|
||||||
|
FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
|
||||||
|
unsigned value = 0;
|
||||||
|
// Convert to unsigned to prevent a warning.
|
||||||
|
const unsigned max_int = to_unsigned(INT_MAX);
|
||||||
|
unsigned big = max_int / 10;
|
||||||
|
do {
|
||||||
|
// Check for overflow.
|
||||||
|
if (value > big) {
|
||||||
|
value = max_int + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
value = value * 10 + unsigned(*begin - '0');
|
||||||
|
++begin;
|
||||||
|
} while (begin != end && '0' <= *begin && *begin <= '9');
|
||||||
|
if (value > max_int) eh.on_error("number is too big");
|
||||||
|
return static_cast<int>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses fill and alignment.
|
||||||
|
template <typename Char, typename Handler>
|
||||||
|
FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
|
||||||
|
Handler&& handler) {
|
||||||
|
FMT_ASSERT(begin != end, "");
|
||||||
|
auto align = align::none;
|
||||||
|
auto p = begin + code_point_length(begin);
|
||||||
|
if (p >= end) p = begin;
|
||||||
|
for (;;) {
|
||||||
|
switch (to_ascii(*p)) {
|
||||||
|
case '<':
|
||||||
|
align = align::left;
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
align = align::right;
|
||||||
|
break;
|
||||||
|
case '^':
|
||||||
|
align = align::center;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (align != align::none) {
|
||||||
|
if (p != begin) {
|
||||||
|
auto c = *begin;
|
||||||
|
if (c == '{')
|
||||||
|
return handler.on_error("invalid fill character '{'"), begin;
|
||||||
|
handler.on_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
|
||||||
|
begin = p + 1;
|
||||||
|
} else
|
||||||
|
++begin;
|
||||||
|
handler.on_align(align);
|
||||||
|
break;
|
||||||
|
} else if (p == begin) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p = begin;
|
||||||
|
}
|
||||||
|
return begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct auto_id {};
|
||||||
|
|
||||||
|
// Adapts SpecHandler to IDHandler API for dynamic width.
|
||||||
|
template <typename SpecHandler, typename Char> struct width_adapter {
|
||||||
|
explicit FMT_CONSTEXPR width_adapter(SpecHandler& h) : handler(h) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
|
||||||
|
FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); }
|
||||||
|
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
|
||||||
|
handler.on_dynamic_width(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_error(const char* message) {
|
||||||
|
handler.on_error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
SpecHandler& handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Handler>
|
||||||
|
FMT_CONSTEXPR const Char* parse_width(const Char* begin, const Char* end,
|
||||||
|
Handler&& handler) {
|
||||||
|
FMT_ASSERT(begin != end, "");
|
||||||
|
if ('0' <= *begin && *begin <= '9') {
|
||||||
|
handler.on_width(parse_nonnegative_int(begin, end, handler));
|
||||||
|
} else if (*begin == '{') {
|
||||||
|
++begin;
|
||||||
|
if (begin != end)
|
||||||
|
begin = parse_arg_id(begin, end, width_adapter<Handler, Char>(handler));
|
||||||
|
if (begin == end || *begin != '}')
|
||||||
|
return handler.on_error("invalid format string"), begin;
|
||||||
|
++begin;
|
||||||
|
}
|
||||||
|
return begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapts SpecHandler to IDHandler API for dynamic precision.
|
||||||
|
template <typename SpecHandler, typename Char> struct precision_adapter {
|
||||||
|
explicit FMT_CONSTEXPR precision_adapter(SpecHandler& h) : handler(h) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
|
||||||
|
FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); }
|
||||||
|
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
|
||||||
|
handler.on_dynamic_precision(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_error(const char* message) {
|
||||||
|
handler.on_error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
SpecHandler& handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Handler>
|
||||||
|
FMT_CONSTEXPR const Char* parse_precision(const Char* begin, const Char* end,
|
||||||
|
Handler&& handler) {
|
||||||
|
++begin;
|
||||||
|
auto c = begin != end ? *begin : Char();
|
||||||
|
if ('0' <= c && c <= '9') {
|
||||||
|
handler.on_precision(parse_nonnegative_int(begin, end, handler));
|
||||||
|
} else if (c == '{') {
|
||||||
|
++begin;
|
||||||
|
if (begin != end) {
|
||||||
|
begin =
|
||||||
|
parse_arg_id(begin, end, precision_adapter<Handler, Char>(handler));
|
||||||
|
}
|
||||||
|
if (begin == end || *begin++ != '}')
|
||||||
|
return handler.on_error("invalid format string"), begin;
|
||||||
|
} else {
|
||||||
|
return handler.on_error("missing precision specifier"), begin;
|
||||||
|
}
|
||||||
|
handler.end_precision();
|
||||||
|
return begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses standard format specifiers and sends notifications about parsed
|
||||||
|
// components to handler.
|
||||||
|
template <typename Char, typename SpecHandler>
|
||||||
|
FMT_CONSTEXPR_DECL FMT_INLINE const Char* parse_format_specs(
|
||||||
|
const Char* begin, const Char* end, SpecHandler&& handler) {
|
||||||
|
if (begin + 1 < end && begin[1] == '}' && is_ascii_letter(*begin) &&
|
||||||
|
*begin != 'L') {
|
||||||
|
handler.on_type(*begin++);
|
||||||
|
return begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (begin == end) return begin;
|
||||||
|
|
||||||
|
begin = parse_align(begin, end, handler);
|
||||||
|
if (begin == end) return begin;
|
||||||
|
|
||||||
|
// Parse sign.
|
||||||
|
switch (to_ascii(*begin)) {
|
||||||
|
case '+':
|
||||||
|
handler.on_plus();
|
||||||
|
++begin;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
handler.on_minus();
|
||||||
|
++begin;
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
handler.on_space();
|
||||||
|
++begin;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (begin == end) return begin;
|
||||||
|
|
||||||
|
if (*begin == '#') {
|
||||||
|
handler.on_hash();
|
||||||
|
if (++begin == end) return begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse zero flag.
|
||||||
|
if (*begin == '0') {
|
||||||
|
handler.on_zero();
|
||||||
|
if (++begin == end) return begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
begin = parse_width(begin, end, handler);
|
||||||
|
if (begin == end) return begin;
|
||||||
|
|
||||||
|
// Parse precision.
|
||||||
|
if (*begin == '.') {
|
||||||
|
begin = parse_precision(begin, end, handler);
|
||||||
|
if (begin == end) return begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*begin == 'L') {
|
||||||
|
handler.on_localized();
|
||||||
|
++begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse type.
|
||||||
|
if (begin != end && *begin != '}') handler.on_type(*begin++);
|
||||||
|
return begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the result via the out param to workaround gcc bug 77539.
|
||||||
|
template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
|
||||||
|
FMT_CONSTEXPR bool find(Ptr first, Ptr last, T value, Ptr& out) {
|
||||||
|
for (out = first; out != last; ++out) {
|
||||||
|
if (*out == value) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool find<false, char>(const char* first, const char* last, char value,
|
||||||
|
const char*& out) {
|
||||||
|
out = static_cast<const char*>(
|
||||||
|
std::memchr(first, value, to_unsigned(last - first)));
|
||||||
|
return out != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Handler, typename Char> struct id_adapter {
|
||||||
|
Handler& handler;
|
||||||
|
int arg_id;
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); }
|
||||||
|
FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); }
|
||||||
|
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
|
||||||
|
arg_id = handler.on_arg_id(id);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR void on_error(const char* message) {
|
||||||
|
handler.on_error(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Handler>
|
||||||
|
FMT_CONSTEXPR const Char* parse_replacement_field(const Char* begin,
|
||||||
|
const Char* end,
|
||||||
|
Handler&& handler) {
|
||||||
|
++begin;
|
||||||
|
if (begin == end) return handler.on_error("invalid format string"), end;
|
||||||
|
if (*begin == '}') {
|
||||||
|
handler.on_replacement_field(handler.on_arg_id(), begin);
|
||||||
|
} else if (*begin == '{') {
|
||||||
|
handler.on_text(begin, begin + 1);
|
||||||
|
} else {
|
||||||
|
auto adapter = id_adapter<Handler, Char>{handler, 0};
|
||||||
|
begin = parse_arg_id(begin, end, adapter);
|
||||||
|
Char c = begin != end ? *begin : Char();
|
||||||
|
if (c == '}') {
|
||||||
|
handler.on_replacement_field(adapter.arg_id, begin);
|
||||||
|
} else if (c == ':') {
|
||||||
|
begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
|
||||||
|
if (begin == end || *begin != '}')
|
||||||
|
return handler.on_error("unknown format specifier"), end;
|
||||||
|
} else {
|
||||||
|
return handler.on_error("missing '}' in format string"), end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return begin + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool IS_CONSTEXPR, typename Char, typename Handler>
|
||||||
|
FMT_CONSTEXPR_DECL FMT_INLINE void parse_format_string(
|
||||||
|
basic_string_view<Char> format_str, Handler&& handler) {
|
||||||
|
// this is most likely a name-lookup defect in msvc's modules implementation
|
||||||
|
using detail::find;
|
||||||
|
|
||||||
|
auto begin = format_str.data();
|
||||||
|
auto end = begin + format_str.size();
|
||||||
|
if (end - begin < 32) {
|
||||||
|
// Use a simple loop instead of memchr for small strings.
|
||||||
|
const Char* p = begin;
|
||||||
|
while (p != end) {
|
||||||
|
auto c = *p++;
|
||||||
|
if (c == '{') {
|
||||||
|
handler.on_text(begin, p - 1);
|
||||||
|
begin = p = parse_replacement_field(p - 1, end, handler);
|
||||||
|
} else if (c == '}') {
|
||||||
|
if (p == end || *p != '}')
|
||||||
|
return handler.on_error("unmatched '}' in format string");
|
||||||
|
handler.on_text(begin, p);
|
||||||
|
begin = ++p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handler.on_text(begin, end);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct writer {
|
||||||
|
FMT_CONSTEXPR void operator()(const Char* pbegin, const Char* pend) {
|
||||||
|
if (pbegin == pend) return;
|
||||||
|
for (;;) {
|
||||||
|
const Char* p = nullptr;
|
||||||
|
if (!find<IS_CONSTEXPR>(pbegin, pend, '}', p))
|
||||||
|
return handler_.on_text(pbegin, pend);
|
||||||
|
++p;
|
||||||
|
if (p == pend || *p != '}')
|
||||||
|
return handler_.on_error("unmatched '}' in format string");
|
||||||
|
handler_.on_text(pbegin, p);
|
||||||
|
pbegin = p + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Handler& handler_;
|
||||||
|
} write{handler};
|
||||||
|
while (begin != end) {
|
||||||
|
// Doing two passes with memchr (one for '{' and another for '}') is up to
|
||||||
|
// 2.5x faster than the naive one-pass implementation on big format strings.
|
||||||
|
const Char* p = begin;
|
||||||
|
if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, '{', p))
|
||||||
|
return write(begin, end);
|
||||||
|
write(begin, p);
|
||||||
|
begin = parse_replacement_field(p, end, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename ParseContext>
|
||||||
|
FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
|
||||||
|
ParseContext& ctx) {
|
||||||
|
using char_type = typename ParseContext::char_type;
|
||||||
|
using context = buffer_context<char_type>;
|
||||||
|
using mapped_type = conditional_t<
|
||||||
|
mapped_type_constant<T, context>::value != type::custom_type,
|
||||||
|
decltype(arg_mapper<context>().map(std::declval<const T&>())), T>;
|
||||||
|
auto f = conditional_t<has_formatter<mapped_type, context>::value,
|
||||||
|
formatter<mapped_type, char_type>,
|
||||||
|
fallback_formatter<T, char_type>>();
|
||||||
|
return f.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A parse context with extra argument id checks. It is only used at compile
|
||||||
|
// time because adding checks at runtime would introduce substantial overhead
|
||||||
|
// and would be redundant since argument ids are checked when arguments are
|
||||||
|
// retrieved anyway.
|
||||||
|
template <typename Char, typename ErrorHandler = error_handler>
|
||||||
|
class compile_parse_context
|
||||||
|
: public basic_format_parse_context<Char, ErrorHandler> {
|
||||||
|
private:
|
||||||
|
int num_args_;
|
||||||
|
using base = basic_format_parse_context<Char, ErrorHandler>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FMT_CONSTEXPR compile_parse_context(
|
||||||
|
basic_string_view<Char> format_str, int num_args = INT_MAX,
|
||||||
|
ErrorHandler eh = {})
|
||||||
|
: base(format_str, eh), num_args_(num_args) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR int next_arg_id() {
|
||||||
|
int id = base::next_arg_id();
|
||||||
|
if (id >= num_args_) this->on_error("argument not found");
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void check_arg_id(int id) {
|
||||||
|
base::check_arg_id(id);
|
||||||
|
if (id >= num_args_) this->on_error("argument not found");
|
||||||
|
}
|
||||||
|
using base::check_arg_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename ErrorHandler, typename... Args>
|
||||||
|
class format_string_checker {
|
||||||
|
public:
|
||||||
|
explicit FMT_CONSTEXPR format_string_checker(
|
||||||
|
basic_string_view<Char> format_str, ErrorHandler eh)
|
||||||
|
: context_(format_str, num_args, eh),
|
||||||
|
parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR int on_arg_id() { return context_.next_arg_id(); }
|
||||||
|
FMT_CONSTEXPR int on_arg_id(int id) { return context_.check_arg_id(id), id; }
|
||||||
|
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
|
||||||
|
on_error("compile-time checks don't support named arguments");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const Char* on_format_specs(int id, const Char* begin,
|
||||||
|
const Char*) {
|
||||||
|
advance_to(context_, begin);
|
||||||
|
// id >= 0 check is a workaround for gcc 10 bug (#2065).
|
||||||
|
return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_error(const char* message) {
|
||||||
|
context_.on_error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using parse_context_type = compile_parse_context<Char, ErrorHandler>;
|
||||||
|
enum { num_args = sizeof...(Args) };
|
||||||
|
|
||||||
|
// Format specifier parsing function.
|
||||||
|
using parse_func = const Char* (*)(parse_context_type&);
|
||||||
|
|
||||||
|
parse_context_type context_;
|
||||||
|
parse_func parse_funcs_[num_args > 0 ? num_args : 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Args, typename S,
|
||||||
|
enable_if_t<(is_compile_string<S>::value), int>>
|
||||||
|
void check_format_string(S format_str) {
|
||||||
|
FMT_CONSTEXPR_DECL auto s = to_string_view(format_str);
|
||||||
|
using checker = format_string_checker<typename S::char_type, error_handler,
|
||||||
|
remove_cvref_t<Args>...>;
|
||||||
|
FMT_CONSTEXPR_DECL bool invalid_format =
|
||||||
|
(parse_format_string<true>(s, checker(s, {})), true);
|
||||||
|
(void)invalid_format;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||||
std::basic_string<Char> vformat(
|
std::basic_string<Char> vformat(
|
||||||
basic_string_view<Char> format_str,
|
basic_string_view<Char> format_str,
|
||||||
|
@ -33,8 +33,8 @@
|
|||||||
#ifndef FMT_FORMAT_H_
|
#ifndef FMT_FORMAT_H_
|
||||||
#define FMT_FORMAT_H_
|
#define FMT_FORMAT_H_
|
||||||
|
|
||||||
#include <cerrno> // errno
|
#include <cerrno> // errno
|
||||||
#include <cmath> // std::signbit
|
#include <cmath> // std::signbit
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cwchar>
|
#include <cwchar>
|
||||||
#include <limits> // std::numeric_limits
|
#include <limits> // std::numeric_limits
|
||||||
@ -484,19 +484,6 @@ buffer_appender<OutChar> copy_str(InputIt begin, InputIt end,
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
FMT_CONSTEXPR int code_point_length(const Char* begin) {
|
|
||||||
if (const_check(sizeof(Char) != 1)) return 1;
|
|
||||||
constexpr char lengths[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0};
|
|
||||||
int len = lengths[static_cast<unsigned char>(*begin) >> 3];
|
|
||||||
|
|
||||||
// Compute the pointer to the next character early so that the next
|
|
||||||
// iteration can start working on the next character. Neither Clang
|
|
||||||
// nor GCC figure out this reordering on their own.
|
|
||||||
return len + !len;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A public domain branchless UTF-8 decoder by Christopher Wellons:
|
// A public domain branchless UTF-8 decoder by Christopher Wellons:
|
||||||
// https://github.com/skeeto/branchless-utf8
|
// https://github.com/skeeto/branchless-utf8
|
||||||
/* Decode the next character, c, from s, reporting errors in e.
|
/* Decode the next character, c, from s, reporting errors in e.
|
||||||
@ -1178,12 +1165,6 @@ template <typename Char> struct fill_t {
|
|||||||
};
|
};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
FMT_MODULE_EXPORT_BEGIN
|
FMT_MODULE_EXPORT_BEGIN
|
||||||
// We cannot use enum classes as bit fields because of a gcc bug
|
|
||||||
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414.
|
|
||||||
namespace align {
|
|
||||||
enum type { none, left, right, center, numeric };
|
|
||||||
}
|
|
||||||
using align_t = align::type;
|
|
||||||
|
|
||||||
namespace sign {
|
namespace sign {
|
||||||
enum type { none, minus, plus, space };
|
enum type { none, minus, plus, space };
|
||||||
@ -2300,29 +2281,6 @@ template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
|
|||||||
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
|
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses the range [begin, end) as an unsigned integer. This function assumes
|
|
||||||
// that the range is non-empty and the first character is a digit.
|
|
||||||
template <typename Char, typename ErrorHandler>
|
|
||||||
FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end,
|
|
||||||
ErrorHandler&& eh) {
|
|
||||||
FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
|
|
||||||
unsigned value = 0;
|
|
||||||
// Convert to unsigned to prevent a warning.
|
|
||||||
const unsigned max_int = to_unsigned(max_value<int>());
|
|
||||||
unsigned big = max_int / 10;
|
|
||||||
do {
|
|
||||||
// Check for overflow.
|
|
||||||
if (value > big) {
|
|
||||||
value = max_int + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
value = value * 10 + unsigned(*begin - '0');
|
|
||||||
++begin;
|
|
||||||
} while (begin != end && '0' <= *begin && *begin <= '9');
|
|
||||||
if (value > max_int) eh.on_error("number is too big");
|
|
||||||
return static_cast<int>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Context> class custom_formatter {
|
template <typename Context> class custom_formatter {
|
||||||
private:
|
private:
|
||||||
using char_type = typename Context::char_type;
|
using char_type = typename Context::char_type;
|
||||||
@ -2516,8 +2474,6 @@ FMT_CONSTEXPR int get_dynamic_spec(FormatArg arg, ErrorHandler eh) {
|
|||||||
return static_cast<int>(value);
|
return static_cast<int>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct auto_id {};
|
|
||||||
|
|
||||||
template <typename Context, typename ID>
|
template <typename Context, typename ID>
|
||||||
FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, ID id) {
|
FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, ID id) {
|
||||||
auto arg = ctx.arg(id);
|
auto arg = ctx.arg(id);
|
||||||
@ -2698,324 +2654,6 @@ FMT_CONSTEXPR_DECL FMT_INLINE const Char* parse_arg_id(const Char* begin,
|
|||||||
return begin;
|
return begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adapts SpecHandler to IDHandler API for dynamic width.
|
|
||||||
template <typename SpecHandler, typename Char> struct width_adapter {
|
|
||||||
explicit FMT_CONSTEXPR width_adapter(SpecHandler& h) : handler(h) {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
|
|
||||||
FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); }
|
|
||||||
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
|
|
||||||
handler.on_dynamic_width(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_error(const char* message) {
|
|
||||||
handler.on_error(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
SpecHandler& handler;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Adapts SpecHandler to IDHandler API for dynamic precision.
|
|
||||||
template <typename SpecHandler, typename Char> struct precision_adapter {
|
|
||||||
explicit FMT_CONSTEXPR precision_adapter(SpecHandler& h) : handler(h) {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
|
|
||||||
FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); }
|
|
||||||
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
|
|
||||||
handler.on_dynamic_precision(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_error(const char* message) {
|
|
||||||
handler.on_error(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
SpecHandler& handler;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char> constexpr bool is_ascii_letter(Char c) {
|
|
||||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Converts a character to ASCII. Returns a number > 127 on conversion failure.
|
|
||||||
template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
|
|
||||||
constexpr Char to_ascii(Char value) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
|
|
||||||
constexpr typename std::underlying_type<Char>::type to_ascii(Char value) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses fill and alignment.
|
|
||||||
template <typename Char, typename Handler>
|
|
||||||
FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
|
|
||||||
Handler&& handler) {
|
|
||||||
FMT_ASSERT(begin != end, "");
|
|
||||||
auto align = align::none;
|
|
||||||
auto p = begin + code_point_length(begin);
|
|
||||||
if (p >= end) p = begin;
|
|
||||||
for (;;) {
|
|
||||||
switch (to_ascii(*p)) {
|
|
||||||
case '<':
|
|
||||||
align = align::left;
|
|
||||||
break;
|
|
||||||
case '>':
|
|
||||||
align = align::right;
|
|
||||||
break;
|
|
||||||
case '^':
|
|
||||||
align = align::center;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (align != align::none) {
|
|
||||||
if (p != begin) {
|
|
||||||
auto c = *begin;
|
|
||||||
if (c == '{')
|
|
||||||
return handler.on_error("invalid fill character '{'"), begin;
|
|
||||||
handler.on_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
|
|
||||||
begin = p + 1;
|
|
||||||
} else
|
|
||||||
++begin;
|
|
||||||
handler.on_align(align);
|
|
||||||
break;
|
|
||||||
} else if (p == begin) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p = begin;
|
|
||||||
}
|
|
||||||
return begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename Handler>
|
|
||||||
FMT_CONSTEXPR const Char* parse_width(const Char* begin, const Char* end,
|
|
||||||
Handler&& handler) {
|
|
||||||
FMT_ASSERT(begin != end, "");
|
|
||||||
if ('0' <= *begin && *begin <= '9') {
|
|
||||||
handler.on_width(parse_nonnegative_int(begin, end, handler));
|
|
||||||
} else if (*begin == '{') {
|
|
||||||
++begin;
|
|
||||||
if (begin != end)
|
|
||||||
begin = parse_arg_id(begin, end, width_adapter<Handler, Char>(handler));
|
|
||||||
if (begin == end || *begin != '}')
|
|
||||||
return handler.on_error("invalid format string"), begin;
|
|
||||||
++begin;
|
|
||||||
}
|
|
||||||
return begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename Handler>
|
|
||||||
FMT_CONSTEXPR const Char* parse_precision(const Char* begin, const Char* end,
|
|
||||||
Handler&& handler) {
|
|
||||||
++begin;
|
|
||||||
auto c = begin != end ? *begin : Char();
|
|
||||||
if ('0' <= c && c <= '9') {
|
|
||||||
handler.on_precision(parse_nonnegative_int(begin, end, handler));
|
|
||||||
} else if (c == '{') {
|
|
||||||
++begin;
|
|
||||||
if (begin != end) {
|
|
||||||
begin =
|
|
||||||
parse_arg_id(begin, end, precision_adapter<Handler, Char>(handler));
|
|
||||||
}
|
|
||||||
if (begin == end || *begin++ != '}')
|
|
||||||
return handler.on_error("invalid format string"), begin;
|
|
||||||
} else {
|
|
||||||
return handler.on_error("missing precision specifier"), begin;
|
|
||||||
}
|
|
||||||
handler.end_precision();
|
|
||||||
return begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses standard format specifiers and sends notifications about parsed
|
|
||||||
// components to handler.
|
|
||||||
template <typename Char, typename SpecHandler>
|
|
||||||
FMT_CONSTEXPR_DECL FMT_INLINE const Char* parse_format_specs(
|
|
||||||
const Char* begin, const Char* end, SpecHandler&& handler) {
|
|
||||||
if (begin + 1 < end && begin[1] == '}' && is_ascii_letter(*begin) &&
|
|
||||||
*begin != 'L') {
|
|
||||||
handler.on_type(*begin++);
|
|
||||||
return begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (begin == end) return begin;
|
|
||||||
|
|
||||||
begin = parse_align(begin, end, handler);
|
|
||||||
if (begin == end) return begin;
|
|
||||||
|
|
||||||
// Parse sign.
|
|
||||||
switch (to_ascii(*begin)) {
|
|
||||||
case '+':
|
|
||||||
handler.on_plus();
|
|
||||||
++begin;
|
|
||||||
break;
|
|
||||||
case '-':
|
|
||||||
handler.on_minus();
|
|
||||||
++begin;
|
|
||||||
break;
|
|
||||||
case ' ':
|
|
||||||
handler.on_space();
|
|
||||||
++begin;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (begin == end) return begin;
|
|
||||||
|
|
||||||
if (*begin == '#') {
|
|
||||||
handler.on_hash();
|
|
||||||
if (++begin == end) return begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse zero flag.
|
|
||||||
if (*begin == '0') {
|
|
||||||
handler.on_zero();
|
|
||||||
if (++begin == end) return begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
begin = parse_width(begin, end, handler);
|
|
||||||
if (begin == end) return begin;
|
|
||||||
|
|
||||||
// Parse precision.
|
|
||||||
if (*begin == '.') {
|
|
||||||
begin = parse_precision(begin, end, handler);
|
|
||||||
if (begin == end) return begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*begin == 'L') {
|
|
||||||
handler.on_localized();
|
|
||||||
++begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse type.
|
|
||||||
if (begin != end && *begin != '}') handler.on_type(*begin++);
|
|
||||||
return begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the result via the out param to workaround gcc bug 77539.
|
|
||||||
template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
|
|
||||||
FMT_CONSTEXPR bool find(Ptr first, Ptr last, T value, Ptr& out) {
|
|
||||||
for (out = first; out != last; ++out) {
|
|
||||||
if (*out == value) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline bool find<false, char>(const char* first, const char* last, char value,
|
|
||||||
const char*& out) {
|
|
||||||
out = static_cast<const char*>(
|
|
||||||
std::memchr(first, value, detail::to_unsigned(last - first)));
|
|
||||||
return out != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Handler, typename Char> struct id_adapter {
|
|
||||||
Handler& handler;
|
|
||||||
int arg_id;
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); }
|
|
||||||
FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); }
|
|
||||||
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
|
|
||||||
arg_id = handler.on_arg_id(id);
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR void on_error(const char* message) {
|
|
||||||
handler.on_error(message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename Handler>
|
|
||||||
FMT_CONSTEXPR const Char* parse_replacement_field(const Char* begin,
|
|
||||||
const Char* end,
|
|
||||||
Handler&& handler) {
|
|
||||||
++begin;
|
|
||||||
if (begin == end) return handler.on_error("invalid format string"), end;
|
|
||||||
if (*begin == '}') {
|
|
||||||
handler.on_replacement_field(handler.on_arg_id(), begin);
|
|
||||||
} else if (*begin == '{') {
|
|
||||||
handler.on_text(begin, begin + 1);
|
|
||||||
} else {
|
|
||||||
auto adapter = id_adapter<Handler, Char>{handler, 0};
|
|
||||||
begin = parse_arg_id(begin, end, adapter);
|
|
||||||
Char c = begin != end ? *begin : Char();
|
|
||||||
if (c == '}') {
|
|
||||||
handler.on_replacement_field(adapter.arg_id, begin);
|
|
||||||
} else if (c == ':') {
|
|
||||||
begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
|
|
||||||
if (begin == end || *begin != '}')
|
|
||||||
return handler.on_error("unknown format specifier"), end;
|
|
||||||
} else {
|
|
||||||
return handler.on_error("missing '}' in format string"), end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return begin + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool IS_CONSTEXPR, typename Char, typename Handler>
|
|
||||||
FMT_CONSTEXPR_DECL FMT_INLINE void parse_format_string(
|
|
||||||
basic_string_view<Char> format_str, Handler&& handler) {
|
|
||||||
// this is most likely a name-lookup defect in msvc's modules implementation
|
|
||||||
using detail::find;
|
|
||||||
|
|
||||||
auto begin = format_str.data();
|
|
||||||
auto end = begin + format_str.size();
|
|
||||||
if (end - begin < 32) {
|
|
||||||
// Use a simple loop instead of memchr for small strings.
|
|
||||||
const Char* p = begin;
|
|
||||||
while (p != end) {
|
|
||||||
auto c = *p++;
|
|
||||||
if (c == '{') {
|
|
||||||
handler.on_text(begin, p - 1);
|
|
||||||
begin = p = parse_replacement_field(p - 1, end, handler);
|
|
||||||
} else if (c == '}') {
|
|
||||||
if (p == end || *p != '}')
|
|
||||||
return handler.on_error("unmatched '}' in format string");
|
|
||||||
handler.on_text(begin, p);
|
|
||||||
begin = ++p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handler.on_text(begin, end);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
struct writer {
|
|
||||||
FMT_CONSTEXPR void operator()(const Char* pbegin, const Char* pend) {
|
|
||||||
if (pbegin == pend) return;
|
|
||||||
for (;;) {
|
|
||||||
const Char* p = nullptr;
|
|
||||||
if (!find<IS_CONSTEXPR>(pbegin, pend, '}', p))
|
|
||||||
return handler_.on_text(pbegin, pend);
|
|
||||||
++p;
|
|
||||||
if (p == pend || *p != '}')
|
|
||||||
return handler_.on_error("unmatched '}' in format string");
|
|
||||||
handler_.on_text(pbegin, p);
|
|
||||||
pbegin = p + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Handler& handler_;
|
|
||||||
} write{handler};
|
|
||||||
while (begin != end) {
|
|
||||||
// Doing two passes with memchr (one for '{' and another for '}') is up to
|
|
||||||
// 2.5x faster than the naive one-pass implementation on big format strings.
|
|
||||||
const Char* p = begin;
|
|
||||||
if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, '{', p))
|
|
||||||
return write(begin, end);
|
|
||||||
write(begin, p);
|
|
||||||
begin = parse_replacement_field(p, end, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename ParseContext>
|
|
||||||
FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
|
|
||||||
ParseContext& ctx) {
|
|
||||||
using char_type = typename ParseContext::char_type;
|
|
||||||
using context = buffer_context<char_type>;
|
|
||||||
using mapped_type = conditional_t<
|
|
||||||
detail::mapped_type_constant<T, context>::value != type::custom_type,
|
|
||||||
decltype(arg_mapper<context>().map(std::declval<const T&>())), T>;
|
|
||||||
auto f = conditional_t<has_formatter<mapped_type, context>::value,
|
|
||||||
formatter<mapped_type, char_type>,
|
|
||||||
detail::fallback_formatter<T, char_type>>();
|
|
||||||
return f.parse(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename Char, typename Context>
|
template <typename OutputIt, typename Char, typename Context>
|
||||||
struct format_handler : detail::error_handler {
|
struct format_handler : detail::error_handler {
|
||||||
basic_format_parse_context<Char> parse_context;
|
basic_format_parse_context<Char> parse_context;
|
||||||
@ -3066,77 +2704,6 @@ struct format_handler : detail::error_handler {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// A parse context with extra argument id checks. It is only used at compile
|
|
||||||
// time because adding checks at runtime would introduce substantial overhead
|
|
||||||
// and would be redundant since argument ids are checked when arguments are
|
|
||||||
// retrieved anyway.
|
|
||||||
template <typename Char, typename ErrorHandler = error_handler>
|
|
||||||
class compile_parse_context
|
|
||||||
: public basic_format_parse_context<Char, ErrorHandler> {
|
|
||||||
private:
|
|
||||||
int num_args_;
|
|
||||||
using base = basic_format_parse_context<Char, ErrorHandler>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit FMT_CONSTEXPR compile_parse_context(
|
|
||||||
basic_string_view<Char> format_str, int num_args = max_value<int>(),
|
|
||||||
ErrorHandler eh = {})
|
|
||||||
: base(format_str, eh), num_args_(num_args) {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR int next_arg_id() {
|
|
||||||
int id = base::next_arg_id();
|
|
||||||
if (id >= num_args_) this->on_error("argument not found");
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void check_arg_id(int id) {
|
|
||||||
base::check_arg_id(id);
|
|
||||||
if (id >= num_args_) this->on_error("argument not found");
|
|
||||||
}
|
|
||||||
using base::check_arg_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename ErrorHandler, typename... Args>
|
|
||||||
class format_string_checker {
|
|
||||||
public:
|
|
||||||
explicit FMT_CONSTEXPR format_string_checker(
|
|
||||||
basic_string_view<Char> format_str, ErrorHandler eh)
|
|
||||||
: context_(format_str, num_args, eh),
|
|
||||||
parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR int on_arg_id() { return context_.next_arg_id(); }
|
|
||||||
FMT_CONSTEXPR int on_arg_id(int id) { return context_.check_arg_id(id), id; }
|
|
||||||
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
|
|
||||||
on_error("compile-time checks don't support named arguments");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR const Char* on_format_specs(int id, const Char* begin,
|
|
||||||
const Char*) {
|
|
||||||
advance_to(context_, begin);
|
|
||||||
// id >= 0 check is a workaround for gcc 10 bug (#2065).
|
|
||||||
return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_error(const char* message) {
|
|
||||||
context_.on_error(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
using parse_context_type = compile_parse_context<Char, ErrorHandler>;
|
|
||||||
enum { num_args = sizeof...(Args) };
|
|
||||||
|
|
||||||
// Format specifier parsing function.
|
|
||||||
using parse_func = const Char* (*)(parse_context_type&);
|
|
||||||
|
|
||||||
parse_context_type context_;
|
|
||||||
parse_func parse_funcs_[num_args > 0 ? num_args : 1];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Converts string literals to basic_string_view.
|
// Converts string literals to basic_string_view.
|
||||||
template <typename Char, size_t N>
|
template <typename Char, size_t N>
|
||||||
FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
|
FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
|
||||||
@ -3180,17 +2747,6 @@ FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
|
|||||||
*/
|
*/
|
||||||
#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string)
|
#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string)
|
||||||
|
|
||||||
template <typename... Args, typename S,
|
|
||||||
enable_if_t<(is_compile_string<S>::value), int>>
|
|
||||||
void check_format_string(S format_str) {
|
|
||||||
FMT_CONSTEXPR_DECL auto s = to_string_view(format_str);
|
|
||||||
using checker = format_string_checker<typename S::char_type, error_handler,
|
|
||||||
remove_cvref_t<Args>...>;
|
|
||||||
FMT_CONSTEXPR_DECL bool invalid_format =
|
|
||||||
(parse_format_string<true>(s, checker(s, {})), true);
|
|
||||||
(void)invalid_format;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <template <typename> class Handler, typename Context>
|
template <template <typename> class Handler, typename Context>
|
||||||
FMT_CONSTEXPR void handle_dynamic_spec(int& value,
|
FMT_CONSTEXPR void handle_dynamic_spec(int& value,
|
||||||
arg_ref<typename Context::char_type> ref,
|
arg_ref<typename Context::char_type> ref,
|
||||||
|
Reference in New Issue
Block a user