From f0ad179a13aa778389f910b57822f54cdf923b55 Mon Sep 17 00:00:00 2001 From: Andreas Wass Date: Thu, 2 Nov 2017 09:24:01 +0100 Subject: [PATCH 1/4] Add concat and concat_separated Concat simply concatenates all arguments and will replace a single placeholder with the concatenation. Example: format("({})", concat("hello ", "world")) will result in "(hello world)" concat_separated concatenates all arguments with a separator between them. Example: format("({})", concat(", ", 1, 2, 3)) will result in "(1, 2, 3)" --- fmt/format.h | 79 +++++++++++++++++++++++++++++++++++++++++++++ test/format-test.cc | 24 ++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/fmt/format.h b/fmt/format.h index 7748bfac..0423aa35 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -4085,6 +4085,85 @@ void format_arg(fmt::BasicFormatter &f, } format_str = end + 1; } + +struct ArgConcatNoSeparator +{ +}; + +template +struct ArgConcatSeparator { + BasicCStringRef sep; + //ArgConcatSeparator(const BasicCStringRef &sep): sep(sep) {} +}; + +template +struct ArgConcat: public Separator { + std::tuple a; + ArgConcat(const Separator &sep, Args&&...args): + Separator(sep), a(std::forward(args)...){ + } + + template + std::enable_if_t + format(fmt::BasicFormatter &, + const Char *&) const { + } + + template + std::enable_if_t::value, void> + format(fmt::BasicFormatter &f, + const Char *&format_str) const { + const Char* save = format_str; + f.format(format_str, + internal::MakeArg >(std::get(a))); + format(f, save); + } + + template + std::enable_if_t>::value, void> + format(fmt::BasicFormatter &f, + const Char *&format_str) const { + const Char* save = format_str; + if(I != 0) { + f.writer().write(Separator::sep); + } + f.format(format_str, + internal::MakeArg >(std::get(a))); + format(f, save); + } +}; + +template +ArgConcat concat(Args&&...args) { + return ArgConcat{ArgConcatNoSeparator{}, std::forward(args)...}; +} + +template +ArgConcat, Args...> concat_separated(const BasicCStringRef &sep, Args&&...args) { + return ArgConcat, Args...>{ArgConcatSeparator{sep}, std::forward(args)...}; +} + +template +ArgConcat, Args...> concat_separated(const BasicCStringRef &sep, Args&&...args) { + return ArgConcat, Args...>{ArgConcatSeparator{sep}, std::forward(args)...}; +} + +template +void format_arg(fmt::BasicFormatter &f, + const Char *&format_str, const ArgConcat &e) { + const Char* end = format_str; + if (*end == ':') + ++end; + while (*end && *end != '}') + ++end; + if (*end != '}') + FMT_THROW(FormatError("missing '}' in format string")); + + e.format<0>(f, format_str); + format_str = end + 1; +} } // namespace fmt #if FMT_USE_USER_DEFINED_LITERALS diff --git a/test/format-test.cc b/test/format-test.cc index f512ef48..97c9dbda 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1604,6 +1604,30 @@ TEST(FormatTest, JoinArg) { #endif } +TEST(FormatTest, ConcatArg) { + using fmt::concat; + EXPECT_EQ("123", format("{}", concat(1,2,3))); + EXPECT_EQ("()", format("({})", concat())); + EXPECT_EQ("(001002003)", format("({:03})", concat(1,2,3))); + EXPECT_EQ("(1 hello world 2)", format("({})", concat(1," hello"," world ", 2))); + EXPECT_EQ("(1 hello world 2)", format("({}{})", concat(1," hello"," world "), 2)); + EXPECT_EQ("(+01.20+03.40)", format("({:+06.2f})", concat(1.2f, 3.4f))); + + EXPECT_EQ(L"(1, 2, 3)", format(L"({})", concat(1, L", ", 2, L", ", 3))); +} + +TEST(FormatTest, ConcatSeparatedArg) { + using fmt::concat_separated; + EXPECT_EQ("(1, 2, 3)", format("({})", concat_separated(", ", 1,2,3))); + EXPECT_EQ("()", format("({})", concat_separated(", "))); + EXPECT_EQ("(001, 002, 003)", format("({:03})", concat_separated(", ", 1,2,3))); + EXPECT_EQ("(1 hello world 2)", format("({})", concat_separated(" ", 1,"hello","world", 2))); + EXPECT_EQ("(1 hello world 2)", format("({} {})", concat_separated(" ", 1,"hello","world"), 2)); + EXPECT_EQ("(+01.20, +03.40)", format("({:+06.2f})", concat_separated(", ", 1.2f, 3.4f))); + + EXPECT_EQ(L"(1, 2, 3)", format(L"({})", concat_separated(L", ", 1, 2, 3))); +} + template std::string str(const T &value) { return fmt::format("{}", value); From ce25d969d69c93bbf9515dc24eac67e88d1974de Mon Sep 17 00:00:00 2001 From: Andreas Wass Date: Thu, 2 Nov 2017 09:30:45 +0100 Subject: [PATCH 2/4] Remove unused code --- fmt/format.h | 1 - 1 file changed, 1 deletion(-) diff --git a/fmt/format.h b/fmt/format.h index 0423aa35..ba6c8935 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -4093,7 +4093,6 @@ struct ArgConcatNoSeparator template struct ArgConcatSeparator { BasicCStringRef sep; - //ArgConcatSeparator(const BasicCStringRef &sep): sep(sep) {} }; template From 958b5a756e482b368b362ef1fbebbdae3fd59652 Mon Sep 17 00:00:00 2001 From: Andreas Wass Date: Thu, 2 Nov 2017 11:09:49 +0100 Subject: [PATCH 3/4] Fix g++ compilation errors --- fmt/format.h | 85 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/fmt/format.h b/fmt/format.h index ba6c8935..89d9a6c9 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -4086,67 +4086,86 @@ void format_arg(fmt::BasicFormatter &f, format_str = end + 1; } -struct ArgConcatNoSeparator -{ +struct ArgConcatNoSeparator { + template + void add_separator(fmt::BasicFormatter&) const {} + + template + void add_separator(fmt::BasicFormatter&) const {} }; template struct ArgConcatSeparator { BasicCStringRef sep; + ArgConcatSeparator(const BasicCStringRef &sep): sep(sep) {} + template + void add_separator(fmt::BasicFormatter &f) const { + f.writer().write(sep); + } }; template -struct ArgConcat: public Separator { - std::tuple a; - ArgConcat(const Separator &sep, Args&&...args): - Separator(sep), a(std::forward(args)...){ +struct ArgConcat { +}; + +template +struct ArgConcat: public Separator { + + ArgConcat(const Separator &sep): Separator(sep) {} + + template + void format(fmt::BasicFormatter &, + const Char *&) const { } - template - std::enable_if_t - format(fmt::BasicFormatter &, - const Char *&) const { + template + void format_first(fmt::BasicFormatter &, + const Char *&) const { + } +}; + +template +struct ArgConcat: public ArgConcat { + A a; + + ArgConcat(const Separator &sep, A a, Args...args): + ArgConcat(sep, args...), + a(a){ } - template - std::enable_if_t::value, void> - format(fmt::BasicFormatter &f, + template + void format(fmt::BasicFormatter &f, const Char *&format_str) const { const Char* save = format_str; + this->add_separator(f); f.format(format_str, - internal::MakeArg >(std::get(a))); - format(f, save); + internal::MakeArg >(a)); + ArgConcat::format(f, save); } - template - std::enable_if_t>::value, void> - format(fmt::BasicFormatter &f, - const Char *&format_str) const { + template + void format_first(fmt::BasicFormatter &f, + const Char *&format_str) const { const Char* save = format_str; - if(I != 0) { - f.writer().write(Separator::sep); - } f.format(format_str, - internal::MakeArg >(std::get(a))); - format(f, save); + internal::MakeArg >(a)); + ArgConcat::format(f, save); } }; template -ArgConcat concat(Args&&...args) { - return ArgConcat{ArgConcatNoSeparator{}, std::forward(args)...}; +ArgConcat concat(Args...args) { + return ArgConcat(ArgConcatNoSeparator(), args...); } template -ArgConcat, Args...> concat_separated(const BasicCStringRef &sep, Args&&...args) { - return ArgConcat, Args...>{ArgConcatSeparator{sep}, std::forward(args)...}; +ArgConcat, Args...> concat_separated(const BasicCStringRef &sep, Args...args) { + return ArgConcat, Args...>(ArgConcatSeparator(sep), args...); } template -ArgConcat, Args...> concat_separated(const BasicCStringRef &sep, Args&&...args) { - return ArgConcat, Args...>{ArgConcatSeparator{sep}, std::forward(args)...}; +ArgConcat, Args...> concat_separated(const BasicCStringRef &sep, Args...args) { + return ArgConcat, Args...>(ArgConcatSeparator(sep), args...); } template @@ -4160,7 +4179,7 @@ void format_arg(fmt::BasicFormatter &f, if (*end != '}') FMT_THROW(FormatError("missing '}' in format string")); - e.format<0>(f, format_str); + e.format_first(f, format_str); format_str = end + 1; } } // namespace fmt From 9acbaacdae06c4c4b67b9f158beb7f1d3366c910 Mon Sep 17 00:00:00 2001 From: Andreas Wass Date: Thu, 2 Nov 2017 11:52:05 +0100 Subject: [PATCH 4/4] Make ArgConcatNoSeparator::add_separator a single templated function --- fmt/format.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fmt/format.h b/fmt/format.h index 89d9a6c9..426fa7c1 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -4087,11 +4087,8 @@ void format_arg(fmt::BasicFormatter &f, } struct ArgConcatNoSeparator { - template - void add_separator(fmt::BasicFormatter&) const {} - - template - void add_separator(fmt::BasicFormatter&) const {} + template + void add_separator(T&) const {} }; template