mirror of
https://github.com/fmtlib/fmt.git
synced 2025-03-12 07:13:29 +00:00
Move format string checks to core.h
This commit is contained in:
parent
6469b9037c
commit
8f1902c05a
@ -176,6 +176,24 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if __cplusplus == 201103L || __cplusplus == 201402L
|
||||
# if defined(__INTEL_COMPILER) || defined(__PGI)
|
||||
# define FMT_FALLTHROUGH
|
||||
# elif defined(__clang__)
|
||||
# define FMT_FALLTHROUGH [[clang::fallthrough]]
|
||||
# elif FMT_GCC_VERSION >= 700 && \
|
||||
(!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
|
||||
# define FMT_FALLTHROUGH [[gnu::fallthrough]]
|
||||
# else
|
||||
# define FMT_FALLTHROUGH
|
||||
# endif
|
||||
#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \
|
||||
(defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
|
||||
# define FMT_FALLTHROUGH [[fallthrough]]
|
||||
#else
|
||||
# define FMT_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
#ifndef FMT_DEPRECATED
|
||||
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
|
||||
# define FMT_DEPRECATED [[deprecated]]
|
||||
@ -197,6 +215,16 @@
|
||||
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
|
||||
#endif
|
||||
|
||||
#ifndef FMT_USE_FLOAT
|
||||
# define FMT_USE_FLOAT 1
|
||||
#endif
|
||||
#ifndef FMT_USE_DOUBLE
|
||||
# define FMT_USE_DOUBLE 1
|
||||
#endif
|
||||
#ifndef FMT_USE_LONG_DOUBLE
|
||||
# define FMT_USE_LONG_DOUBLE 1
|
||||
#endif
|
||||
|
||||
#ifndef FMT_INLINE
|
||||
# if FMT_GCC_VERSION || FMT_CLANG_VERSION
|
||||
# define FMT_INLINE inline __attribute__((always_inline))
|
||||
@ -242,12 +270,10 @@
|
||||
# define FMT_MODULE_EXPORT_END
|
||||
#endif
|
||||
#ifndef FMT_BEGIN_DETAIL_NAMESPACE
|
||||
# define FMT_BEGIN_DETAIL_NAMESPACE \
|
||||
namespace detail {
|
||||
# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
|
||||
#endif
|
||||
#ifndef FMT_END_DETAIL_NAMESPACE
|
||||
# define FMT_END_DETAIL_NAMESPACE \
|
||||
}
|
||||
# define FMT_END_DETAIL_NAMESPACE }
|
||||
#endif
|
||||
|
||||
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
|
||||
@ -2436,6 +2462,220 @@ class compile_parse_context
|
||||
using base::check_arg_id;
|
||||
};
|
||||
|
||||
template <typename ErrorHandler>
|
||||
FMT_CONSTEXPR void check_int_type_spec(char spec, ErrorHandler&& eh) {
|
||||
switch (spec) {
|
||||
case 0:
|
||||
case 'd':
|
||||
case 'x':
|
||||
case 'X':
|
||||
case 'b':
|
||||
case 'B':
|
||||
case 'o':
|
||||
case 'c':
|
||||
break;
|
||||
default:
|
||||
eh.on_error("invalid type specifier");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ErrorHandler>
|
||||
class char_specs_checker : public ErrorHandler {
|
||||
private:
|
||||
char type_;
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR char_specs_checker(char type, ErrorHandler eh)
|
||||
: ErrorHandler(eh), type_(type) {}
|
||||
|
||||
FMT_CONSTEXPR void on_int() { check_int_type_spec(type_, *this); }
|
||||
FMT_CONSTEXPR void on_char() {}
|
||||
};
|
||||
|
||||
template <typename Char, typename Handler>
|
||||
FMT_CONSTEXPR void handle_char_specs(const basic_format_specs<Char>& specs,
|
||||
Handler&& handler) {
|
||||
if (specs.type && specs.type != 'c') return handler.on_int();
|
||||
if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
|
||||
handler.on_error("invalid format specifier for char");
|
||||
handler.on_char();
|
||||
}
|
||||
|
||||
// A floating-point presentation format.
|
||||
enum class float_format : unsigned char {
|
||||
general, // General: exponent notation or fixed point based on magnitude.
|
||||
exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3.
|
||||
fixed, // Fixed point with the default precision of 6, e.g. 0.0012.
|
||||
hex
|
||||
};
|
||||
|
||||
struct float_specs {
|
||||
int precision;
|
||||
float_format format : 8;
|
||||
sign_t sign : 8;
|
||||
bool upper : 1;
|
||||
bool locale : 1;
|
||||
bool binary32 : 1;
|
||||
bool use_grisu : 1;
|
||||
bool showpoint : 1;
|
||||
};
|
||||
|
||||
template <typename ErrorHandler = error_handler, typename Char>
|
||||
FMT_CONSTEXPR float_specs parse_float_type_spec(
|
||||
const basic_format_specs<Char>& specs, ErrorHandler&& eh = {}) {
|
||||
auto result = float_specs();
|
||||
result.showpoint = specs.alt;
|
||||
result.locale = specs.localized;
|
||||
switch (specs.type) {
|
||||
case 0:
|
||||
result.format = float_format::general;
|
||||
break;
|
||||
case 'G':
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'g':
|
||||
result.format = float_format::general;
|
||||
break;
|
||||
case 'E':
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'e':
|
||||
result.format = float_format::exp;
|
||||
result.showpoint |= specs.precision != 0;
|
||||
break;
|
||||
case 'F':
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'f':
|
||||
result.format = float_format::fixed;
|
||||
result.showpoint |= specs.precision != 0;
|
||||
break;
|
||||
case 'A':
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'a':
|
||||
result.format = float_format::hex;
|
||||
break;
|
||||
default:
|
||||
eh.on_error("invalid type specifier");
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename ErrorHandler> class numeric_specs_checker {
|
||||
public:
|
||||
FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, detail::type arg_type)
|
||||
: error_handler_(eh), arg_type_(arg_type) {}
|
||||
|
||||
FMT_CONSTEXPR void require_numeric_argument() {
|
||||
if (!is_arithmetic_type(arg_type_))
|
||||
error_handler_.on_error("format specifier requires numeric argument");
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void check_sign() {
|
||||
require_numeric_argument();
|
||||
if (is_integral_type(arg_type_) && arg_type_ != type::int_type &&
|
||||
arg_type_ != type::long_long_type && arg_type_ != type::char_type) {
|
||||
error_handler_.on_error("format specifier requires signed argument");
|
||||
}
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void check_precision() {
|
||||
if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type)
|
||||
error_handler_.on_error("precision not allowed for this argument type");
|
||||
}
|
||||
|
||||
private:
|
||||
ErrorHandler& error_handler_;
|
||||
detail::type arg_type_;
|
||||
};
|
||||
|
||||
template <typename ErrorHandler>
|
||||
class cstring_type_checker : public ErrorHandler {
|
||||
public:
|
||||
FMT_CONSTEXPR explicit cstring_type_checker(ErrorHandler eh)
|
||||
: ErrorHandler(eh) {}
|
||||
|
||||
FMT_CONSTEXPR void on_string() {}
|
||||
FMT_CONSTEXPR void on_pointer() {}
|
||||
};
|
||||
|
||||
template <typename Char, typename Handler>
|
||||
FMT_CONSTEXPR void handle_cstring_type_spec(Char spec, Handler&& handler) {
|
||||
if (spec == 0 || spec == 's')
|
||||
handler.on_string();
|
||||
else if (spec == 'p')
|
||||
handler.on_pointer();
|
||||
else
|
||||
handler.on_error("invalid type specifier");
|
||||
}
|
||||
|
||||
template <typename Char, typename ErrorHandler>
|
||||
FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) {
|
||||
if (spec != 0 && spec != 's') eh.on_error("invalid type specifier");
|
||||
}
|
||||
|
||||
template <typename Char, typename ErrorHandler>
|
||||
FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) {
|
||||
if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier");
|
||||
}
|
||||
|
||||
// A format specifier handler that checks if specifiers are consistent with the
|
||||
// argument type.
|
||||
template <typename Handler> class specs_checker : public Handler {
|
||||
private:
|
||||
numeric_specs_checker<Handler> checker_;
|
||||
|
||||
// Suppress an MSVC warning about using this in initializer list.
|
||||
FMT_CONSTEXPR Handler& error_handler() { return *this; }
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type)
|
||||
: Handler(handler), checker_(error_handler(), arg_type) {}
|
||||
|
||||
FMT_CONSTEXPR specs_checker(const specs_checker& other)
|
||||
: Handler(other), checker_(error_handler(), other.arg_type_) {}
|
||||
|
||||
FMT_CONSTEXPR void on_align(align_t align) {
|
||||
if (align == align::numeric) checker_.require_numeric_argument();
|
||||
Handler::on_align(align);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_plus() {
|
||||
checker_.check_sign();
|
||||
Handler::on_plus();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_minus() {
|
||||
checker_.check_sign();
|
||||
Handler::on_minus();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_space() {
|
||||
checker_.check_sign();
|
||||
Handler::on_space();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_hash() {
|
||||
checker_.require_numeric_argument();
|
||||
Handler::on_hash();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_localized() {
|
||||
checker_.require_numeric_argument();
|
||||
Handler::on_localized();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_zero() {
|
||||
checker_.require_numeric_argument();
|
||||
Handler::on_zero();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void end_precision() { checker_.check_precision(); }
|
||||
};
|
||||
|
||||
constexpr int invalid_arg_index = -1;
|
||||
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
@ -2446,7 +2686,7 @@ constexpr int get_arg_index_by_name(basic_string_view<Char> name) {
|
||||
}
|
||||
if constexpr (sizeof...(Args) > 0)
|
||||
return get_arg_index_by_name<N + 1, Args...>(name);
|
||||
(void)name;
|
||||
(void)name; // Workaround an MSVC bug about "unused" parameter.
|
||||
return invalid_arg_index;
|
||||
}
|
||||
#endif
|
||||
@ -2489,7 +2729,7 @@ class format_string_checker {
|
||||
|
||||
FMT_CONSTEXPR const Char* on_format_specs(int id, const Char* begin,
|
||||
const Char*) {
|
||||
advance_to(context_, begin);
|
||||
context_.advance_to(context_.begin() + (begin - &*context_.begin()));
|
||||
// id >= 0 check is a workaround for gcc 10 bug (#2065).
|
||||
return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin;
|
||||
}
|
||||
@ -2584,9 +2824,91 @@ FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
|
||||
#ifndef _WIN32
|
||||
inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
|
||||
#endif
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
// A formatter specialization for the core types corresponding to detail::type
|
||||
// constants.
|
||||
template <typename T, typename Char>
|
||||
struct formatter<T, Char,
|
||||
enable_if_t<detail::type_constant<T, Char>::value !=
|
||||
detail::type::custom_type>> {
|
||||
FMT_CONSTEXPR formatter() = default;
|
||||
|
||||
// Parses format specifiers stopping either at the end of the range or at the
|
||||
// terminating '}'.
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
auto begin = ctx.begin(), end = ctx.end();
|
||||
if (begin == end) return begin;
|
||||
using handler_type = detail::dynamic_specs_handler<ParseContext>;
|
||||
auto type = detail::type_constant<T, Char>::value;
|
||||
detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
|
||||
type);
|
||||
auto it = detail::parse_format_specs(begin, end, handler);
|
||||
auto eh = ctx.error_handler();
|
||||
switch (type) {
|
||||
case detail::type::none_type:
|
||||
FMT_ASSERT(false, "invalid argument type");
|
||||
break;
|
||||
case detail::type::bool_type:
|
||||
if (!specs_.type || specs_.type == 's') break;
|
||||
FMT_FALLTHROUGH;
|
||||
case detail::type::int_type:
|
||||
case detail::type::uint_type:
|
||||
case detail::type::long_long_type:
|
||||
case detail::type::ulong_long_type:
|
||||
case detail::type::int128_type:
|
||||
case detail::type::uint128_type:
|
||||
detail::check_int_type_spec(specs_.type, eh);
|
||||
break;
|
||||
case detail::type::char_type:
|
||||
detail::handle_char_specs(
|
||||
specs_, detail::char_specs_checker<decltype(eh)>(specs_.type, eh));
|
||||
break;
|
||||
case detail::type::float_type:
|
||||
if (detail::const_check(FMT_USE_FLOAT))
|
||||
detail::parse_float_type_spec(specs_, eh);
|
||||
else
|
||||
FMT_ASSERT(false, "float support disabled");
|
||||
break;
|
||||
case detail::type::double_type:
|
||||
if (detail::const_check(FMT_USE_DOUBLE))
|
||||
detail::parse_float_type_spec(specs_, eh);
|
||||
else
|
||||
FMT_ASSERT(false, "double support disabled");
|
||||
break;
|
||||
case detail::type::long_double_type:
|
||||
if (detail::const_check(FMT_USE_LONG_DOUBLE))
|
||||
detail::parse_float_type_spec(specs_, eh);
|
||||
else
|
||||
FMT_ASSERT(false, "long double support disabled");
|
||||
break;
|
||||
case detail::type::cstring_type:
|
||||
detail::handle_cstring_type_spec(
|
||||
specs_.type, detail::cstring_type_checker<decltype(eh)>(eh));
|
||||
break;
|
||||
case detail::type::string_type:
|
||||
detail::check_string_type_spec(specs_.type, eh);
|
||||
break;
|
||||
case detail::type::pointer_type:
|
||||
detail::check_pointer_type_spec(specs_.type, eh);
|
||||
break;
|
||||
case detail::type::custom_type:
|
||||
// Custom format specifiers should be checked in parse functions of
|
||||
// formatter specializations.
|
||||
break;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
FMT_CONSTEXPR FMT_INLINE auto format(const T& val, FormatContext& ctx) const
|
||||
-> decltype(ctx.out());
|
||||
|
||||
private:
|
||||
detail::dynamic_format_specs<Char> specs_;
|
||||
};
|
||||
|
||||
/** Formats a string and writes the output to ``out``. */
|
||||
// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with
|
||||
// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
|
||||
|
@ -77,24 +77,6 @@
|
||||
# define FMT_MSC_DEFAULT
|
||||
#endif
|
||||
|
||||
#if __cplusplus == 201103L || __cplusplus == 201402L
|
||||
# if defined(__INTEL_COMPILER) || defined(__PGI)
|
||||
# define FMT_FALLTHROUGH
|
||||
# elif defined(__clang__)
|
||||
# define FMT_FALLTHROUGH [[clang::fallthrough]]
|
||||
# elif FMT_GCC_VERSION >= 700 && \
|
||||
(!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
|
||||
# define FMT_FALLTHROUGH [[gnu::fallthrough]]
|
||||
# else
|
||||
# define FMT_FALLTHROUGH
|
||||
# endif
|
||||
#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \
|
||||
(defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
|
||||
# define FMT_FALLTHROUGH [[fallthrough]]
|
||||
#else
|
||||
# define FMT_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
#ifndef FMT_THROW
|
||||
# if FMT_EXCEPTIONS
|
||||
# if FMT_MSC_VER || FMT_NVCC
|
||||
@ -139,18 +121,6 @@ FMT_END_NAMESPACE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef FMT_USE_FLOAT
|
||||
# define FMT_USE_FLOAT 1
|
||||
#endif
|
||||
|
||||
#ifndef FMT_USE_DOUBLE
|
||||
# define FMT_USE_DOUBLE 1
|
||||
#endif
|
||||
|
||||
#ifndef FMT_USE_LONG_DOUBLE
|
||||
# define FMT_USE_LONG_DOUBLE 1
|
||||
#endif
|
||||
|
||||
// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of
|
||||
// integer formatter template instantiations to just one by only using the
|
||||
// largest integer type. This results in a reduction in binary size but will
|
||||
@ -1204,25 +1174,6 @@ constexpr typename dragonbox::float_info<T>::carrier_uint exponent_mask() {
|
||||
<< dragonbox::float_info<T>::significand_bits;
|
||||
}
|
||||
|
||||
// A floating-point presentation format.
|
||||
enum class float_format : unsigned char {
|
||||
general, // General: exponent notation or fixed point based on magnitude.
|
||||
exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3.
|
||||
fixed, // Fixed point with the default precision of 6, e.g. 0.0012.
|
||||
hex
|
||||
};
|
||||
|
||||
struct float_specs {
|
||||
int precision;
|
||||
float_format format : 8;
|
||||
sign_t sign : 8;
|
||||
bool upper : 1;
|
||||
bool locale : 1;
|
||||
bool binary32 : 1;
|
||||
bool use_grisu : 1;
|
||||
bool showpoint : 1;
|
||||
};
|
||||
|
||||
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
|
||||
template <typename Char, typename It> It write_exponent(int exp, It it) {
|
||||
FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
|
||||
@ -1255,119 +1206,6 @@ int snprintf_float(T value, int precision, float_specs specs,
|
||||
template <typename T> T promote_float(T value) { return value; }
|
||||
inline double promote_float(float value) { return static_cast<double>(value); }
|
||||
|
||||
template <typename ErrorHandler = error_handler, typename Char>
|
||||
FMT_CONSTEXPR float_specs parse_float_type_spec(
|
||||
const basic_format_specs<Char>& specs, ErrorHandler&& eh = {}) {
|
||||
auto result = float_specs();
|
||||
result.showpoint = specs.alt;
|
||||
result.locale = specs.localized;
|
||||
switch (specs.type) {
|
||||
case 0:
|
||||
result.format = float_format::general;
|
||||
break;
|
||||
case 'G':
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'g':
|
||||
result.format = float_format::general;
|
||||
break;
|
||||
case 'E':
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'e':
|
||||
result.format = float_format::exp;
|
||||
result.showpoint |= specs.precision != 0;
|
||||
break;
|
||||
case 'F':
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'f':
|
||||
result.format = float_format::fixed;
|
||||
result.showpoint |= specs.precision != 0;
|
||||
break;
|
||||
case 'A':
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'a':
|
||||
result.format = float_format::hex;
|
||||
break;
|
||||
default:
|
||||
eh.on_error("invalid type specifier");
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename ErrorHandler>
|
||||
FMT_CONSTEXPR void check_int_type_spec(char spec, ErrorHandler&& eh) {
|
||||
switch (spec) {
|
||||
case 0:
|
||||
case 'd':
|
||||
case 'x':
|
||||
case 'X':
|
||||
case 'b':
|
||||
case 'B':
|
||||
case 'o':
|
||||
case 'c':
|
||||
break;
|
||||
default:
|
||||
eh.on_error("invalid type specifier");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char, typename Handler>
|
||||
FMT_CONSTEXPR void handle_char_specs(const basic_format_specs<Char>& specs,
|
||||
Handler&& handler) {
|
||||
if (specs.type && specs.type != 'c') return handler.on_int();
|
||||
if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
|
||||
handler.on_error("invalid format specifier for char");
|
||||
handler.on_char();
|
||||
}
|
||||
|
||||
template <typename Char, typename Handler>
|
||||
FMT_CONSTEXPR void handle_cstring_type_spec(Char spec, Handler&& handler) {
|
||||
if (spec == 0 || spec == 's')
|
||||
handler.on_string();
|
||||
else if (spec == 'p')
|
||||
handler.on_pointer();
|
||||
else
|
||||
handler.on_error("invalid type specifier");
|
||||
}
|
||||
|
||||
template <typename Char, typename ErrorHandler>
|
||||
FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) {
|
||||
if (spec != 0 && spec != 's') eh.on_error("invalid type specifier");
|
||||
}
|
||||
|
||||
template <typename Char, typename ErrorHandler>
|
||||
FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) {
|
||||
if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier");
|
||||
}
|
||||
|
||||
template <typename ErrorHandler>
|
||||
class char_specs_checker : public ErrorHandler {
|
||||
private:
|
||||
char type_;
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR char_specs_checker(char type, ErrorHandler eh)
|
||||
: ErrorHandler(eh), type_(type) {}
|
||||
|
||||
FMT_CONSTEXPR void on_int() { check_int_type_spec(type_, *this); }
|
||||
FMT_CONSTEXPR void on_char() {}
|
||||
};
|
||||
|
||||
template <typename ErrorHandler>
|
||||
class cstring_type_checker : public ErrorHandler {
|
||||
public:
|
||||
FMT_CONSTEXPR explicit cstring_type_checker(ErrorHandler eh)
|
||||
: ErrorHandler(eh) {}
|
||||
|
||||
FMT_CONSTEXPR void on_string() {}
|
||||
FMT_CONSTEXPR void on_pointer() {}
|
||||
};
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
FMT_NOINLINE FMT_CONSTEXPR OutputIt fill(OutputIt it, size_t n,
|
||||
const fill_t<Char>& fill) {
|
||||
@ -2315,88 +2153,6 @@ template <typename ErrorHandler> class precision_checker {
|
||||
ErrorHandler& handler_;
|
||||
};
|
||||
|
||||
template <typename ErrorHandler> class numeric_specs_checker {
|
||||
public:
|
||||
FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, detail::type arg_type)
|
||||
: error_handler_(eh), arg_type_(arg_type) {}
|
||||
|
||||
FMT_CONSTEXPR void require_numeric_argument() {
|
||||
if (!is_arithmetic_type(arg_type_))
|
||||
error_handler_.on_error("format specifier requires numeric argument");
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void check_sign() {
|
||||
require_numeric_argument();
|
||||
if (is_integral_type(arg_type_) && arg_type_ != type::int_type &&
|
||||
arg_type_ != type::long_long_type && arg_type_ != type::char_type) {
|
||||
error_handler_.on_error("format specifier requires signed argument");
|
||||
}
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void check_precision() {
|
||||
if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type)
|
||||
error_handler_.on_error("precision not allowed for this argument type");
|
||||
}
|
||||
|
||||
private:
|
||||
ErrorHandler& error_handler_;
|
||||
detail::type arg_type_;
|
||||
};
|
||||
|
||||
// A format specifier handler that checks if specifiers are consistent with the
|
||||
// argument type.
|
||||
template <typename Handler> class specs_checker : public Handler {
|
||||
private:
|
||||
numeric_specs_checker<Handler> checker_;
|
||||
|
||||
// Suppress an MSVC warning about using this in initializer list.
|
||||
FMT_CONSTEXPR Handler& error_handler() { return *this; }
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type)
|
||||
: Handler(handler), checker_(error_handler(), arg_type) {}
|
||||
|
||||
FMT_CONSTEXPR specs_checker(const specs_checker& other)
|
||||
: Handler(other), checker_(error_handler(), other.arg_type_) {}
|
||||
|
||||
FMT_CONSTEXPR void on_align(align_t align) {
|
||||
if (align == align::numeric) checker_.require_numeric_argument();
|
||||
Handler::on_align(align);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_plus() {
|
||||
checker_.check_sign();
|
||||
Handler::on_plus();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_minus() {
|
||||
checker_.check_sign();
|
||||
Handler::on_minus();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_space() {
|
||||
checker_.check_sign();
|
||||
Handler::on_space();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_hash() {
|
||||
checker_.require_numeric_argument();
|
||||
Handler::on_hash();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_localized() {
|
||||
checker_.require_numeric_argument();
|
||||
Handler::on_localized();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_zero() {
|
||||
checker_.require_numeric_argument();
|
||||
Handler::on_zero();
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void end_precision() { checker_.check_precision(); }
|
||||
};
|
||||
|
||||
template <template <typename> class Handler, typename FormatArg,
|
||||
typename ErrorHandler>
|
||||
FMT_CONSTEXPR int get_dynamic_spec(FormatArg arg, ErrorHandler eh) {
|
||||
@ -2491,7 +2247,8 @@ struct format_handler : detail::error_handler {
|
||||
const Char* on_format_specs(int id, const Char* begin, const Char* end) {
|
||||
auto arg = get_arg(context, id);
|
||||
if (arg.type() == type::custom_type) {
|
||||
advance_to(parse_context, begin);
|
||||
parse_context.advance_to(parse_context.begin() +
|
||||
(begin - &*parse_context.begin()));
|
||||
visit_format_arg(custom_formatter<Context>(parse_context, context), arg);
|
||||
return parse_context.begin();
|
||||
}
|
||||
@ -2650,99 +2407,25 @@ class format_int {
|
||||
std::string str() const { return std::string(str_, size()); }
|
||||
};
|
||||
|
||||
// A formatter specialization for the core types corresponding to detail::type
|
||||
// constants.
|
||||
template <typename T, typename Char>
|
||||
struct formatter<T, Char,
|
||||
enable_if_t<detail::type_constant<T, Char>::value !=
|
||||
detail::type::custom_type>> {
|
||||
FMT_CONSTEXPR formatter() = default;
|
||||
|
||||
// Parses format specifiers stopping either at the end of the range or at the
|
||||
// terminating '}'.
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
auto begin = ctx.begin(), end = ctx.end();
|
||||
if (begin == end) return begin;
|
||||
using handler_type = detail::dynamic_specs_handler<ParseContext>;
|
||||
auto type = detail::type_constant<T, Char>::value;
|
||||
detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
|
||||
type);
|
||||
auto it = detail::parse_format_specs(begin, end, handler);
|
||||
auto eh = ctx.error_handler();
|
||||
switch (type) {
|
||||
case detail::type::none_type:
|
||||
FMT_ASSERT(false, "invalid argument type");
|
||||
break;
|
||||
case detail::type::bool_type:
|
||||
if (!specs_.type || specs_.type == 's') break;
|
||||
FMT_FALLTHROUGH;
|
||||
case detail::type::int_type:
|
||||
case detail::type::uint_type:
|
||||
case detail::type::long_long_type:
|
||||
case detail::type::ulong_long_type:
|
||||
case detail::type::int128_type:
|
||||
case detail::type::uint128_type:
|
||||
detail::check_int_type_spec(specs_.type, eh);
|
||||
break;
|
||||
case detail::type::char_type:
|
||||
detail::handle_char_specs(
|
||||
specs_, detail::char_specs_checker<decltype(eh)>(specs_.type, eh));
|
||||
break;
|
||||
case detail::type::float_type:
|
||||
if (detail::const_check(FMT_USE_FLOAT))
|
||||
detail::parse_float_type_spec(specs_, eh);
|
||||
else
|
||||
FMT_ASSERT(false, "float support disabled");
|
||||
break;
|
||||
case detail::type::double_type:
|
||||
if (detail::const_check(FMT_USE_DOUBLE))
|
||||
detail::parse_float_type_spec(specs_, eh);
|
||||
else
|
||||
FMT_ASSERT(false, "double support disabled");
|
||||
break;
|
||||
case detail::type::long_double_type:
|
||||
if (detail::const_check(FMT_USE_LONG_DOUBLE))
|
||||
detail::parse_float_type_spec(specs_, eh);
|
||||
else
|
||||
FMT_ASSERT(false, "long double support disabled");
|
||||
break;
|
||||
case detail::type::cstring_type:
|
||||
detail::handle_cstring_type_spec(
|
||||
specs_.type, detail::cstring_type_checker<decltype(eh)>(eh));
|
||||
break;
|
||||
case detail::type::string_type:
|
||||
detail::check_string_type_spec(specs_.type, eh);
|
||||
break;
|
||||
case detail::type::pointer_type:
|
||||
detail::check_pointer_type_spec(specs_.type, eh);
|
||||
break;
|
||||
case detail::type::custom_type:
|
||||
// Custom format specifiers should be checked in parse functions of
|
||||
// formatter specializations.
|
||||
break;
|
||||
}
|
||||
return it;
|
||||
template <typename FormatContext>
|
||||
FMT_CONSTEXPR FMT_INLINE auto
|
||||
formatter<T, Char,
|
||||
enable_if_t<detail::type_constant<T, Char>::value !=
|
||||
detail::type::custom_type>>::format(const T& val,
|
||||
FormatContext& ctx)
|
||||
const -> decltype(ctx.out()) {
|
||||
if (specs_.width_ref.kind != detail::arg_id_kind::none ||
|
||||
specs_.precision_ref.kind != detail::arg_id_kind::none) {
|
||||
auto specs = specs_;
|
||||
detail::handle_dynamic_spec<detail::width_checker>(specs.width,
|
||||
specs.width_ref, ctx);
|
||||
detail::handle_dynamic_spec<detail::precision_checker>(
|
||||
specs.precision, specs.precision_ref, ctx);
|
||||
return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
FMT_CONSTEXPR FMT_INLINE auto format(const T& val, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
if (specs_.width_ref.kind != detail::arg_id_kind::none ||
|
||||
specs_.precision_ref.kind != detail::arg_id_kind::none) {
|
||||
auto specs = specs_;
|
||||
detail::handle_dynamic_spec<detail::width_checker>(specs.width,
|
||||
specs.width_ref, ctx);
|
||||
detail::handle_dynamic_spec<detail::precision_checker>(
|
||||
specs.precision, specs.precision_ref, ctx);
|
||||
return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
|
||||
}
|
||||
return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
|
||||
}
|
||||
|
||||
private:
|
||||
detail::dynamic_format_specs<Char> specs_;
|
||||
};
|
||||
return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
|
||||
}
|
||||
|
||||
#define FMT_FORMAT_AS(Type, Base) \
|
||||
template <typename Char> \
|
||||
@ -2852,12 +2535,6 @@ template <typename Char = char> class dynamic_formatter {
|
||||
const Char* format_str_;
|
||||
};
|
||||
|
||||
template <typename Char, typename ErrorHandler>
|
||||
FMT_CONSTEXPR void advance_to(
|
||||
basic_format_parse_context<Char, ErrorHandler>& ctx, const Char* p) {
|
||||
ctx.advance_to(ctx.begin() + (p - &*ctx.begin()));
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Converts ``p`` to ``const void*`` for pointer formatting.
|
||||
|
Loading…
x
Reference in New Issue
Block a user