summaryrefslogtreecommitdiffstats
path: root/cxxformat.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'cxxformat.hpp')
-rw-r--r--cxxformat.hpp656
1 files changed, 0 insertions, 656 deletions
diff --git a/cxxformat.hpp b/cxxformat.hpp
deleted file mode 100644
index 47c0cd7..0000000
--- a/cxxformat.hpp
+++ /dev/null
@@ -1,656 +0,0 @@
-/*
-Copyright 2019 Mittendrein Markus
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-#pragma once
-#include <string>
-#include <string_view>
-#include <optional>
-#include <cstring>
-#include <stdexcept>
-#include <cstdint>
-#include <concepts>
-
-namespace format {
-
-using FormatException = std::invalid_argument;
-
-template<typename To, typename From, typename Enable = void>
-struct FormatConvert {};
-
-template<typename To, typename From>
-struct ImplicitConversion
-{
- static constexpr To convert(From from)
- {
- return from;
- }
-};
-
-template<std::signed_integral From>
-struct FormatConvert<std::intmax_t, From> : ImplicitConversion<std::intmax_t, From> {};
-
-template<std::unsigned_integral From>
-struct FormatConvert<std::intmax_t, From, std::enable_if_t<(sizeof(From) < sizeof(std::intmax_t))>> : ImplicitConversion<std::intmax_t, From> {};
-
-template<std::unsigned_integral From>
-struct FormatConvert<std::uintmax_t, From> : ImplicitConversion<std::uintmax_t, From> {};
-
-template<std::convertible_to<std::string_view> String>
-struct FormatConvert<std::string_view, String> : ImplicitConversion<std::string_view, const String &> {};
-
-template<std::floating_point From>
-struct FormatConvert<long double, From> : ImplicitConversion<long double, const From &> {};
-
-template<typename Pointer>
-struct FormatConvert<const void *, Pointer, std::enable_if_t<std::is_pointer_v<Pointer> && !std::is_array_v<std::remove_pointer_t<Pointer>>>> : ImplicitConversion<const void *, const Pointer> {};
-
-template<typename Pointer>
-struct FormatConvert<const void *, Pointer, std::enable_if_t<std::is_pointer_v<Pointer> && std::is_array_v<std::remove_pointer_t<Pointer>>>> : ImplicitConversion<const void*, const Pointer> {};
-
-template<typename From, typename Enable = void>
-struct AutoConversion
-{
- static bool conversion()
- {
- throw FormatException{""};
- }
-};
-
-template<char Conversion, typename From>
-struct SimpleAutoConversion
-{
- static constexpr char conversion()
- {
- return Conversion;
- }
-};
-
-template<std::unsigned_integral From>
-struct AutoConversion<From> : SimpleAutoConversion<'u', From> {};
-
-template<std::signed_integral From>
-struct AutoConversion<From> : SimpleAutoConversion<'d', From> {};
-
-template<std::floating_point From>
-struct AutoConversion<From> : SimpleAutoConversion<'G', From> {};
-
-template<std::convertible_to<std::string_view> From>
-struct AutoConversion<From> : SimpleAutoConversion<'s', From> {};
-
-template<>
-struct AutoConversion<char> : SimpleAutoConversion<'c', const char> {};
-
-namespace detail
-{
- template<typename T, bool Ok = false>
- struct StointHelper
- {
- static T convert(const std::string &, std::size_t *, int)
- {
- static_assert(Ok, "stoint: Unsupported type");
- return T{};
- }
- };
-
- template<typename T, T(converter)(const std::string &, std::size_t *, int)>
- struct StointHelperConverter
- {
- static T convert(const std::string &str, std::size_t *pos, int base)
- {
- return converter(str, pos, base);
- }
- };
-
- template<>
- struct StointHelper<int> : StointHelperConverter<int, std::stoi> {};
- template<>
- struct StointHelper<long> : StointHelperConverter<long, std::stol> {};
- template<>
- struct StointHelper<long long> : StointHelperConverter<long long, std::stoll> {};
- template<>
- struct StointHelper<unsigned long> : StointHelperConverter<unsigned long, std::stoul> {};
- template<>
- struct StointHelper<unsigned long long> : StointHelperConverter<unsigned long long, std::stoull> {};
-
- template<typename T>
- T stoint(const std::string &str, std::size_t *pos = nullptr, int base = 10)
- {
- return StointHelper<T>::convert(str, pos, base);
- }
-
- template<typename... Args>
- std::string strprintf(const char *fmt, Args... args)
- {
- int size = std::snprintf(nullptr, 0, fmt, args...) + 1;
- std::string result;
- result.resize(size);
- std::snprintf(result.data(), size, fmt, args...);
- result.resize(size - 1);
- return result;
- }
-
- template<typename To, typename From, bool allowFallback, typename Enable = void>
- struct FormatConvertHelper
- {
- static constexpr To convert(const From &)
- {
- static_assert(allowFallback, "Impossible conversion needed");
- throw FormatException{""};
- }
- };
-
- template<typename T, bool allowFallback>
- struct FormatConvertHelper<T, T, allowFallback>
- {
- static constexpr const T &convert(const T &from)
- {
- return from;
- }
- };
-
- template<typename To, typename From, bool allowFallback>
- struct FormatConvertHelper<To, From, allowFallback, std::enable_if_t<!std::is_same_v<To, From> && std::is_same_v<std::remove_cvref_t<decltype(FormatConvert<To, std::remove_cvref_t<From>>::convert(std::declval<From>()))>, std::remove_cvref_t<To>>>>
- {
- static constexpr To convert(const From &from)
- {
- return FormatConvert<To, std::remove_cvref_t<From>>::convert(from);
- }
- };
-
- template<typename To, bool allowFallback = true, typename From>
- auto convert(const From &from, const char *toName, std::size_t index = 0)
- {
- try
- {
- return FormatConvertHelper<To, From, allowFallback>::convert(from);
- }
- catch (const FormatException &)
- {
- throw FormatException{"Argument#" + std::to_string(index) + " is not convertible to " + std::string{toName}};
- }
- }
-
- template<typename From, typename Enable = void>
- struct StringOrStringViewHelper
- {
- using type = std::string_view;
- };
-
- template<typename From>
- struct StringOrStringViewHelper<From, std::enable_if_t<std::is_same_v<std::string, std::decay_t<decltype(FormatConvert<std::string, From>::convert(std::declval<From>()))>>>>
- {
- using type = std::string;
- };
-
- template<typename From>
- using string_or_string_view = typename StringOrStringViewHelper<From>::type;
-
- template<bool allowPointer = false, bool allowFallback = true, typename From>
- constexpr char autoConversionSpecifier(std::size_t index = 0)
- {
- try
- {
- if constexpr (!allowFallback)
- {
- if constexpr(!std::is_same_v<char, decltype(AutoConversion<std::remove_cvref_t<From>>::conversion())>)
- {
- if constexpr (std::is_pointer_v<From>)
- {
- if constexpr (allowPointer)
- {
- return 'p';
- }
- else
- {
- if constexpr (!allowFallback) static_assert(allowPointer, "%v doesn't allow automatic conversions for pointers; use %V to alsow allow arbitrary pointers");
- else throw FormatException{"Argument#" + std::to_string(index) + " is not applicable for auto conversion with %v, use %V to also allow arbitrary pointers"};
- }
- }
- }
- }
- return AutoConversion<std::remove_cvref_t<From>>::conversion();
- }
- catch (const FormatException &)
- {
- if constexpr (std::is_pointer_v<From>)
- {
- if constexpr (allowPointer)
- {
- return 'p';
- }
- else
- {
- if constexpr (!allowFallback) static_assert(allowPointer, "%v doesn't allow automatic conversions for pointers; use %V to alsow allow arbitrary pointers");
- throw FormatException{"Argument#" + std::to_string(index) + " is not applicable for auto conversion with %v, use %V to also allow arbitrary pointers"};
- }
- }
- throw FormatException{"Argument#" + std::to_string(index) + " is not applicable for auto conversion (%v / %V)"};
- }
- }
-
- std::string format(std::string_view fmt, const std::size_t argumentIndex)
- {
- const auto fmtSize = fmt.size();
- for (std::size_t i = 0; i < fmtSize; ++i)
- {
- if (fmt[i] == '%')
- {
- if (i >= fmtSize - 1)
- {
- throw FormatException{"Incomplete directive"};
- }
-
- if (fmt[++i] == '%')
- {
- return std::string{fmt.substr(0, i)} + format(fmt.substr(i + 1), argumentIndex);
- }
-
- throw FormatException{"Not enough arguments passed"};
- }
- }
- return std::string{fmt};
- }
-
- template<typename Arg, typename... Args>
- std::string format(std::string_view fmt, const std::size_t argumentIndex, Arg &&arg, Args &&... args)
- {
- const auto fmtSize = fmt.size();
- for (std::size_t i = 0; i < fmtSize; ++i)
- {
- auto checkFmtLength = [i, fmtSize]
- {
- if (i >= fmtSize - 1)
- {
- throw FormatException{"Incomplete directive"};
- }
- };
- const auto current = fmt[i];
- if (current == '%')
- {
- const auto directiveStart = i;
- checkFmtLength();
- std::string specification{"%"};
-
- if (fmt[i + 1] == '%') // handle %% here
- {
- return std::string{fmt.substr(0, directiveStart + 1)} + format(fmt.substr(i + 1), argumentIndex, std::forward<Arg>(arg), std::forward<Args>(args)...);
- }
- while(i < fmtSize - 1)
- {
- switch (const auto flag = fmt[++i])
- {
- case '#': case '0': case '-': case ' ': case '+':
- specification.push_back(flag);
- checkFmtLength();
- break;
- default:
- --i;
- goto afterFlagLoop;
- }
- }
- afterFlagLoop:
-
- std::string tempNumber;
- // field width
- while (std::isdigit(fmt[++i]))
- {
- tempNumber.push_back(fmt[i]);
- checkFmtLength();
- }
- --i;
- const auto fieldWidth = tempNumber.empty() ? 0 : stoint<std::size_t>(tempNumber);
- tempNumber.clear();
-
- std::optional<std::size_t> precision;
- // precision
- if (fmt[++i] == '.')
- {
- checkFmtLength();
- while (std::isdigit(fmt[++i]))
- {
- tempNumber.push_back(fmt[i]);
- checkFmtLength();
- }
- precision = tempNumber.empty() ? 0 : stoint<std::size_t>(tempNumber);
- }
- --i;
-
- std::string conversion{fmt[++i]};
- if(conversion.front() == 'v')
- {
- conversion = autoConversionSpecifier<false, true, Arg>(argumentIndex);
- }
- else if(conversion.front() == 'V')
- {
- conversion = autoConversionSpecifier<true, true, Arg>(argumentIndex);
- }
-
- auto continueFormatting = [directiveStart, &args..., fmt, &specification, &fieldWidth, &precision, i, conversion, argumentIndex](auto length, auto converted)
- {
- if (fieldWidth) specification += std::to_string(fieldWidth);
- if (precision) specification += '.' + std::to_string(*precision);
- if (length) specification += length;
- specification += conversion;
- return std::string{fmt.substr(0, directiveStart)} + strprintf(specification.c_str(), converted) + format(fmt.substr(i + 1), argumentIndex + 1, std::forward<Args>(args)...);
- };
-
- switch (conversion.back())
- {
- case 'c':
- return continueFormatting(false, convert<char>(std::forward<Arg>(arg), "character", argumentIndex));
-
- case 'd': case 'i':
- return continueFormatting('j', convert<std::intmax_t>(std::forward<Arg>(arg), "signed integer", argumentIndex));
-
- case 'u': case 'o': case 'x': case 'X':
- return continueFormatting('j', convert<std::uintmax_t>(std::forward<Arg>(arg), "unsigned integer", argumentIndex));
-
- case 's':
- {
- const auto& string = convert<string_or_string_view<std::remove_cvref_t<Arg>>>(std::forward<Arg>(arg), "string", argumentIndex);
- precision = precision ? std::min(*precision, string.size()) : string.size();
- return continueFormatting(false, string.data());
- }
-
- case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': case 'a': case 'A':
- return continueFormatting(false, convert<double>(std::forward<Arg>(arg), "double", argumentIndex));
-
- case 'p':
- return continueFormatting(false, convert<const void *>(std::forward<Arg>(arg), "pointer", argumentIndex));
-
- default:
- throw FormatException{std::string{"Unknown conversion specifier: \""} + conversion + "\""};
- }
- }
- }
-
- throw FormatException{std::to_string(argumentIndex - 1) + " placeholders found, but " + std::to_string(argumentIndex + sizeof...(args)) + " arguments passed"};
- }
-}
-
-template<bool NoThrow = false, typename String, typename... Args>
-std::string format(const String &fmt, Args &&... args)
-{
- const auto fmtStr = detail::convert<std::string_view, false>(fmt, "string");
- try
- {
- return detail::format(fmtStr, 1, std::forward<Args>(args)...);
- }
- catch (const FormatException &e)
- {
- const auto &message = std::string{"format(\""} + std::string{fmtStr} + "\", " + std::to_string(sizeof...(args)) + " more arguments...): " + e.what();
-
- if constexpr (NoThrow) return message;
- else throw FormatException(message);
- }
-}
-
-template<size_t N>
-struct str
-{
-public:
- const char s[N + 1];
- static constexpr size_t size = N;
-
-private:
- template<size_t... indices>
- constexpr str<N + 1> appendCharHelper(const char c, std::index_sequence<indices...>) const
- {
- return str<N + 1>{s[indices]..., c};
- }
-
- template<size_t... indices>
- static constexpr str<N + 1> prependCharHelper(const char c, const str<N> &s, std::index_sequence<indices...>)
- {
- return str<N + 1>{c, s.s[indices]...};
- }
-
- template<auto other, size_t... ownIndices, size_t... othersIndices>
- constexpr str<N + sizeof...(othersIndices)> appendStrHelper(std::index_sequence<ownIndices...>, std::index_sequence<othersIndices...>) const
- {
- return str<N + sizeof...(othersIndices)>{s[ownIndices]..., other.s[othersIndices]...};
- }
-
-public:
- template<size_t... indices>
- constexpr str(const char (&s)[N + 1], std::index_sequence<indices...>) : s{s[indices]...}
- {
-
- }
-
- constexpr str(const char (&s)[N + 1]) : str{s, std::make_index_sequence<N + 1>()}
- {
-
- }
-
- template<typename... Chars>
- constexpr str(Chars... chars) : s{chars..., '\0'}
- {
- static_assert(N == sizeof...(chars), "O.o");
- }
-
- template<size_t index>
- constexpr char get() const
- {
- static_assert(index < N, "str::get: index out of range");
- return s[index];
- }
-
- template<size_t offset, size_t... indices>
- constexpr str<sizeof...(indices)> extract(std::index_sequence<indices...>) const
- {
- static_assert(((indices + offset < N) && ...), "str::extract: index out of range");
- return str<sizeof...(indices)>{s[indices + offset]...};
- }
-
- constexpr str<N + 1> operator+(const char c) const
- {
- return appendCharHelper(c, std::make_index_sequence<N>());
- }
-
- template<size_t otherN>
- constexpr str<N + otherN> operator+(const str<otherN> &other) const
- {
- return appendStrHelper<other>(std::make_index_sequence<N>(), std::make_index_sequence<otherN>());
- }
-
- template<size_t _N>
- friend constexpr str<_N + 1> operator+(const char c, const str<_N> &other);
-};
-
-template<size_t N>
-constexpr str<N + 1> operator+(const char c, const str<N> &other)
-{
- return str<N>::prependCharHelper(c, other, std::make_index_sequence<N>());
-}
-
-template<size_t N>
-str(const char(&)[N]) -> str<N - 1>;
-
-template<typename... Chars>
-str(Chars... chars) -> str<sizeof...(Chars)>;
-
-namespace detail
-{
- template<size_t i, size_t N>
- constexpr char get(const str<N>& s)
- {
- return s.template get<i>();
- }
-
- template<size_t N, size_t offset, size_t... indices>
- constexpr str<sizeof...(indices)> extractStr(const str<N> &source, std::index_sequence<indices...>)
- {
- return str<sizeof...(indices)>{get<indices + offset>(source)...};
- }
-
- template<size_t start, size_t end = std::string::npos, bool endIsIndex = false, size_t N>
- constexpr str<end == std::string::npos ? N - start : (endIsIndex ? end + 1 - start : end)> substr(const str<N> &s)
- {
- static_assert(!endIsIndex || end < N, "substr: end index / substr-length is out of range");
- if constexpr (!endIsIndex && end == 0) return str<0>{};
- else if constexpr (end == std::string::npos) return substr<start, N - 1, true>(s);
- else if constexpr (!endIsIndex) return substr<start, start + end - 1, true>(s);
- else return s.template extract<start>(std::make_index_sequence<end - start + 1>());
- }
-
- template<typename T, auto s, size_t i = 0, bool allowSign = true, T parsedPart = 0>
- constexpr T strToInt()
- {
- if constexpr (i == s.size) return parsedPart;
- else
- {
- constexpr char c = get<i>(s);
- if constexpr (c == '-')
- {
- static_assert(allowSign, "strToInt: invalid character found");
- static_assert(std::is_signed_v<T>, "strToInt: negative sign found when converting to an unsigned type");
- return -strToInt<T, s, i + 1, false>();
- }
- else if constexpr (c == '+')
- {
- static_assert(allowSign, "strToInt: invalid character found");
- return strToInt<T, s, i + 1, false>();
- }
- else
- {
- static_assert(c >= '0' && c <= '9', "strToInt: invalid character found");
- return strToInt<T, s, i + 1, false, parsedPart * T{10} + T{c - '0'}>();
- }
- }
- }
-
- template<typename T, T N>
- constexpr size_t decimalLength()
- {
- if constexpr (N < 0) return 1 + decimalLength<T, -N>();
- else if constexpr (N < 10) return 1;
- else return 1 + decimalLength<T, N / 10>();
- }
-
- template<typename T, T N>
- constexpr str<decimalLength<T, N>()> toStr()
- {
- if constexpr (N < 0) return '-' + toStr<T, -N>();
- else if constexpr (N < 10)
- {
- return str<1>{'0' + N};
- }
- else return toStr<T, N / 10>() + char(N % 10 + '0');
- }
-
- template<auto fmt, size_t i, size_t argumentIndex>
- std::string format_s()
- {
- if constexpr (i == fmt.size) return fmt.s;
- else if constexpr (get<i>(fmt) == '%')
- {
- if constexpr (get<i + 1>(fmt) == '%') return substr<0, i, true>(fmt).s + format_s<substr<i + 2>(fmt), 0, argumentIndex>();
- else
- {
- static_assert(get<i + 1>(fmt) == '%', "Not enough arguments passed");
- throw FormatException{"Not enough arguments passed"};
- }
- }
- else return format_s<fmt, i + 1, argumentIndex>();
- }
-
- template<auto fmt, size_t i, size_t offset = 0>
- constexpr size_t getFlagEnd()
- {
- constexpr char next = get<i + offset>(fmt);
- if constexpr (next == '#' || next == '0' || next == '-' || next == ' ' || next == '+') return getFlagEnd<fmt, i, offset + 1>();
- else return i + offset;
- }
-
- template<auto fmt, size_t i>
- constexpr size_t getNumberEnd()
- {
- if constexpr (std::isdigit(get<i>(fmt))) return getNumberEnd<fmt, i + 1>();
- else return i;
- }
-
- template<char conversion, size_t argumentIndex, typename Arg>
- constexpr char checkReplaceAutoConversion()
- {
- if constexpr (conversion == 'v' || conversion == 'V') return autoConversionSpecifier<conversion == 'V', false, Arg>(argumentIndex);
- else return conversion;
- }
-
- template<bool havePrecision, auto fmt, size_t iAfterFieldWidth, size_t iAfterPrecision, typename String>
- size_t determineStringPrecision(const String& string)
- {
- if constexpr (havePrecision) return std::min(strToInt<size_t, substr<iAfterFieldWidth + 1, iAfterPrecision - 1, true>(fmt)>(), string.size());
- else return string.size();
- }
-
- template<auto fmt, char conversion, size_t i, size_t iAfterPrecision, char length>
- constexpr auto makeDirective()
- {
- if constexpr (length) return substr<i, iAfterPrecision - 1, true>(fmt) + length + conversion;
- else return substr<i, iAfterPrecision - 1, true>(fmt) + conversion;
- }
-
- template<char c, std::size_t size>
- consteval void showUnknownConversionSpecifier()
- {
- static_assert(size == 0, "Unknown conversion specifier!");
- }
-
- template<auto fmt, size_t i, size_t argumentIndex, typename Arg, typename... Args>
- std::string format_s(Arg &&arg, Args &&...args)
- {
- static_assert(i < fmt.size, "Too many arguments passed");
- if constexpr (i == fmt.size)
- {
- throw FormatException{std::to_string(argumentIndex - 1) + " placeholders found, but " + std::to_string(argumentIndex + sizeof...(args)) + " arguments passed"};
- }
- else if constexpr (get<i>(fmt) == '%')
- {
- if constexpr (get<i + 1>(fmt) == '%') return substr<0, i, true>(fmt).s + format_s<substr<i + 2>(fmt), 0, argumentIndex>(std::forward<Arg>(arg), std::forward<Args>(args)...);
-
- constexpr char iAfterFlag = getFlagEnd<fmt, i + 1>();
- constexpr size_t iAfterFieldWidth = getNumberEnd<fmt, iAfterFlag>();
-
- constexpr auto havePrecision = get<iAfterFieldWidth>(fmt) == '.';
- constexpr size_t iAfterPrecision = [havePrecision, iAfterFieldWidth]() -> size_t { if constexpr (havePrecision) return getNumberEnd<fmt, iAfterFieldWidth + 1>(); else return iAfterFieldWidth; }();
-
- constexpr char conversion = checkReplaceAutoConversion<get<iAfterPrecision>(fmt), argumentIndex, Arg>();
-
- if constexpr (conversion == 'c') return substr<0, i>(fmt).s + strprintf(makeDirective<fmt, conversion, i, iAfterPrecision, 0>().s, convert<char, false>(std::forward<Arg>(arg), "character", argumentIndex)) + format_s<substr<iAfterPrecision + 1>(fmt), 0, argumentIndex + 1>(std::forward<Args>(args)...);
- else if constexpr (conversion == 'd' || conversion == 'i') return substr<0, i>(fmt).s + strprintf(makeDirective<fmt, conversion, i, iAfterPrecision, 'j'>().s, convert<std::intmax_t, false>(std::forward<Arg>(arg), "signed integer", argumentIndex)) + format_s<substr<iAfterPrecision + 1>(fmt), 0, argumentIndex + 1>(std::forward<Args>(args)...);
- else if constexpr (conversion == 'u' || conversion == 'o' || conversion == 'x' || conversion == 'X') return substr<0, i>(fmt).s + strprintf(makeDirective<fmt, conversion, i, iAfterPrecision, 'j'>().s, convert<std::uintmax_t, false>(std::forward<Arg>(arg), "unsigned integer", argumentIndex)) + format_s<substr<iAfterPrecision + 1>(fmt), 0, argumentIndex + 1>(std::forward<Args>(args)...);
- else if constexpr (conversion == 'e' || conversion == 'E' || conversion == 'f' || conversion == 'F' || conversion == 'g' || conversion == 'G' || conversion == 'a' || conversion == 'A') return substr<0, i>(fmt).s + strprintf(makeDirective<fmt, conversion, i, iAfterPrecision, 0>().s, convert<double, false>(std::forward<Arg>(arg), "double", argumentIndex)) + format_s<substr<iAfterPrecision + 1>(fmt), 0, argumentIndex + 1>(std::forward<Args>(args)...);
- else if constexpr (conversion == 'p') return substr<0, i>(fmt).s + strprintf(makeDirective<fmt, conversion, i, iAfterPrecision, 0>().s, convert<const void *, false>(std::forward<Arg>(arg), "pointer", argumentIndex)) + format_s<substr<iAfterPrecision + 1>(fmt), 0, argumentIndex + 1>(std::forward<Args>(args)...);
- else if constexpr (conversion == 's')
- {
- const auto& string = convert<string_or_string_view<std::remove_cvref_t<Arg>>, false>(std::forward<Arg>(arg), "string", argumentIndex);
- return substr<0, i>(fmt).s + strprintf("%.*s", determineStringPrecision<havePrecision, fmt, iAfterFieldWidth, iAfterPrecision>(string), string.data()) + format_s<substr<iAfterPrecision + 1>(fmt), 0, argumentIndex + 1>(std::forward<Args>(args)...);
- }
- else
- {
- showUnknownConversionSpecifier<conversion, fmt.size>();
- throw FormatException{std::string{"Unknown conversion specifier: \""} + conversion + "\""};
- }
- }
- else return format_s<fmt, i + 1, argumentIndex>(std::forward<Arg>(arg), std::forward<Args>(args)...);
- }
-}
-
-template<str fmt, typename... Args>
-std::string format(Args &&...args)
-{
- return detail::format_s<fmt, 0, 1>(std::forward<Args>(args)...);
-}
-}