summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/cxxformat/runtime.hpp135
-rw-r--r--main.cpp3
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)...);
diff --git a/main.cpp b/main.cpp
index 2011cb8..ff4a4a0 100644
--- a/main.cpp
+++ b/main.cpp
@@ -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;
}