summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/cxxformat/core.hpp10
-rw-r--r--include/cxxformat/formatters.hpp10
-rw-r--r--include/cxxformat/runtime.hpp59
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>> && ...)