From d6004e950ae8cd6162cbde6b48f3f141393550fc Mon Sep 17 00:00:00 2001 From: Markus Mittendrein Date: Sat, 8 Jan 2022 22:35:49 +0100 Subject: Store format string in runtime format_template and use string_views instead of strings as parts --- include/cxxformat/runtime.hpp | 135 +++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 74 deletions(-) (limited to 'include') diff --git a/include/cxxformat/runtime.hpp b/include/cxxformat/runtime.hpp index 7e25ee2..a33264e 100644 --- a/include/cxxformat/runtime.hpp +++ b/include/cxxformat/runtime.hpp @@ -12,7 +12,9 @@ namespace format { namespace { namespace run_time { class format_template { - std::vector> parts; + using Part = std::variant; + std::string formatString; + std::vector parts; std::size_t argCount; template @@ -43,7 +45,10 @@ namespace format { }; public: - format_template(std::vector>&& parts, std::size_t argCount) noexcept : parts{std::move(parts)}, argCount{argCount} {} + format_template(const std::string& formatString) : formatString{formatString}, argCount{0} + { + parseFormat(this->formatString); + } template requires (has_some_formatter> && ...) void operator()(Output&& output, Args&&... args) const @@ -109,7 +114,7 @@ namespace format { } return argFormatters[spec.argIndex]->format(out, spec, minWidth, precision); }, - [&out](std::string text) { out(text); } + [&out](std::string_view text) { out(text); } }; for (const auto& part : parts) { @@ -118,97 +123,79 @@ namespace format { } private: - void checkArgCount(std::size_t args) const - { - if (args > argCount) - { - throw std::invalid_argument{"Too many arguments passed! Expected %u."_format(argCount)}; - } - else if (args < argCount) - { - throw std::invalid_argument{"Not enough arguments passed! Expected %u."_format(argCount)}; - } - } - }; - - format_template parseFormat(std::string_view fmt) - { - ArgIndex nextArg{0}; - ArgIndex maxArg{0}; - - std::vector> parts; - - const auto addString = [&parts](std::string_view part) + void parseFormat(std::string_view fmt) { - if (!parts.empty() && std::holds_alternative(parts.back())) - { - std::get(parts.back()).append(part); - } - else + ArgIndex nextArg{0}; + for (std::size_t pos{0}; pos < fmt.size(); ) { - parts.emplace_back(std::string{part}); - } - }; - - for (std::size_t pos{0}; pos < fmt.size(); ) - { - const auto nextSpec = fmt.find('%', pos); - if (nextSpec == std::string_view::npos) - { - parts.emplace_back(std::string{fmt.substr(pos)}); - break; - } - - try { - if (nextSpec + 1 >= fmt.size()) + const auto nextSpec = fmt.find('%', pos); + if (nextSpec == std::string_view::npos) { - throw exceptions::incomplete_specifier{}; + parts.emplace_back(fmt.substr(pos)); + break; } - const auto specStart = fmt[nextSpec + 1]; - if (specStart == '%') - { - // + 1 to include the first % - addString(fmt.substr(pos, nextSpec - pos + 1)); - pos = nextSpec + 2; - } - else - { - const auto [newPos, spec, newNextArg, specMaxArg] = parseSpec(fmt, nextSpec + 1, nextArg); - if (!checkValidConversion(spec.conversion)) + try { + if (nextSpec + 1 >= fmt.size()) { - throw std::invalid_argument{"Invalid conversion specifier ‘%c’"_format(spec.conversion)}; + throw exceptions::incomplete_specifier{}; } - addString(fmt.substr(pos, nextSpec - pos)); + const auto specStart = fmt[nextSpec + 1]; + if (specStart == '%') + { + // + 1 to include the first % + parts.emplace_back(fmt.substr(pos, nextSpec - pos + 1)); + pos = nextSpec + 2; + } + else + { + const auto [newPos, spec, newNextArg, maxArg] = parseSpec(fmt, nextSpec + 1, nextArg); + if (!checkValidConversion(spec.conversion)) + { + throw std::invalid_argument{"Invalid conversion specifier ‘%c’"_format(spec.conversion)}; + } - nextArg = newNextArg; - maxArg = std::max(maxArg, specMaxArg); - pos = newPos; + parts.emplace_back(fmt.substr(pos, nextSpec - pos)); - parts.emplace_back(spec); + nextArg = newNextArg; + argCount = std::max(argCount, static_cast(maxArg)); + pos = newPos; + + parts.emplace_back(spec); + } + } catch(const exceptions::incomplete_specifier&) { + throw exceptions::incomplete_specifier{"Incomplete format specifier “%s” at end of format string"_format(fmt.substr(nextSpec))}; + } catch(const std::exception& e) { + constexpr std::size_t showPartSize = 10; + throw std::invalid_argument{"%s when parsing format specifier “%s%s” at column %u"_format(e.what(), fmt.substr(nextSpec, showPartSize), nextSpec + showPartSize > fmt.size() ? "" : "…", nextSpec)}; } - } catch(const exceptions::incomplete_specifier&) { - throw exceptions::incomplete_specifier{"Incomplete format specifier “%s” at end of format string"_format(fmt.substr(nextSpec))}; - } catch(const std::exception& e) { - constexpr std::size_t showPartSize = 10; - throw std::invalid_argument{"%s when parsing format specifier “%s%s” at column %u"_format(e.what(), fmt.substr(nextSpec, showPartSize), nextSpec + showPartSize > fmt.size() ? "" : "…", nextSpec)}; } } - return {std::move(parts), maxArg}; - } + void checkArgCount(std::size_t args) const + { + if (args > argCount) + { + throw std::invalid_argument{"Too many arguments passed! Expected %u."_format(argCount)}; + } + else if (args < argCount) + { + throw std::invalid_argument{"Not enough arguments passed! Expected %u."_format(argCount)}; + } + } + }; } } template requires (has_some_formatter> && ...) - void format_to(std::string_view fmt, Output&& output, Args&&... args) + void format_to(const std::string& fmt, Output&& output, Args&&... args) { - run_time::parseFormat(fmt)(std::forward(output), std::forward(args)...); + run_time::format_template{fmt}(std::forward(output), std::forward(args)...); } template requires (has_some_formatter> && ...) - void format_nothrow_to(std::string_view fmt, Output&& output, Args&&... args) noexcept + void format_nothrow_to(const std::string& fmt, Output&& output, Args&&... args) noexcept { try { @@ -226,7 +213,7 @@ namespace format { } template requires (has_some_formatter> && ...) - std::string format(std::string_view fmt, Args&&... args) + std::string format(const std::string& fmt, Args&&... args) { std::ostringstream out; format_to(fmt, out, std::forward(args)...); @@ -234,7 +221,7 @@ namespace format { } template requires (has_some_formatter> && ...) - std::string format_nothrow(std::string_view fmt, Args&&... args) + std::string format_nothrow(const std::string& fmt, Args&&... args) { std::ostringstream out; format_nothrow_to(fmt, out, std::forward(args)...); -- cgit v1.2.3-54-g00ecf