Commit 10e70a06 authored by Victor Zverovich's avatar Victor Zverovich

Improve handling of custom arguments

parent e0243000
...@@ -1385,7 +1385,22 @@ class basic_arg { ...@@ -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 { ...@@ -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 ...@@ -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 ...@@ -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 { ...@@ -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> { ...@@ -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 { ...@@ -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 { ...@@ -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> { ...@@ -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> { ...@@ -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) { ...@@ -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> { ...@@ -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) { ...@@ -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> { ...@@ -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) { ...@@ -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) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment