Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

printf("%.f") should use zero precision, not default precision #490

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 65 additions & 9 deletions fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -1179,10 +1179,31 @@ struct ConvertToIntImpl2<T, true> {
};
};

template <typename T, bool ENABLE_CONVERSION>
struct ConvertToIntImpl3 {
enum {
value = false
};
};

template <typename T>
struct ConvertToIntImpl3<T, true> {
enum {
// This results in error C2718 in Visual Studio if T has alignment restrictions.
value = sizeof(fmt::internal::convert(get<T>())) == sizeof(Yes)
};
};

#if FMT_MSC_VER >= 1700
#define FMT_INT_ALIGNABLE(T) (alignof(T) <= alignof(fmt::ULongLong))
#else
#define FMT_INT_ALIGNABLE(T) (true)
#endif

template<typename T>
struct ConvertToInt {
enum {
enable_conversion = sizeof(fmt::internal::convert(get<T>())) == sizeof(Yes)
enable_conversion = ConvertToIntImpl3<T, FMT_INT_ALIGNABLE(T)>::value
};
enum { value = ConvertToIntImpl2<T, enable_conversion>::value };
};
Expand Down Expand Up @@ -1262,6 +1283,31 @@ void format_arg(Formatter &, const Char *, const T &) {
"an overload of format_arg.");
}

template <typename Formatter>
class MakeArg;

// first argument to format_arg for custom formatters
template <typename AF>
class UserFormatter {
AF &af_;
public:
typedef AF ArgFormatter;
typedef typename AF::Char Char;
typedef MakeArg< UserFormatter > MakeArg;

UserFormatter(AF &af) : af_(af) {}

BasicWriter<typename AF::Char> &writer() { return af_.writer(); }

void visit(const Arg &arg) { af_.visit(arg); }

const Char *format(const Char *&format_str, const internal::Arg &arg)
{
af_.visit(format_str, arg);
}
};


// Makes an Arg object from any type.
template <typename Formatter>
class MakeValue : public Arg {
Expand Down Expand Up @@ -1305,7 +1351,7 @@ class MakeValue : public Arg {
template <typename T>
static void format_custom_arg(
void *formatter, const void *arg, void *format_str_ptr) {
format_arg(*static_cast<Formatter*>(formatter),
format_arg(*static_cast<UserFormatter<typename Formatter::ArgFormatter>*>(formatter),
*static_cast<const Char**>(format_str_ptr),
*static_cast<const T*>(arg));
}
Expand Down Expand Up @@ -1463,6 +1509,7 @@ class RuntimeError : public std::runtime_error {

template <typename Char>
class ArgMap;

} // namespace internal

/** An argument list. */
Expand Down Expand Up @@ -1945,10 +1992,10 @@ class ArgMap {
}
};

template <typename Impl, typename Char, typename Spec = fmt::FormatSpec>
template <typename Impl, typename CharType, typename Spec = fmt::FormatSpec>
class ArgFormatterBase : public ArgVisitor<Impl, void> {
private:
BasicWriter<Char> &writer_;
BasicWriter<CharType> &writer_;
Spec &spec_;

FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatterBase);
Expand All @@ -1960,7 +2007,7 @@ class ArgFormatterBase : public ArgVisitor<Impl, void> {
}

protected:
BasicWriter<Char> &writer() { return writer_; }
BasicWriter<CharType> &writer() { return writer_; }
Spec &spec() { return spec_; }

void write(bool value) {
Expand All @@ -1976,6 +2023,10 @@ class ArgFormatterBase : public ArgVisitor<Impl, void> {

public:
typedef Spec SpecType;
typedef CharType Char;

template <typename ArgFormatter>
friend class UserFormatter;

ArgFormatterBase(BasicWriter<Char> &w, Spec &s)
: writer_(w), spec_(s) {}
Expand Down Expand Up @@ -2134,7 +2185,8 @@ class BasicArgFormatter : public internal::ArgFormatterBase<Impl, Char, Spec> {

/** Formats an argument of a custom (user-defined) type. */
void visit_custom(internal::Arg::CustomValue c) {
c.format(&formatter_, c.value, &format_);
internal::UserFormatter<BasicArgFormatter> uf(*this);
c.format(&uf, c.value, &format_);
}
};

Expand All @@ -2151,11 +2203,12 @@ class ArgFormatter :
};

/** This template formats data and writes the output to a writer. */
template <typename CharType, typename ArgFormatter>
template <typename CharType, typename AF>
class BasicFormatter : private internal::FormatterBase {
public:
/** The character type for the output. */
typedef CharType Char;
typedef AF ArgFormatter;

private:
BasicWriter<Char> &writer_;
Expand Down Expand Up @@ -3693,7 +3746,8 @@ const Char *BasicFormatter<Char, ArgFormatter>::format(
typename ArgFormatter::SpecType spec;
if (*s == ':') {
if (arg.type == Arg::CUSTOM) {
arg.custom.format(this, arg.custom.value, &s);
internal::UserFormatter<BasicFormatter<Char, ArgFormatter> > uf(*this);
arg.custom.format(&uf, arg.custom.value, &s);
return s;
}
++s;
Expand Down Expand Up @@ -3913,8 +3967,10 @@ auto join(const Range& range, const BasicCStringRef<wchar_t>& sep)
}
#endif

using internal::UserFormatter;

template <typename ArgFormatter, typename Char, typename It>
void format_arg(fmt::BasicFormatter<Char, ArgFormatter> &f,
void format_arg(fmt::UserFormatter<ArgFormatter> &f,
const Char *&format_str, const ArgJoin<Char, It>& e) {
const Char* end = format_str;
if (*end == ':')
Expand Down
4 changes: 2 additions & 2 deletions fmt/ostream.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ class is_streamable {
} // namespace internal

// Formats a value.
template <typename Char, typename ArgFormatter_, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
template <typename ArgFormatter_, typename T, typename Char=typename ArgFormatter_::Char>
void format_arg(UserFormatter<ArgFormatter_> &f,
const Char *&format_str, const T &value) {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;

Expand Down
7 changes: 5 additions & 2 deletions fmt/printf.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ class BasicPrintfArgFormatter :

/** Formats an argument of a custom (user-defined) type. */
void visit_custom(internal::Arg::CustomValue c) {
BasicFormatter<Char> formatter(ArgList(), this->writer());
UserFormatter<BasicPrintfArgFormatter> formatter(*this);
const Char format_str[] = {'}', 0};
const Char *format = format_str;
c.format(&formatter, c.value, &format);
Expand All @@ -308,7 +308,7 @@ class PrintfArgFormatter :
};

/** This template formats data and writes the output to a writer. */
template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> >
template <typename Char, typename AF = PrintfArgFormatter<Char> >
class PrintfFormatter : private internal::FormatterBase {
private:
BasicWriter<Char> &writer_;
Expand All @@ -325,6 +325,7 @@ class PrintfFormatter : private internal::FormatterBase {
unsigned parse_header(const Char *&s, FormatSpec &spec);

public:
typedef AF ArgFormatter;
/**
\rst
Constructs a ``PrintfFormatter`` object. References to the arguments and
Expand Down Expand Up @@ -439,6 +440,8 @@ void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str) {
} else if (*s == '*') {
++s;
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s));
} else {
spec.precision_ = 0;
}
}

Expand Down
2 changes: 1 addition & 1 deletion fmt/time.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

namespace fmt {
template <typename ArgFormatter>
void format_arg(BasicFormatter<char, ArgFormatter> &f,
void format_arg(UserFormatter<ArgFormatter> &f,
const char *&format_str, const std::tm &tm) {
if (*format_str == ':')
++format_str;
Expand Down
3 changes: 3 additions & 0 deletions test/printf-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,9 @@ TEST(PrintfTest, LongLong) {

TEST(PrintfTest, Float) {
EXPECT_PRINTF("392.650000", "%f", 392.65);
EXPECT_PRINTF("392.65", "%.2f", 392.65);
EXPECT_PRINTF("392.6", "%.1f", 392.65);
EXPECT_PRINTF("393", "%.f", 392.65);
EXPECT_PRINTF("392.650000", "%F", 392.65);
EXPECT_PRINTF("392.65", "%s", 392.65);
char buffer[BUFFER_SIZE];
Expand Down