#pragma once #include #include #include #include #include #include #include namespace util { class ArgBase { public: ArgBase() {} virtual ~ArgBase() {} virtual void Format(std::ostringstream& ss, const std::string& fmt) = 0; }; template class Arg : public ArgBase { public: Arg(T arg) : m_arg(arg) {} virtual ~Arg() {} virtual void Format(std::ostringstream& ss, const std::string&) { ss << m_arg; } private: T m_arg; }; class ArgArray : public std::vector { public: ArgArray() {} ~ArgArray() { std::for_each(begin(), end(), [](ArgBase* p) { delete p; }); } }; static void FormatItem(std::ostringstream& ss, const std::string& item, const ArgArray& args) { int index = 0; int alignment = 0; std::string fmt; char* endptr = nullptr; index = strtol(&item[0], &endptr, 10); if (index >= (int)args.size()) { return; } if (index < 0) { index = args.size() + index; } if (*endptr == ',') { alignment = strtol(endptr + 1, &endptr, 10); if (alignment > 0) { ss << std::right << std::setw(alignment); } else if (alignment < 0) { ss << std::left << std::setw(-alignment); } } if (*endptr == ':') { fmt = endptr + 1; } if (*endptr == '.' && *(endptr + 1) == '.') { auto endIndex = strtol(endptr + 2, &endptr, 10); if (endIndex < 0) { endIndex = args.size() + endIndex + 1; } for (; index < endIndex; index++) { args[index]->Format(ss, fmt); if (index != endIndex - 1) { ss << ", "; } } } else { args[index]->Format(ss, fmt); } return; } template static void Transfer(ArgArray& argArray, T t) { argArray.push_back(new Arg(t)); } template static void Transfer(ArgArray& argArray, T t, Args&&... args) { Transfer(argArray, t); Transfer(argArray, args...); } template std::string Format(const std::string& format, Args&&... args) { if (sizeof...(args) == 0) { return format; } ArgArray argArray; Transfer(argArray, args...); size_t start = 0; size_t pos = 0; std::ostringstream ss; while (true) { pos = format.find('{', start); if (pos == std::string::npos) { ss << format.substr(start); break; } ss << format.substr(start, pos - start); if (format[pos + 1] == '{') { ss << '{'; start = pos + 2; continue; } start = pos + 1; pos = format.find('}', start); if (pos == std::string::npos) { ss << format.substr(start - 1); break; } FormatItem(ss, format.substr(start, pos - start), argArray); start = pos + 1; } return ss.str(); } std::string Format(const std::string& format, const std::vector& args) { if (args.size() == 0) { return format; } ArgArray argArray; for (const auto& arg : args) { Transfer(argArray, arg); } size_t start = 0; size_t pos = 0; std::ostringstream ss; while (true) { pos = format.find('{', start); if (pos == std::string::npos) { ss << format.substr(start); break; } ss << format.substr(start, pos - start); if (format[pos + 1] == '{') { ss << '{'; start = pos + 2; continue; } start = pos + 1; pos = format.find('}', start); if (pos == std::string::npos) { ss << format.substr(start - 1); break; } FormatItem(ss, format.substr(start, pos - start), argArray); start = pos + 1; } return ss.str(); } }