Reduce the number of nontrivial formatter instantiations

This commit is contained in:
Victor Zverovich 2019-06-06 19:06:07 -07:00
parent f5f3ffac59
commit 3fdba04924
3 changed files with 80 additions and 27 deletions

View File

@ -632,6 +632,28 @@ enum type {
custom_type custom_type
}; };
// Maps core type T to the corresponding type enum constant.
template <typename T, typename Char>
struct type_constant : std::integral_constant<type, none_type> {};
#define FMT_TYPE_CONSTANT(Type, constant) \
template <typename Char> \
struct type_constant<Type, Char> : std::integral_constant<type, constant> {}
FMT_TYPE_CONSTANT(int, int_type);
FMT_TYPE_CONSTANT(unsigned, uint_type);
FMT_TYPE_CONSTANT(long long, long_long_type);
FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
FMT_TYPE_CONSTANT(bool, bool_type);
FMT_TYPE_CONSTANT(Char, char_type);
FMT_TYPE_CONSTANT(double, double_type);
FMT_TYPE_CONSTANT(long double, long_double_type);
FMT_TYPE_CONSTANT(const Char*, cstring_type);
FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
FMT_TYPE_CONSTANT(const void*, pointer_type);
#undef FMT_TYPE_CONSTANT
FMT_CONSTEXPR bool is_integral(type t) { FMT_CONSTEXPR bool is_integral(type t) {
FMT_ASSERT(t != internal::named_arg_type, "invalid argument type"); FMT_ASSERT(t != internal::named_arg_type, "invalid argument type");
return t > internal::none_type && t <= internal::last_integer_type; return t > internal::none_type && t <= internal::last_integer_type;

View File

@ -2179,7 +2179,7 @@ FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
ParseContext& ctx) { ParseContext& ctx) {
// GCC 7.2 requires initializer. // GCC 7.2 requires initializer.
typedef typename ParseContext::char_type char_type; typedef typename ParseContext::char_type char_type;
conditional_t<is_formattable<T, format_context>::value, conditional_t<is_formattable<T, buffer_context<char_type>>::value,
formatter<T, char_type>, formatter<T, char_type>,
internal::fallback_formatter<T, char_type>> internal::fallback_formatter<T, char_type>>
f; f;
@ -2255,12 +2255,6 @@ void check_format_string(S format_str) {
(void)invalid_format; (void)invalid_format;
} }
// Specifies whether to format T using the standard formatter.
// It is not possible to use get_type in formatter specialization directly
// because of a bug in MSVC.
template <typename Context, typename T>
using format_type = bool_constant<get_type<Context, T>::value != custom_type>;
template <template <typename> class Handler, typename Spec, typename Context> template <template <typename> class Handler, typename Spec, typename Context>
void handle_dynamic_spec(Spec& value, arg_ref<typename Context::char_type> ref, void handle_dynamic_spec(Spec& value, arg_ref<typename Context::char_type> ref,
Context& ctx, Context& ctx,
@ -3041,24 +3035,24 @@ class format_int {
std::string str() const { return std::string(str_, size()); } std::string str() const { return std::string(str_, size()); }
}; };
// Formatter of objects of type T. // formatter specializations for the core types corresponding to internal::type
// constants.
template <typename T, typename Char> template <typename T, typename Char>
struct formatter< struct formatter<T, Char,
T, Char, enable_if_t<internal::type_constant<T, Char>::value !=
enable_if_t<internal::format_type<buffer_context<Char>, T>::value>> { internal::none_type>> {
FMT_CONSTEXPR formatter() : format_str_(nullptr) {} FMT_CONSTEXPR formatter() : format_str_(nullptr) {}
// Parses format specifiers stopping either at the end of the range or at the // Parses format specifiers stopping either at the end of the range or at the
// terminating '}'. // terminating '}'.
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext& ctx) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
format_str_ = ctx.begin(); format_str_ = ctx.begin();
typedef internal::dynamic_specs_handler<ParseContext> handler_type; using handler_type = internal::dynamic_specs_handler<ParseContext>;
auto type = internal::get_type<buffer_context<Char>, T>::value; auto type = internal::type_constant<T, Char>::value;
internal::specs_checker<handler_type> handler(handler_type(specs_, ctx), internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
type); type);
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler); auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
auto type_spec = specs_.type;
auto eh = ctx.error_handler(); auto eh = ctx.error_handler();
switch (type) { switch (type) {
case internal::none_type: case internal::none_type:
@ -3070,27 +3064,27 @@ struct formatter<
case internal::long_long_type: case internal::long_long_type:
case internal::ulong_long_type: case internal::ulong_long_type:
case internal::bool_type: case internal::bool_type:
handle_int_type_spec(type_spec, handle_int_type_spec(specs_.type,
internal::int_type_checker<decltype(eh)>(eh)); internal::int_type_checker<decltype(eh)>(eh));
break; break;
case internal::char_type: case internal::char_type:
handle_char_specs( handle_char_specs(
&specs_, internal::char_specs_checker<decltype(eh)>(type_spec, eh)); &specs_, internal::char_specs_checker<decltype(eh)>(specs_.type, eh));
break; break;
case internal::double_type: case internal::double_type:
case internal::long_double_type: case internal::long_double_type:
handle_float_type_spec(type_spec, handle_float_type_spec(specs_.type,
internal::float_type_checker<decltype(eh)>(eh)); internal::float_type_checker<decltype(eh)>(eh));
break; break;
case internal::cstring_type: case internal::cstring_type:
internal::handle_cstring_type_spec( internal::handle_cstring_type_spec(
type_spec, internal::cstring_type_checker<decltype(eh)>(eh)); specs_.type, internal::cstring_type_checker<decltype(eh)>(eh));
break; break;
case internal::string_type: case internal::string_type:
internal::check_string_type_spec(type_spec, eh); internal::check_string_type_spec(specs_.type, eh);
break; break;
case internal::pointer_type: case internal::pointer_type:
internal::check_pointer_type_spec(type_spec, eh); internal::check_pointer_type_spec(specs_.type, eh);
break; break;
case internal::custom_type: case internal::custom_type:
// Custom format specifiers should be checked in parse functions of // Custom format specifiers should be checked in parse functions of
@ -3106,9 +3100,8 @@ struct formatter<
specs_.width_, specs_.width_ref, ctx, format_str_); specs_.width_, specs_.width_ref, ctx, format_str_);
internal::handle_dynamic_spec<internal::precision_checker>( internal::handle_dynamic_spec<internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx, format_str_); specs_.precision, specs_.precision_ref, ctx, format_str_);
typedef output_range<typename FormatContext::iterator, using range_type = output_range<typename FormatContext::iterator,
typename FormatContext::char_type> typename FormatContext::char_type>;
range_type;
return visit_format_arg(arg_formatter<range_type>(ctx, nullptr, &specs_), return visit_format_arg(arg_formatter<range_type>(ctx, nullptr, &specs_),
internal::make_arg<FormatContext>(val)); internal::make_arg<FormatContext>(val));
} }
@ -3118,6 +3111,44 @@ struct formatter<
const Char* format_str_; const Char* format_str_;
}; };
#define FMT_FORMAT_AS(Type, Base) \
template <typename Char> \
struct formatter<Type, Char> : formatter<Base, Char> { \
template <typename FormatContext> \
auto format(const Type& val, FormatContext& ctx) -> decltype(ctx.out()) { \
return formatter<Base, Char>::format(val, ctx); \
} \
}
FMT_FORMAT_AS(signed char, int);
FMT_FORMAT_AS(unsigned char, unsigned);
FMT_FORMAT_AS(short, int);
FMT_FORMAT_AS(unsigned short, unsigned);
FMT_FORMAT_AS(long, long long);
FMT_FORMAT_AS(unsigned long, unsigned long long);
FMT_FORMAT_AS(float, double);
FMT_FORMAT_AS(Char*, const Char*);
FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
FMT_FORMAT_AS(std::nullptr_t, const void*);
#undef FMT_FORMAT_AS
template <typename Char>
struct formatter<void*, Char> : formatter<const void*, Char> {
template <typename FormatContext>
auto format(void* val, FormatContext& ctx) -> decltype(ctx.out()) {
return formatter<const void*, Char>::format(val, ctx);
}
};
template <typename Char, size_t N>
struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {
template <typename FormatContext>
auto format(const Char* val, FormatContext& ctx) -> decltype(ctx.out()) {
return formatter<basic_string_view<Char>, Char>::format(val, ctx);
}
};
// A formatter for types known only at run time such as variant alternatives. // A formatter for types known only at run time such as variant alternatives.
// //
// Usage: // Usage:

View File

@ -1876,9 +1876,9 @@ enum TestEnum { A };
TEST(FormatTest, Enum) { EXPECT_EQ("0", fmt::format("{}", A)); } TEST(FormatTest, Enum) { EXPECT_EQ("0", fmt::format("{}", A)); }
TEST(FormatTest, EnumFormatterUnambiguous) { TEST(FormatTest, FormatterNotSpecialized) {
fmt::formatter<TestEnum> f; EXPECT_FALSE((fmt::internal::is_formattable<
ASSERT_GE(sizeof(f), 0); // use f to avoid compiler warning fmt::formatter<TestEnum>, fmt::format_context>::value));
} }
#if FMT_HAS_FEATURE(cxx_strong_enums) #if FMT_HAS_FEATURE(cxx_strong_enums)