mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-31 03:07:36 +02:00
Restructure printf_arg_formatter to make it customizable
This commit is contained in:
committed by
Victor Zverovich
parent
5d755d0a4e
commit
2b415b7af7
@ -197,9 +197,7 @@ using internal::printf; // For printing into memory_buffer.
|
|||||||
|
|
||||||
template <typename Range> class printf_arg_formatter;
|
template <typename Range> class printf_arg_formatter;
|
||||||
|
|
||||||
template <typename OutputIt, typename Char,
|
template <typename OutputIt, typename Char>
|
||||||
typename ArgFormatter =
|
|
||||||
printf_arg_formatter<back_insert_range<internal::buffer<Char>>>>
|
|
||||||
class basic_printf_context;
|
class basic_printf_context;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -212,12 +210,12 @@ class printf_arg_formatter
|
|||||||
: public internal::function<
|
: public internal::function<
|
||||||
typename internal::arg_formatter_base<Range>::iterator>,
|
typename internal::arg_formatter_base<Range>::iterator>,
|
||||||
public internal::arg_formatter_base<Range> {
|
public internal::arg_formatter_base<Range> {
|
||||||
|
public:
|
||||||
|
typedef decltype(internal::declval<Range>().begin()) iterator;
|
||||||
private:
|
private:
|
||||||
typedef typename Range::value_type char_type;
|
typedef typename Range::value_type char_type;
|
||||||
typedef decltype(internal::declval<Range>().begin()) iterator;
|
|
||||||
typedef internal::arg_formatter_base<Range> base;
|
typedef internal::arg_formatter_base<Range> base;
|
||||||
typedef basic_printf_context<iterator, char_type, printf_arg_formatter>
|
typedef basic_printf_context<iterator, char_type> context_type;
|
||||||
context_type;
|
|
||||||
|
|
||||||
context_type& context_;
|
context_type& context_;
|
||||||
|
|
||||||
@ -328,7 +326,7 @@ template <typename T> struct printf_formatter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** This template formats data and writes the output to a writer. */
|
/** This template formats data and writes the output to a writer. */
|
||||||
template <typename OutputIt, typename Char, typename ArgFormatter>
|
template <typename OutputIt, typename Char>
|
||||||
class basic_printf_context {
|
class basic_printf_context {
|
||||||
public:
|
public:
|
||||||
/** The character type for the output. */
|
/** The character type for the output. */
|
||||||
@ -379,11 +377,12 @@ class basic_printf_context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Formats stored arguments and writes the output to the range. */
|
/** Formats stored arguments and writes the output to the range. */
|
||||||
|
template<typename ArgFormatter = printf_arg_formatter<back_insert_range<internal::buffer<Char>>>>
|
||||||
OutputIt format();
|
OutputIt format();
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename OutputIt, typename Char, typename AF>
|
template <typename OutputIt, typename Char>
|
||||||
void basic_printf_context<OutputIt, Char, AF>::parse_flags(format_specs& spec,
|
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& spec,
|
||||||
const Char*& it,
|
const Char*& it,
|
||||||
const Char* end) {
|
const Char* end) {
|
||||||
for (; it != end; ++it) {
|
for (; it != end; ++it) {
|
||||||
@ -409,9 +408,9 @@ void basic_printf_context<OutputIt, Char, AF>::parse_flags(format_specs& spec,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename Char, typename AF>
|
template <typename OutputIt, typename Char>
|
||||||
typename basic_printf_context<OutputIt, Char, AF>::format_arg
|
typename basic_printf_context<OutputIt, Char>::format_arg
|
||||||
basic_printf_context<OutputIt, Char, AF>::get_arg(unsigned arg_index) {
|
basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
|
||||||
if (arg_index == std::numeric_limits<unsigned>::max())
|
if (arg_index == std::numeric_limits<unsigned>::max())
|
||||||
arg_index = parse_ctx_.next_arg_id();
|
arg_index = parse_ctx_.next_arg_id();
|
||||||
else
|
else
|
||||||
@ -419,8 +418,8 @@ basic_printf_context<OutputIt, Char, AF>::get_arg(unsigned arg_index) {
|
|||||||
return internal::get_arg(*this, arg_index);
|
return internal::get_arg(*this, arg_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename Char, typename AF>
|
template <typename OutputIt, typename Char>
|
||||||
unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
|
unsigned basic_printf_context<OutputIt, Char>::parse_header(
|
||||||
const Char*& it, const Char* end, format_specs& spec) {
|
const Char*& it, const Char* end, format_specs& spec) {
|
||||||
unsigned arg_index = std::numeric_limits<unsigned>::max();
|
unsigned arg_index = std::numeric_limits<unsigned>::max();
|
||||||
char_type c = *it;
|
char_type c = *it;
|
||||||
@ -457,8 +456,9 @@ unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
|
|||||||
return arg_index;
|
return arg_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename Char, typename AF>
|
template <typename OutputIt, typename Char>
|
||||||
OutputIt basic_printf_context<OutputIt, Char, AF>::format() {
|
template <typename ArgFormatter>
|
||||||
|
OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||||
auto out = this->out();
|
auto out = this->out();
|
||||||
const Char* start = parse_ctx_.begin();
|
const Char* start = parse_ctx_.begin();
|
||||||
const Char* end = parse_ctx_.end();
|
const Char* end = parse_ctx_.end();
|
||||||
@ -567,7 +567,7 @@ OutputIt basic_printf_context<OutputIt, Char, AF>::format() {
|
|||||||
start = it;
|
start = it;
|
||||||
|
|
||||||
// Format argument.
|
// Format argument.
|
||||||
visit_format_arg(AF(out, spec, *this), arg);
|
visit_format_arg(ArgFormatter(out, spec, *this), arg);
|
||||||
}
|
}
|
||||||
return std::copy(start, it, out);
|
return std::copy(start, it, out);
|
||||||
}
|
}
|
||||||
@ -712,6 +712,19 @@ inline int vfprintf(
|
|||||||
return static_cast<int>(buffer.size());
|
return static_cast<int>(buffer.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Formats arguments and writes the output to the range. */
|
||||||
|
template <typename ArgFormatter, typename Char,
|
||||||
|
typename Context = basic_printf_context<
|
||||||
|
typename ArgFormatter::iterator, Char>>
|
||||||
|
typename ArgFormatter::iterator vprintf(internal::buffer<Char>& out,
|
||||||
|
basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<Context> args) {
|
||||||
|
typename ArgFormatter::iterator iter(out);
|
||||||
|
Context(iter, format_str, args).template format<ArgFormatter>();
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Prints formatted data to the stream *os*.
|
Prints formatted data to the stream *os*.
|
||||||
|
@ -554,3 +554,51 @@ TEST(PrintfTest, VSPrintfMakeWArgsExample) {
|
|||||||
fmt::make_wprintf_args(42, L"something")));
|
fmt::make_wprintf_args(42, L"something")));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef fmt::printf_arg_formatter<
|
||||||
|
fmt::back_insert_range<fmt::internal::buffer<char>>> formatter_t;
|
||||||
|
typedef fmt::basic_printf_context<formatter_t::iterator, char> context_t;
|
||||||
|
|
||||||
|
// A custom printf argument formatter that doesn't print `-` for floating-point
|
||||||
|
// values rounded to 0.
|
||||||
|
class custom_printf_arg_formatter : public formatter_t {
|
||||||
|
public:
|
||||||
|
using formatter_t::iterator;
|
||||||
|
|
||||||
|
custom_printf_arg_formatter(formatter_t::iterator iter, formatter_t::format_specs& spec, context_t& ctx)
|
||||||
|
: formatter_t(iter, spec, ctx) {}
|
||||||
|
|
||||||
|
using formatter_t::operator();
|
||||||
|
|
||||||
|
#if FMT_MSC_VER > 0 && FMT_MSC_VER <= 1804
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||||
|
iterator operator()(T value) {
|
||||||
|
#else
|
||||||
|
iterator operator()(double value) {
|
||||||
|
#endif
|
||||||
|
// Comparing a float to 0.0 is safe.
|
||||||
|
if (round(value * pow(10, spec()->precision)) == 0.0) value = 0;
|
||||||
|
return formatter_t::operator()(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef fmt::basic_format_args<context_t> format_args_t;
|
||||||
|
|
||||||
|
std::string custom_vformat(fmt::string_view format_str, format_args_t args) {
|
||||||
|
fmt::memory_buffer buffer;
|
||||||
|
fmt::vprintf<custom_printf_arg_formatter>(buffer, format_str, args);
|
||||||
|
return std::string(buffer.data(), buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
std::string custom_format(const char* format_str, const Args&... args) {
|
||||||
|
auto va = fmt::make_printf_args (args...);
|
||||||
|
return custom_vformat(format_str, va);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CustomFormatterTest, Format) {
|
||||||
|
EXPECT_EQ("0.00", custom_format("%.2f", -.00001));
|
||||||
|
EXPECT_EQ("0.00", custom_format("%.2f", .00001));
|
||||||
|
EXPECT_EQ("1.00", custom_format("%.2f", 1.00001));
|
||||||
|
EXPECT_EQ("-1.00", custom_format("%.2f", -1.00001));
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user