diff options
| -rw-r--r-- | include/cxxformat/runtime.hpp | 135 | ||||
| -rw-r--r-- | main.cpp | 3 |
2 files changed, 62 insertions, 76 deletions
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<std::variant<std::string, format_specifier>> parts; + using Part = std::variant<std::string_view, format_specifier>; + std::string formatString; + std::vector<Part> parts; std::size_t argCount; template<typename Out> @@ -43,7 +45,10 @@ namespace format { }; public: - format_template(std::vector<std::variant<std::string, format_specifier>>&& 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<format_output_compatible Output, typename... Args> requires (has_some_formatter<std::decay_t<Args>> && ...) 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<std::variant<std::string, format_specifier>> parts; - - const auto addString = [&parts](std::string_view part) + void parseFormat(std::string_view fmt) { - if (!parts.empty() && std::holds_alternative<std::string>(parts.back())) - { - std::get<std::string>(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<std::size_t>(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<format_output_compatible Output, typename... Args> requires (has_some_formatter<std::decay_t<Args>> && ...) - 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>(output), std::forward<Args>(args)...); + run_time::format_template{fmt}(std::forward<Output>(output), std::forward<Args>(args)...); } template<format_output_compatible Output, typename... Args> requires (has_some_formatter<std::decay_t<Args>> && ...) - 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<typename... Args> requires (has_some_formatter<std::decay_t<Args>> && ...) - 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>(args)...); @@ -234,7 +221,7 @@ namespace format { } template<typename... Args> requires (has_some_formatter<std::decay_t<Args>> && ...) - 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>(args)...); @@ -22,8 +22,7 @@ int main(int argc, char* argv[]) if (argc == 2) { - std::string_view fmt{argv[1]}; - format_nothrow_to(fmt, stdout, "Knirp", 's', argv[0], 42u, 1.337); + format_nothrow_to(argv[1], stdout, "Knirp", 's', argv[0], 42u, 1.337); putchar('\n'); return 0; } |
