From 76f62be24b7e612b1f6880f9d7b0ddc07a8d38eb Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 18 Apr 2020 00:22:41 +0800 Subject: ... --- include/cru/common/format.hpp | 99 ++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 35 deletions(-) (limited to 'include/cru/common/format.hpp') diff --git a/include/cru/common/format.hpp b/include/cru/common/format.hpp index 4405c8c5..4276880e 100644 --- a/include/cru/common/format.hpp +++ b/include/cru/common/format.hpp @@ -7,62 +7,91 @@ namespace cru::util { namespace details { -template +template struct FormatTrait {}; template <> -struct FormatTrait { - static constexpr std::string_view placeholder = "{}"; - using ResultType = std::string; +struct FormatTrait { + using StringType = std::string; + using ViewType = std::string_view; using StreamType = std::stringstream; + static constexpr ViewType placeholder = "{}"; }; template <> -struct FormatTrait { - static constexpr std::wstring_view placeholder = L"{}"; - using ResultType = std::wstring; +struct FormatTrait { + using StringType = std::wstring; + using ViewType = std::wstring_view; using StreamType = std::wstringstream; + static constexpr ViewType placeholder = L"{}"; +}; +} // namespace details + +template +struct Formatter { + static typename TFormatTrait::StringType Format(const T& value) { + typename TFormatTrait::StreamType stream; + stream << value; + return stream.str(); + } }; -template -void FormatInternal(typename FormatTrait::StreamType& stream, - const TStringView& string) { - const auto find_result = string.find(FormatTrait::placeholder); - if (find_result != TStringView::npos) +namespace details { +template +void FormatInternal(TString& string) { + using Trait = FormatTrait; + constexpr const auto& placeholder = Trait::placeholder; + + const auto find_result = string.find(placeholder); + if (find_result != TString::npos) throw std::invalid_argument("There is more placeholders than args."); - stream << string; } -template -void FormatInternal(typename FormatTrait::StreamType& stream, - const TStringView& string, const T& arg, - const TRest&... args) { - const auto find_result = string.find(FormatTrait::placeholder); - if (find_result == TStringView::npos) - throw std::invalid_argument("There is less placeholders than args."); - - stream << string.substr(0, find_result); - stream << arg; +template +void FormatInternal(TString& string, const T& arg, const TRest&... args) { + using Trait = FormatTrait; + constexpr const auto& placeholder = Trait::placeholder; - FormatInternal(stream, string.substr(find_result + 2), args...); -} + const auto find_result = string.find(placeholder); + if (find_result == TString::npos) + throw std::invalid_argument("There is less placeholders than args."); -template -auto FormatTemplate(const TStringView& format, const T&... args) -> - typename FormatTrait::ResultType { - typename FormatTrait::StreamType stream; - FormatInternal(stream, format, args...); - return stream.str(); + string.replace(find_result, 2, Formatter::Format(arg)); + FormatInternal(string, args...); } } // namespace details template -std::wstring Format(const std::wstring_view& format, const T&... args) { - return details::FormatTemplate(format, args...); +std::wstring Format(std::wstring format, const T&... args) { + details::FormatInternal(format, args...); + return format; } template -std::string Format(const std::string_view& format, const T&... args) { - return details::FormatTemplate(format, args...); +std::string Format(std::string format, const T&... args) { + details::FormatInternal(format, args...); + return format; +} + +// Why is two overloads below exist? +// Because people should be able to pass string_view instance as format. +// However, the two overloads above do not accept string_view as format due to +// conversion from string_view to string is explicit. +// +// Why not just overload but SFINAE? +// Because I want to make two overloads below worse than the two ones above. +// Otherwise it will be ambiguous when pass const char* as format. +// + +template +auto Format(T format, const TArgs&... args) + -> std::enable_if_t, std::wstring> { + return Format(std::wstring{format}, args...); +} + +template +auto Format(T format, const TArgs&... args) + -> std::enable_if_t, std::string> { + return Format(std::string{format}, args...); } } // namespace cru::util -- cgit v1.2.3