mirror of
https://github.com/fmtlib/fmt.git
synced 2025-04-25 03:02:38 +00:00
Improve handling of custom arguments
This commit is contained in:
parent
e0243000de
commit
10e70a06c9
@ -1385,7 +1385,22 @@ class basic_arg {
|
|||||||
friend class basic_args<Context>;
|
friend class basic_args<Context>;
|
||||||
friend class internal::arg_map<Context>;
|
friend class internal::arg_map<Context>;
|
||||||
|
|
||||||
|
using char_type = typename Context::char_type;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
class handle {
|
||||||
|
public:
|
||||||
|
explicit handle(internal::custom_value<char_type> custom)
|
||||||
|
: custom_(custom) {}
|
||||||
|
|
||||||
|
void format(basic_buffer<char_type> &buf, Context &ctx) {
|
||||||
|
custom_.format(buf, custom_.value, &ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
internal::custom_value<char_type> custom_;
|
||||||
|
};
|
||||||
|
|
||||||
constexpr basic_arg() : type_(internal::NONE) {}
|
constexpr basic_arg() : type_(internal::NONE) {}
|
||||||
|
|
||||||
explicit operator bool() const noexcept { return type_ != internal::NONE; }
|
explicit operator bool() const noexcept { return type_ != internal::NONE; }
|
||||||
@ -1407,7 +1422,7 @@ class basic_arg {
|
|||||||
template <typename Visitor, typename Context>
|
template <typename Visitor, typename Context>
|
||||||
constexpr typename std::result_of<Visitor(int)>::type
|
constexpr typename std::result_of<Visitor(int)>::type
|
||||||
visit(Visitor &&vis, basic_arg<Context> arg) {
|
visit(Visitor &&vis, basic_arg<Context> arg) {
|
||||||
typedef typename Context::char_type Char;
|
using char_type = typename Context::char_type;
|
||||||
switch (arg.type_) {
|
switch (arg.type_) {
|
||||||
case internal::NONE:
|
case internal::NONE:
|
||||||
return vis(monostate());
|
return vis(monostate());
|
||||||
@ -1425,7 +1440,7 @@ constexpr typename std::result_of<Visitor(int)>::type
|
|||||||
case internal::BOOL:
|
case internal::BOOL:
|
||||||
return vis(arg.value_.int_value != 0);
|
return vis(arg.value_.int_value != 0);
|
||||||
case internal::CHAR:
|
case internal::CHAR:
|
||||||
return vis(static_cast<Char>(arg.value_.int_value));
|
return vis(static_cast<char_type>(arg.value_.int_value));
|
||||||
case internal::DOUBLE:
|
case internal::DOUBLE:
|
||||||
return vis(arg.value_.double_value);
|
return vis(arg.value_.double_value);
|
||||||
case internal::LONG_DOUBLE:
|
case internal::LONG_DOUBLE:
|
||||||
@ -1433,12 +1448,12 @@ constexpr typename std::result_of<Visitor(int)>::type
|
|||||||
case internal::CSTRING:
|
case internal::CSTRING:
|
||||||
return vis(arg.value_.string.value);
|
return vis(arg.value_.string.value);
|
||||||
case internal::STRING:
|
case internal::STRING:
|
||||||
return vis(basic_string_view<Char>(
|
return vis(basic_string_view<char_type>(
|
||||||
arg.value_.string.value, arg.value_.string.size));
|
arg.value_.string.value, arg.value_.string.size));
|
||||||
case internal::POINTER:
|
case internal::POINTER:
|
||||||
return vis(arg.value_.pointer);
|
return vis(arg.value_.pointer);
|
||||||
case internal::CUSTOM:
|
case internal::CUSTOM:
|
||||||
return vis(arg.value_.custom);
|
return vis(typename basic_arg<Context>::handle(arg.value_.custom));
|
||||||
}
|
}
|
||||||
return typename std::result_of<Visitor(int)>::type();
|
return typename std::result_of<Visitor(int)>::type();
|
||||||
}
|
}
|
||||||
@ -2161,8 +2176,8 @@ class custom_formatter {
|
|||||||
custom_formatter(basic_buffer<Char> &buffer, Context &ctx)
|
custom_formatter(basic_buffer<Char> &buffer, Context &ctx)
|
||||||
: buffer_(buffer), ctx_(ctx) {}
|
: buffer_(buffer), ctx_(ctx) {}
|
||||||
|
|
||||||
bool operator()(internal::custom_value<Char> custom) {
|
bool operator()(typename basic_arg<Context>::handle h) {
|
||||||
custom.format(buffer_, custom.value, &ctx_);
|
h.format(buffer_, ctx_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2795,8 +2810,8 @@ class arg_formatter : public internal::arg_formatter_base<Char> {
|
|||||||
using internal::arg_formatter_base<Char>::operator();
|
using internal::arg_formatter_base<Char>::operator();
|
||||||
|
|
||||||
/** Formats an argument of a custom (user-defined) type. */
|
/** Formats an argument of a custom (user-defined) type. */
|
||||||
void operator()(internal::custom_value<Char> c) {
|
void operator()(typename basic_arg<basic_context<Char>>::handle handle) {
|
||||||
c.format(this->writer().buffer(), c.value, &ctx_);
|
handle.format(this->writer().buffer(), ctx_);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -200,6 +200,13 @@ class PrintfWidthHandler {
|
|||||||
};
|
};
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
class printf_arg_formatter;
|
||||||
|
|
||||||
|
template <typename Char,
|
||||||
|
typename ArgFormatter = printf_arg_formatter<Char> >
|
||||||
|
class printf_context;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
The ``printf`` argument formatter.
|
The ``printf`` argument formatter.
|
||||||
@ -208,6 +215,8 @@ class PrintfWidthHandler {
|
|||||||
template <typename Char>
|
template <typename Char>
|
||||||
class printf_arg_formatter : public internal::arg_formatter_base<Char> {
|
class printf_arg_formatter : public internal::arg_formatter_base<Char> {
|
||||||
private:
|
private:
|
||||||
|
printf_context<Char>& context_;
|
||||||
|
|
||||||
void write_null_pointer() {
|
void write_null_pointer() {
|
||||||
this->spec().type_ = 0;
|
this->spec().type_ = 0;
|
||||||
this->write("(nil)");
|
this->write("(nil)");
|
||||||
@ -225,8 +234,9 @@ class printf_arg_formatter : public internal::arg_formatter_base<Char> {
|
|||||||
specifier information for standard argument types.
|
specifier information for standard argument types.
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
printf_arg_formatter(basic_buffer<Char> &buffer, format_specs &spec)
|
printf_arg_formatter(
|
||||||
: internal::arg_formatter_base<Char>(buffer, spec) {}
|
basic_buffer<Char> &buffer, format_specs &spec, printf_context<Char> &ctx)
|
||||||
|
: internal::arg_formatter_base<Char>(buffer, spec), context_(ctx) {}
|
||||||
|
|
||||||
using Base::operator();
|
using Base::operator();
|
||||||
|
|
||||||
@ -268,18 +278,11 @@ class printf_arg_formatter : public internal::arg_formatter_base<Char> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Formats an argument of a custom (user-defined) type. */
|
/** Formats an argument of a custom (user-defined) type. */
|
||||||
void operator()(internal::custom_value<Char> c) {
|
void operator()(typename basic_arg<printf_context<Char>>::handle handle) {
|
||||||
const Char format_str[] = {'}', '\0'};
|
handle.format(this->writer().buffer(), context_);
|
||||||
auto args = basic_args<basic_context<Char>>();
|
|
||||||
basic_context<Char> ctx(basic_string_view<Char>(format_str), args);
|
|
||||||
c.format(this->writer().buffer(), c.value, &ctx);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char,
|
|
||||||
typename ArgFormatter = printf_arg_formatter<Char> >
|
|
||||||
class printf_context;
|
|
||||||
|
|
||||||
template <typename T, typename Char = char>
|
template <typename T, typename Char = char>
|
||||||
struct printf_formatter {
|
struct printf_formatter {
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
@ -506,7 +509,7 @@ void printf_context<Char, AF>::format(basic_buffer<Char> &buffer) {
|
|||||||
start = it;
|
start = it;
|
||||||
|
|
||||||
// Format argument.
|
// Format argument.
|
||||||
visit(AF(buffer, spec), arg);
|
visit(AF(buffer, spec, *this), arg);
|
||||||
}
|
}
|
||||||
buffer.append(pointer_from(start), pointer_from(it));
|
buffer.append(pointer_from(start), pointer_from(it));
|
||||||
}
|
}
|
||||||
|
@ -29,22 +29,6 @@ class CustomArgFormatter : public fmt::arg_formatter<char> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// A custom argument formatter that doesn't print `-` for floating-point values
|
|
||||||
// rounded to 0.
|
|
||||||
class CustomPrintfArgFormatter : public printf_arg_formatter<char> {
|
|
||||||
public:
|
|
||||||
CustomPrintfArgFormatter(fmt::buffer &buf, fmt::format_specs &spec)
|
|
||||||
: printf_arg_formatter<char>(buf, spec) {}
|
|
||||||
|
|
||||||
using printf_arg_formatter<char>::operator();
|
|
||||||
|
|
||||||
void operator()(double value) {
|
|
||||||
if (round(value * pow(10, spec().precision())) == 0)
|
|
||||||
value = 0;
|
|
||||||
printf_arg_formatter<char>::operator()(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string custom_vformat(fmt::string_view format_str, fmt::args args) {
|
std::string custom_vformat(fmt::string_view format_str, fmt::args args) {
|
||||||
fmt::memory_buffer buffer;
|
fmt::memory_buffer buffer;
|
||||||
// Pass custom argument formatter as a template arg to vwrite.
|
// Pass custom argument formatter as a template arg to vwrite.
|
||||||
@ -58,25 +42,6 @@ std::string custom_format(const char *format_str, const Args & ... args) {
|
|||||||
return custom_vformat(format_str, va);
|
return custom_vformat(format_str, va);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef fmt::printf_context<char, CustomPrintfArgFormatter>
|
|
||||||
CustomPrintfFormatter;
|
|
||||||
|
|
||||||
std::string custom_vsprintf(
|
|
||||||
const char* format_str,
|
|
||||||
fmt::basic_args<CustomPrintfFormatter> args) {
|
|
||||||
fmt::memory_buffer buffer;
|
|
||||||
CustomPrintfFormatter formatter(format_str, args);
|
|
||||||
formatter.format(buffer);
|
|
||||||
return std::string(buffer.data(), buffer.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
std::string custom_sprintf(const char *format_str, const Args & ... args) {
|
|
||||||
auto va = fmt::make_args<CustomPrintfFormatter>(args...);
|
|
||||||
return custom_vsprintf(format_str, va);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CustomFormatterTest, Format) {
|
TEST(CustomFormatterTest, Format) {
|
||||||
EXPECT_EQ("0.00", custom_format("{:.2f}", -.00001));
|
EXPECT_EQ("0.00", custom_format("{:.2f}", -.00001));
|
||||||
EXPECT_EQ("0.00", custom_sprintf("%.2f", -.00001));
|
|
||||||
}
|
}
|
||||||
|
@ -1509,7 +1509,7 @@ class MockArgFormatter : public fmt::internal::arg_formatter_base<char> {
|
|||||||
|
|
||||||
void operator()(int value) { call(value); }
|
void operator()(int value) { call(value); }
|
||||||
|
|
||||||
void operator()(fmt::internal::custom_value<char>) {}
|
void operator()(fmt::basic_arg<fmt::context>::handle) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
void custom_vformat(fmt::string_view format_str, fmt::args args) {
|
void custom_vformat(fmt::string_view format_str, fmt::args args) {
|
||||||
|
@ -590,18 +590,17 @@ TEST(UtilTest, PointerArg) {
|
|||||||
|
|
||||||
TEST(UtilTest, CustomArg) {
|
TEST(UtilTest, CustomArg) {
|
||||||
::Test test;
|
::Test test;
|
||||||
typedef MockVisitor<fmt::internal::custom_value<char>> Visitor;
|
using handle = typename fmt::basic_arg<fmt::context>::handle;
|
||||||
testing::StrictMock<Visitor> visitor;
|
using visitor = MockVisitor<handle>;
|
||||||
EXPECT_CALL(visitor, visit(_)).WillOnce(
|
testing::StrictMock<visitor> v;
|
||||||
testing::Invoke([&](fmt::internal::custom_value<char> custom) {
|
EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke([&](handle h) {
|
||||||
EXPECT_EQ(&test, custom.value);
|
|
||||||
fmt::memory_buffer buffer;
|
fmt::memory_buffer buffer;
|
||||||
fmt::context ctx("", fmt::args());
|
fmt::context ctx("", fmt::args());
|
||||||
custom.format(buffer, &test, &ctx);
|
h.format(buffer, ctx);
|
||||||
EXPECT_EQ("test", std::string(buffer.data(), buffer.size()));
|
EXPECT_EQ("test", std::string(buffer.data(), buffer.size()));
|
||||||
return Visitor::Result();
|
return visitor::Result();
|
||||||
}));
|
}));
|
||||||
fmt::visit(visitor, make_arg<fmt::context>(test));
|
fmt::visit(v, make_arg<fmt::context>(test));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ArgVisitorTest, VisitInvalidArg) {
|
TEST(ArgVisitorTest, VisitInvalidArg) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user