From f0ad179a13aa778389f910b57822f54cdf923b55 Mon Sep 17 00:00:00 2001 From: Andreas Wass Date: Thu, 2 Nov 2017 09:24:01 +0100 Subject: [PATCH] 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);