diff --git a/fmt/format.h b/fmt/format.h index 7748bfac..426fa7c1 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -4085,6 +4085,100 @@ void format_arg(fmt::BasicFormatter &f, } format_str = end + 1; } + +struct ArgConcatNoSeparator { + template + void add_separator(T&) 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 { +}; + +template +struct ArgConcat: public Separator { + + ArgConcat(const Separator &sep): Separator(sep) {} + + template + void 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 + 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 >(a)); + ArgConcat::format(f, save); + } + + template + void format_first(fmt::BasicFormatter &f, + const Char *&format_str) const { + const Char* save = format_str; + f.format(format_str, + internal::MakeArg >(a)); + ArgConcat::format(f, save); + } +}; + +template +ArgConcat concat(Args...args) { + return ArgConcat(ArgConcatNoSeparator(), args...); +} + +template +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), 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_first(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);