Skip to content

Commit

Permalink
Separate parsing and formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Aug 13, 2017
1 parent 1102d46 commit 5e0562a
Show file tree
Hide file tree
Showing 9 changed files with 445 additions and 266 deletions.
474 changes: 295 additions & 179 deletions fmt/format.h

Large diffs are not rendered by default.

30 changes: 20 additions & 10 deletions fmt/ostream.h
Expand Up @@ -77,18 +77,28 @@ void format_value(basic_buffer<Char> &buffer, const T &value) {
output << value; output << value;
buffer.resize(format_buf.size()); buffer.resize(format_buf.size());
} }

// Disable builtin formatting of enums and use operator<< instead.
template <typename T>
struct format_enum<T,
typename std::enable_if<std::is_enum<T>::value>::type> : std::false_type {};
} // namespace internal } // namespace internal


// Formats a value. // Formats an object of type T that has an overloaded ostream operator<<.
template <typename Char, typename T> template <typename T, typename Char>
void format_value(basic_buffer<Char> &buf, const T &value, struct formatter<T, Char,
basic_context<Char> &ctx) { typename std::enable_if<
basic_memory_buffer<Char> buffer; internal::gettype<T>() == internal::CUSTOM>::type>
internal::format_value(buffer, value); : formatter<basic_string_view<Char>, Char> {
basic_string_view<Char> str(buffer.data(), buffer.size());
internal::do_format_arg< arg_formatter<Char> >( void format(basic_buffer<Char> &buf, const T &value,
buf, internal::make_arg< basic_context<Char> >(str), ctx); basic_context<Char> &ctx) {
} basic_memory_buffer<Char> buffer;
internal::format_value(buffer, value);
basic_string_view<Char> str(buffer.data(), buffer.size());
formatter<basic_string_view<Char>, Char>::format(buf, str, ctx);
}
};


FMT_API void vprint(std::ostream &os, string_view format_str, args args); FMT_API void vprint(std::ostream &os, string_view format_str, args args);


Expand Down
4 changes: 2 additions & 2 deletions fmt/printf.cc
Expand Up @@ -14,7 +14,7 @@ FMT_FUNC int vfprintf(std::FILE *f, string_view format, printf_args args) {
} }


#ifndef FMT_HEADER_ONLY #ifndef FMT_HEADER_ONLY
template void printf_context<char>::format(buffer &); template void printf_context<char>::format(string_view, buffer &);
template void printf_context<wchar_t>::format(wbuffer &); template void printf_context<wchar_t>::format(wstring_view, wbuffer &);
#endif #endif
} }
51 changes: 31 additions & 20 deletions fmt/printf.h
Expand Up @@ -286,28 +286,46 @@ 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()(internal::custom_value<Char> c) {
const Char format_str[] = {'}', '\0'}; const Char format_str_data[] = {'}', '\0'};
basic_string_view<Char> format_str = format_str_data;
auto args = basic_args<basic_context<Char>>(); auto args = basic_args<basic_context<Char>>();
basic_context<Char> ctx(format_str, args); basic_context<Char> ctx(args);
c.format(this->writer().buffer(), c.value, &ctx); c.format(this->writer().buffer(), c.value, format_str, &ctx);
} }
}; };


/** This template formats data and writes the output to a writer. */
template <typename Char, template <typename Char,
typename ArgFormatter = printf_arg_formatter<Char> > typename ArgFormatter = printf_arg_formatter<Char> >
class printf_context;

template <typename T, typename Char = char>
struct printf_formatter {
const Char *parse(basic_string_view<Char> s) {
return s.data();
}

void format(basic_buffer<Char> &buf, const T &value, printf_context<Char> &) {
internal::format_value(buf, value);
}
};

/** This template formats data and writes the output to a writer. */
template <typename Char, typename ArgFormatter>
class printf_context : class printf_context :
private internal::context_base< private internal::context_base<
Char, printf_context<Char, ArgFormatter>> { Char, printf_context<Char, ArgFormatter>> {
public: public:
/** The character type for the output. */ /** The character type for the output. */
typedef Char char_type; using char_type = Char;

template <typename T>
using formatter_type = printf_formatter<T>;


private: private:
typedef internal::context_base<Char, printf_context> Base; typedef internal::context_base<Char, printf_context> Base;
typedef typename Base::format_arg format_arg; typedef typename Base::format_arg format_arg;
typedef basic_format_specs<Char> format_specs; typedef basic_format_specs<Char> format_specs;
typedef typename Base::iterator iterator; typedef internal::null_terminating_iterator<Char> iterator;


void parse_flags(format_specs &spec, iterator &it); void parse_flags(format_specs &spec, iterator &it);


Expand All @@ -328,12 +346,11 @@ class printf_context :
appropriate lifetimes. appropriate lifetimes.
\endrst \endrst
*/ */
explicit printf_context(basic_string_view<Char> format_str, explicit printf_context(basic_args<printf_context> args): Base(args) {}
basic_args<printf_context> args)
: Base(format_str, args) {}


/** Formats stored arguments and writes the output to the buffer. */ /** Formats stored arguments and writes the output to the buffer. */
FMT_API void format(basic_buffer<Char> &buffer); FMT_API void format(
basic_string_view<Char> format_str, basic_buffer<Char> &buffer);
}; };


template <typename Char, typename AF> template <typename Char, typename AF>
Expand Down Expand Up @@ -415,8 +432,9 @@ unsigned printf_context<Char, AF>::parse_header(
} }


template <typename Char, typename AF> template <typename Char, typename AF>
void printf_context<Char, AF>::format(basic_buffer<Char> &buffer) { void printf_context<Char, AF>::format(
auto start = this->pos(); basic_string_view<Char> format_str, basic_buffer<Char> &buffer) {
auto start = iterator(format_str);
auto it = start; auto it = start;
using internal::pointer_from; using internal::pointer_from;
while (*it) { while (*it) {
Expand Down Expand Up @@ -515,17 +533,10 @@ void printf_context<Char, AF>::format(basic_buffer<Char> &buffer) {
buffer.append(pointer_from(start), pointer_from(it)); buffer.append(pointer_from(start), pointer_from(it));
} }


// Formats a value.
template <typename Char, typename T>
void format_value(basic_buffer<Char> &buf, const T &value,
printf_context<Char>& ctx) {
internal::format_value(buf, value);
}

template <typename Char> template <typename Char>
void printf(basic_buffer<Char> &buf, basic_string_view<Char> format, void printf(basic_buffer<Char> &buf, basic_string_view<Char> format,
basic_args<printf_context<Char>> args) { basic_args<printf_context<Char>> args) {
printf_context<Char>(format, args).format(buf); printf_context<Char>(args).format(format, buf);
} }


typedef basic_args<printf_context<char>> printf_args; typedef basic_args<printf_context<char>> printf_args;
Expand Down
70 changes: 38 additions & 32 deletions fmt/time.h
Expand Up @@ -15,40 +15,46 @@


namespace fmt { namespace fmt {


void format_value(buffer &buf, const std::tm &tm, context &ctx) { template <>
auto &it = ctx.pos(); struct formatter<std::tm> {
if (*it == ':') template <typename Range>
++it; auto parse(Range format) -> decltype(begin(format)) {
auto end = it; auto it = internal::null_terminating_iterator<char>(format);
while (*end && *end != '}') if (*it == ':')
++end; ++it;
if (*end != '}') auto end = it;
FMT_THROW(format_error("missing '}' in format string")); while (*end && *end != '}')
memory_buffer format; ++end;
format.reserve(end - it + 1); tm_format.reserve(end - it + 1);
using internal::pointer_from; using internal::pointer_from;
format.append(pointer_from(it), pointer_from(end)); tm_format.append(pointer_from(it), pointer_from(end));
format.push_back('\0'); tm_format.push_back('\0');
std::size_t start = buf.size(); return pointer_from(end);
for (;;) { }
std::size_t size = buf.capacity() - start;
std::size_t count = std::strftime(&buf[start], size, &format[0], &tm); void format(buffer &buf, const std::tm &tm, context &ctx) {
if (count != 0) { std::size_t start = buf.size();
buf.resize(start + count); for (;;) {
break; std::size_t size = buf.capacity() - start;
} std::size_t count = std::strftime(&buf[start], size, &tm_format[0], &tm);
if (size >= format.size() * 256) { if (count != 0) {
// If the buffer is 256 times larger than the format string, assume buf.resize(start + count);
// that `strftime` gives an empty result. There doesn't seem to be a break;
// better way to distinguish the two cases: }
// https://github.com/fmtlib/fmt/issues/367 if (size >= tm_format.size() * 256) {
break; // If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
} }
const std::size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
} }
it = end;
} memory_buffer tm_format;
};
} }


#endif // FMT_TIME_H_ #endif // FMT_TIME_H_
4 changes: 2 additions & 2 deletions test/custom-formatter-test.cc
Expand Up @@ -65,8 +65,8 @@ std::string custom_vsprintf(
const char* format_str, const char* format_str,
fmt::basic_args<CustomPrintfFormatter> args) { fmt::basic_args<CustomPrintfFormatter> args) {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
CustomPrintfFormatter formatter(format_str, args); CustomPrintfFormatter formatter(args);
formatter.format(buffer); formatter.format(format_str, buffer);
return std::string(buffer.data(), buffer.size()); return std::string(buffer.data(), buffer.size());
} }


Expand Down
24 changes: 19 additions & 5 deletions test/format-test.cc
Expand Up @@ -1227,8 +1227,18 @@ TEST(FormatterTest, FormatStringView) {
EXPECT_EQ("test", format("{0}", string_view("test"))); EXPECT_EQ("test", format("{0}", string_view("test")));
} }


void format_value(fmt::buffer &buf, const Date &d, fmt::context &) { namespace fmt {
fmt::format_to(buf, "{}-{}-{}", d.year(), d.month(), d.day()); template <>
struct formatter<Date> {
template <typename Range>
auto parse(Range format) -> decltype(begin(format)) {
return begin(format);
}

void format(buffer &buf, const Date &d, context &) {
format_to(buf, "{}-{}-{}", d.year(), d.month(), d.day());
}
};
} }


TEST(FormatterTest, FormatCustom) { TEST(FormatterTest, FormatCustom) {
Expand All @@ -1239,9 +1249,13 @@ TEST(FormatterTest, FormatCustom) {


class Answer {}; class Answer {};


void format_value(fmt::buffer &buf, Answer, fmt::context &ctx) { namespace fmt {
fmt::formatter<int> f(ctx); template <>
f.format(buf, 42, ctx); struct formatter<Answer> : formatter<int> {
void format(fmt::buffer &buf, Answer, fmt::context &ctx) {
formatter<int>::format(buf, 42, ctx);
}
};
} }


TEST(FormatterTest, CustomFormat) { TEST(FormatterTest, CustomFormat) {
Expand Down
2 changes: 1 addition & 1 deletion test/ostream-test.cc
Expand Up @@ -65,7 +65,7 @@ struct TestArgFormatter : fmt::arg_formatter<char> {


TEST(OStreamTest, CustomArg) { TEST(OStreamTest, CustomArg) {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
fmt::context ctx("}", fmt::args()); fmt::context ctx((fmt::args()));
fmt::format_specs spec; fmt::format_specs spec;
TestArgFormatter af(buffer, ctx, spec); TestArgFormatter af(buffer, ctx, spec);
visit(af, fmt::internal::make_arg<fmt::context>(TestEnum())); visit(af, fmt::internal::make_arg<fmt::context>(TestEnum()));
Expand Down
52 changes: 37 additions & 15 deletions test/util-test.cc
Expand Up @@ -66,19 +66,27 @@ namespace {


struct Test {}; struct Test {};


template <typename Char>
void format_value(fmt::basic_buffer<Char> &b, Test,
fmt::basic_context<Char> &) {
const Char *test = "test";
b.append(test, test + std::strlen(test));
}

template <typename Context, typename T> template <typename Context, typename T>
basic_arg<Context> make_arg(const T &value) { basic_arg<Context> make_arg(const T &value) {
return fmt::internal::make_arg<Context>(value); return fmt::internal::make_arg<Context>(value);
} }
} // namespace } // namespace


namespace fmt {
template <typename Char>
struct formatter<Test, Char> {
template <typename Range>
auto parse(Range format) -> decltype(begin(format)) {
return begin(format);
}

void format(basic_buffer<Char> &b, Test, basic_context<Char> &) {
const Char *test = "test";
b.append(test, test + std::strlen(test));
}
};
}

void CheckForwarding( void CheckForwarding(
MockAllocator<int> &alloc, AllocatorRef< MockAllocator<int> > &ref) { MockAllocator<int> &alloc, AllocatorRef< MockAllocator<int> > &ref) {
int mem; int mem;
Expand Down Expand Up @@ -424,20 +432,33 @@ TEST(UtilTest, FormatArgs) {
} }


struct CustomContext { struct CustomContext {
typedef char char_type; using char_type = char;

template <typename T>
struct formatter_type {
template <typename Range>
auto parse(Range range) -> decltype(begin(range)) {
return begin(range);
}

void format(fmt::buffer &, const T &, CustomContext& ctx) {
ctx.called = true;
}
};

bool called; bool called;
};


void format_value(fmt::buffer &, const Test &, CustomContext &ctx) { fmt::string_view format() { return ""; }
ctx.called = true; void advance_to(const char *) {}
} };


TEST(UtilTest, MakeValueWithCustomFormatter) { TEST(UtilTest, MakeValueWithCustomFormatter) {
::Test t; ::Test t;
fmt::internal::value<CustomContext> arg(t); fmt::internal::value<CustomContext> arg(t);
CustomContext ctx = {false}; CustomContext ctx = {false};
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
arg.custom.format(buffer, &t, &ctx); fmt::string_view format_str;
arg.custom.format(buffer, &t, format_str, &ctx);
EXPECT_TRUE(ctx.called); EXPECT_TRUE(ctx.called);
} }


Expand Down Expand Up @@ -581,8 +602,9 @@ TEST(UtilTest, CustomArg) {
testing::Invoke([&](fmt::internal::custom_value<char> custom) { testing::Invoke([&](fmt::internal::custom_value<char> custom) {
EXPECT_EQ(&test, custom.value); 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); fmt::string_view format_str;
custom.format(buffer, &test, format_str, &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();
})); }));
Expand Down

0 comments on commit 5e0562a

Please sign in to comment.