diff --git a/format.cc b/format.cc index ff39d1fa..895a7961 100644 --- a/format.cc +++ b/format.cc @@ -193,6 +193,20 @@ struct WidthHandler : public fmt::internal::ArgVisitor ULongLong visit_any_uint(ULongLong value) { return value; } }; + +// This function template is used to prevent compile errors when handling +// incompatible string arguments, e.g. handling a wide string in a narrow +// string formatter. +template +Arg::StringValue ignore_incompatible_str(Arg::StringValue); + +template <> +inline Arg::StringValue ignore_incompatible_str( + Arg::StringValue) { return Arg::StringValue(); } + +template <> +inline Arg::StringValue ignore_incompatible_str( + Arg::StringValue s) { return s; } } // namespace int fmt::internal::SignBitNoInline(double value) { return SignBit(value); } @@ -394,6 +408,80 @@ void fmt::internal::FormatWinErrorMessage( } #endif +// An argument formatter. +template +class fmt::internal::ArgFormatter : + public fmt::internal::ArgVisitor, void> { + private: + fmt::BasicWriter &writer_; + fmt::FormatSpec &spec_; + const Char *format_; + + public: + ArgFormatter(fmt::BasicWriter &w, fmt::FormatSpec &s, const Char *fmt) + : writer_(w), spec_(s), format_(fmt) {} + + void visit_int(int value) { + writer_.FormatInt(value, spec_); + } + void visit_uint(unsigned value) { + writer_.FormatInt(value, spec_); + } + void visit_long_long(LongLong value) { + writer_.FormatInt(value, spec_); + } + void visit_ulong_long(ULongLong value) { + writer_.FormatInt(value, spec_); + } + void visit_double(double value) { + writer_.FormatDouble(value, spec_); + } + void visit_long_double(long double value) { + writer_.FormatDouble(value, spec_); + } + + void visit_char(int value) { + if (spec_.type_ && spec_.type_ != 'c') + fmt::internal::ReportUnknownType(spec_.type_, "char"); + typedef typename fmt::BasicWriter::CharPtr CharPtr; + CharPtr out = CharPtr(); + if (spec_.width_ > 1) { + Char fill = static_cast(spec_.fill()); + out = writer_.GrowBuffer(spec_.width_); + if (spec_.align_ == fmt::ALIGN_RIGHT) { + std::fill_n(out, spec_.width_ - 1, fill); + out += spec_.width_ - 1; + } else if (spec_.align_ == fmt::ALIGN_CENTER) { + out = writer_.FillPadding(out, spec_.width_, 1, fill); + } else { + std::fill_n(out + 1, spec_.width_ - 1, fill); + } + } else { + out = writer_.GrowBuffer(1); + } + *out = static_cast(value); + } + + void visit_string(Arg::StringValue value) { + writer_.write_str(value, spec_); + } + void visit_wstring(Arg::StringValue value) { + writer_.write_str(ignore_incompatible_str(value), spec_); + } + + void visit_pointer(const void *value) { + if (spec_.type_ && spec_.type_ != 'p') + fmt::internal::ReportUnknownType(spec_.type_, "pointer"); + spec_.flags_ = fmt::HASH_FLAG; + spec_.type_ = 'x'; + writer_.FormatInt(reinterpret_cast(value), spec_); + } + + void visit_custom(Arg::CustomValue c) { + c.format(this, c.value, format_); + } +}; + template void fmt::internal::FormatErrorReporter::operator()( const Char *s, fmt::StringRef message) const { @@ -570,7 +658,7 @@ void fmt::BasicWriter::FormatDouble(T value, const FormatSpec &spec) { template template void fmt::BasicWriter::write_str( - const internal::Arg::StringValue &str, const FormatSpec &spec) { + const Arg::StringValue &str, const FormatSpec &spec) { // Check if StringChar is convertible to Char. internal::CharTraits::convert(StringChar()); if (spec.type_ && spec.type_ != 's') @@ -847,7 +935,7 @@ void fmt::internal::PrintfParser::Format( writer.write_str(arg.string, spec); break; case Arg::WSTRING: - writer.write_str(arg.wstring, spec); + writer.write_str(ignore_incompatible_str(arg.wstring), spec); break; case Arg::POINTER: if (spec.type_ && spec.type_ != 'p') @@ -871,7 +959,7 @@ void fmt::internal::PrintfParser::Format( template const Char *fmt::BasicFormatter::format( - const Char *format_str, const internal::Arg &arg) { + const Char *format_str, const Arg &arg) { const Char *s = format_str; const char *error = 0; FormatSpec spec; @@ -1010,67 +1098,7 @@ const Char *fmt::BasicFormatter::format( start_ = s; // Format argument. - switch (arg.type) { - case Arg::INT: - writer_.FormatInt(arg.int_value, spec); - break; - case Arg::UINT: - writer_.FormatInt(arg.uint_value, spec); - break; - case Arg::LONG_LONG: - writer_.FormatInt(arg.long_long_value, spec); - break; - case Arg::ULONG_LONG: - writer_.FormatInt(arg.ulong_long_value, spec); - break; - case Arg::DOUBLE: - writer_.FormatDouble(arg.double_value, spec); - break; - case Arg::LONG_DOUBLE: - writer_.FormatDouble(arg.long_double_value, spec); - break; - case Arg::CHAR: { - if (spec.type_ && spec.type_ != 'c') - internal::ReportUnknownType(spec.type_, "char"); - typedef typename BasicWriter::CharPtr CharPtr; - CharPtr out = CharPtr(); - if (spec.width_ > 1) { - Char fill = static_cast(spec.fill()); - out = writer_.GrowBuffer(spec.width_); - if (spec.align_ == ALIGN_RIGHT) { - std::fill_n(out, spec.width_ - 1, fill); - out += spec.width_ - 1; - } else if (spec.align_ == ALIGN_CENTER) { - out = writer_.FillPadding(out, spec.width_, 1, fill); - } else { - std::fill_n(out + 1, spec.width_ - 1, fill); - } - } else { - out = writer_.GrowBuffer(1); - } - *out = static_cast(arg.int_value); - break; - } - case Arg::STRING: - writer_.write_str(arg.string, spec); - break; - case Arg::WSTRING: - writer_.write_str(arg.wstring, spec); - break; - case Arg::POINTER: - if (spec.type_ && spec.type_ != 'p') - internal::ReportUnknownType(spec.type_, "pointer"); - spec.flags_= HASH_FLAG; - spec.type_ = 'x'; - writer_.FormatInt(reinterpret_cast(arg.pointer_value), spec); - break; - case Arg::CUSTOM: - arg.custom.format(this, arg.custom.value, s - 1); - break; - default: - assert(false); - break; - } + internal::ArgFormatter(writer_, spec, s - 1).visit(arg); return s; } diff --git a/format.h b/format.h index 2b1ce9e1..6a30dbac 100644 --- a/format.h +++ b/format.h @@ -359,9 +359,6 @@ class CharTraits : public BasicCharTraits { // Conversion from wchar_t to char is not allowed. static char convert(wchar_t); - // Conversion from const wchar_t * to const char * is not allowed. - static const wchar_t *check(const wchar_t *s); - public: typedef const wchar_t *UnsupportedStrType; @@ -380,8 +377,6 @@ class CharTraits : public BasicCharTraits { static wchar_t convert(char value) { return value; } static wchar_t convert(wchar_t value) { return value; } - static const wchar_t *check(const wchar_t *s) { return s; } - template static int FormatFloat(wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, T value); @@ -637,7 +632,8 @@ class MakeArg : public Arg { void set_string(WStringRef str) { type = WSTRING; - wstring.value = CharTraits::check(str.c_str()); + CharTraits::convert(wchar_t()); + wstring.value = str.c_str(); wstring.size = str.size(); } @@ -814,6 +810,9 @@ class RuntimeError : public std::runtime_error { protected: RuntimeError() : std::runtime_error("") {} }; + +template +class ArgFormatter; } // namespace internal /** @@ -1348,6 +1347,7 @@ class BasicWriter { // Do not implement! void operator<<(typename internal::CharTraits::UnsupportedStrType); + friend class internal::ArgFormatter; friend class BasicFormatter; friend class internal::PrintfParser;