#include #include #include #include #include #include #include template class Formatter { public: static void format(std::ostream& os, T const& data) { os << data; } }; // string specialization template <> class Formatter { public: static void format(std::ostream& os, std::string const& data) { os << '<' << data << '>'; } }; template class Formatter> { public: static void format(std::ostream& os, std::vector const& data) { os << "{"; for (T const& element : data) { os << " "; Formatter::format(os, element); } os << " }"; } }; namespace details { void format(std::ostream& os, std::string specifier) { auto index = specifier.find("{}"); if (index != std::string::npos) { throw std::runtime_error { "Excessive format specifier" }; } os << specifier; } template void format(std::ostream& os, std::string specifier, Arg const& arg, Args const&... args) { auto index = specifier.find("{}"); if (index == std::string::npos) { throw std::runtime_error { "Extra argument passed to format" }; } std::string prefix { specifier }; os << prefix.erase(index); Formatter::format(os, arg); details::format(os, specifier.substr(index + 2), args...); } } template std::string format(std::string const& specifier, Args const&... args) { std::ostringstream oss { }; details::format(oss, specifier, args...); return oss.str(); } int main() { std::string str1 { format("Hello world!") }; assert(str1 == "Hello world!"); std::string str2 { format("int: {}", 5) }; assert(str2 == "int: 5"); std::string my_string { "my string" }; std::string str3 { format("string: {}", my_string) }; assert(str3 == "string: "); std::string str4 { format("{}:{}:{}", 1, 2, 3) }; assert(str4 == "1:2:3"); std::vector v { "abc", "def", "ghi" }; std::string str5 { format("vector: {}", v) }; assert(str5 == "vector: { }"); try { format("{}"); assert(false); } catch (...) { } try { format("my string", 5); assert(false); } catch (...) { } }