diff options
| author | Markus Mittendrein <maxmitti@maxmitti.tk> | 2022-03-22 21:23:17 +0100 |
|---|---|---|
| committer | Markus Mittendrein <maxmitti@maxmitti.tk> | 2022-03-22 21:23:17 +0100 |
| commit | 15c989f527d33b2c3233d78e60b8c7a44f088732 (patch) | |
| tree | 18d80777831b78d9b0bd1f2baddf4840d57dab88 /include | |
| parent | 33f25e99c9484039734018c3d2dfc348a5d68abe (diff) | |
| download | cxxformat-15c989f527d33b2c3233d78e60b8c7a44f088732.tar.gz cxxformat-15c989f527d33b2c3233d78e60b8c7a44f088732.zip | |
Simplifications and refactoring
Diffstat (limited to 'include')
| -rw-r--r-- | include/cxxformat/core.hpp | 10 | ||||
| -rw-r--r-- | include/cxxformat/formatters.hpp | 10 | ||||
| -rw-r--r-- | include/cxxformat/runtime.hpp | 59 |
3 files changed, 50 insertions, 29 deletions
diff --git a/include/cxxformat/core.hpp b/include/cxxformat/core.hpp index fdddece..662f4f8 100644 --- a/include/cxxformat/core.hpp +++ b/include/cxxformat/core.hpp @@ -54,6 +54,16 @@ namespace format { return s[i]; } + constexpr const char* begin() const noexcept + { + return s; + } + + constexpr const char* end() const noexcept + { + return s + N; + } + private: template<std::size_t... indices> constexpr str(const char (&s)[N + 1], std::index_sequence<indices...>) noexcept : s{s[indices]...} {} diff --git a/include/cxxformat/formatters.hpp b/include/cxxformat/formatters.hpp index 1bee0f4..b098a70 100644 --- a/include/cxxformat/formatters.hpp +++ b/include/cxxformat/formatters.hpp @@ -15,10 +15,16 @@ namespace format { namespace { + constexpr bool shortContains(std::string_view search, char c) + { + // std::string_view has find which uses memchr. We want to avoid the comparatively heavy memchr for the short strings this is used for. + return std::find(std::begin(search), std::end(search), c) != std::end(search); + } + template<str what> constexpr auto operator ""_contains() noexcept { - return [](char c) constexpr noexcept { return std::string_view{what}.find(c) != std::string_view::npos; }; + return [](char c) constexpr noexcept { return shortContains(what, c); }; } template<typename Outputter> @@ -110,7 +116,7 @@ namespace format { constexpr void simpleConversionSpecifierCheck(std::string_view supported, char c, std::string_view typeDesc) { using namespace std::string_literals; - if (supported.find(c) == std::string_view::npos) + if (!shortContains(supported, c)) { throw std::invalid_argument{"Unsupported conversion specifier ‘"s + c + "’ for "s + std::string{typeDesc} + "; Supported are "s + std::string{supported}}; } diff --git a/include/cxxformat/runtime.hpp b/include/cxxformat/runtime.hpp index 7659468..1bc86dd 100644 --- a/include/cxxformat/runtime.hpp +++ b/include/cxxformat/runtime.hpp @@ -54,7 +54,7 @@ namespace format { public: format_template(const std::string& formatString) : formatString{formatString}, argCount{0} { - parseFormat(this->formatString); + parseFormat(this->formatString); } template<format_output_compatible Output, typename... Args> requires (has_some_formatter<std::decay_t<Args>> && ...) @@ -108,10 +108,11 @@ namespace format { } } - const overloaded_callable visitor{ - [&out, &indexArgs, &argFormatters](const format_specifier& fullSpec) + for (const auto& part : parts) + { + if (std::holds_alternative<format_specifier>(part)) { - const auto [spec, source] = fullSpec; + const auto [spec, source] = std::get<format_specifier>(part); optional_int<std::size_t> minWidth = spec.minWidth; if (spec.flags.widthAsArg) { @@ -125,29 +126,36 @@ namespace format { assert(precision); } try { - return argFormatters[spec.argIndex]->format(out, spec, minWidth, precision); + argFormatters[spec.argIndex]->format(out, spec, minWidth, precision); } catch(const std::exception& e) { throw std::runtime_error{"%s; when substituting format specifier “%s”"_format(e.what(), source)}; } - }, - [&out](std::string_view text) { out(text); } - }; - for (const auto& part : parts) - { - std::visit(visitor, part); + } + else + { + out(std::get<std::string_view>(part)); + } } } private: void parseFormat(std::string_view fmt) { + const auto addString = [this](std::string_view str) + { + if (!str.empty()) + { + parts.emplace_back(str); + } + }; + ArgIndex nextArg{0}; for (std::size_t pos{0}; pos < fmt.size(); ) { const auto nextSpec = fmt.find('%', pos); if (nextSpec == std::string_view::npos) { - parts.emplace_back(fmt.substr(pos)); + addString(fmt.substr(pos)); break; } @@ -161,7 +169,7 @@ namespace format { if (specStart == '%') { // + 1 to include the first % - parts.emplace_back(fmt.substr(pos, nextSpec - pos + 1)); + addString(fmt.substr(pos, nextSpec - pos + 1)); pos = nextSpec + 2; } else @@ -172,7 +180,7 @@ namespace format { throw std::invalid_argument{"Invalid conversion specifier ‘%c’"_format(spec.conversion)}; } - parts.emplace_back(fmt.substr(pos, nextSpec - pos)); + addString(fmt.substr(pos, nextSpec - pos)); nextArg = newNextArg; argCount = std::max(argCount, static_cast<std::size_t>(maxArg)); @@ -211,21 +219,18 @@ namespace format { } template<format_output_compatible Output, typename... Args> requires (has_some_formatter<std::decay_t<Args>> && ...) - void format_nothrow_to(const std::string& fmt, Output&& output, Args&&... args) noexcept + void format_nothrow_to(const std::string& fmt, Output&& output, Args&&... args) noexcept try { - try - { format_to(fmt, std::forward<Output>(output), std::forward<Args>(args)...); - } - catch(const std::exception& e) - { - // "Formatting error: %s"_format_to(output, e.what()); - // workaround for clang bug: - // TODO: Remove when fixed - const auto out = make_output(output); - out("Formatting error: "); - out(e.what()); - } + } + catch(const std::exception& e) + { + // "Formatting error: %s"_format_to(output, e.what()); + // workaround for clang bug: + // TODO: Remove when fixed + const auto out = make_output(output); + out("Formatting error: "); + out(e.what()); } template<typename... Args> requires (has_some_formatter<std::decay_t<Args>> && ...) |
