diff options
| author | 2025-10-31 20:45:44 +0000 | |
|---|---|---|
| committer | 2025-10-31 20:45:44 +0000 | |
| commit | 088e5a4a257b616c0c6beb795ad59070fa5428bc (patch) | |
| tree | c3ffa6e7ed741cf7adb84a968a4e809e72e67ecf | |
| parent | Revert "Remove some unnecessary casts in ssl_openssl". (diff) | |
Update the vendored libraries.
| -rw-r--r-- | vendor/README.md | 6 | ||||
| -rw-r--r-- | vendor/fmt/base.h | 209 | ||||
| -rw-r--r-- | vendor/fmt/color.h | 36 | ||||
| -rw-r--r-- | vendor/fmt/compile.h | 203 | ||||
| -rw-r--r-- | vendor/fmt/format-inl.h | 122 | ||||
| -rw-r--r-- | vendor/fmt/format.cc | 17 | ||||
| -rw-r--r-- | vendor/fmt/format.h | 803 | ||||
| -rw-r--r-- | vendor/lyra/lyra.hpp | 2271 | ||||
| -rw-r--r-- | vendor/utfcpp/core.h | 56 | ||||
| -rw-r--r-- | vendor/utfcpp/unchecked.h | 23 |
10 files changed, 2345 insertions, 1401 deletions
diff --git a/vendor/README.md b/vendor/README.md index d21d0a4c4..abaea1eee 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -18,7 +18,7 @@ This directory contains vendored dependencies that are shipped with InspIRCd to **License** — MIT License -**Version** — 11.2.0 +**Version** — 12.1.0 **Website** — [https://github.com/fmtlib/fmt/](https://github.com/fmtlib/fmt/) @@ -38,7 +38,7 @@ This directory contains vendored dependencies that are shipped with InspIRCd to **License** — Boost Software License -**Version** — 1.6.1 +**Version** — 1.7.0 **Website** — [https://github.com/bfgroup/Lyra](https://github.com/bfgroup/Lyra) @@ -68,6 +68,6 @@ This directory contains vendored dependencies that are shipped with InspIRCd to **License** — Boost Software License -**Version** — v4.0.6 +**Version** — v4.0.8 **Website** — [https://github.com/nemtrif/utfcpp](https://github.com/nemtrif/utfcpp) diff --git a/vendor/fmt/base.h b/vendor/fmt/base.h index 87b3fd7cb..0d5786778 100644 --- a/vendor/fmt/base.h +++ b/vendor/fmt/base.h @@ -21,7 +21,7 @@ #endif // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 110200 +#define FMT_VERSION 120100 // Detect compiler versions. #if defined(__clang__) && !defined(__ibmxl__) @@ -114,7 +114,9 @@ #endif // Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated. -#if !defined(__cpp_lib_is_constant_evaluated) +#ifdef FMT_USE_CONSTEVAL +// Use the provided definition. +#elif !defined(__cpp_lib_is_constant_evaluated) # define FMT_USE_CONSTEVAL 0 #elif FMT_CPLUSPLUS < 201709L # define FMT_USE_CONSTEVAL 0 @@ -201,14 +203,6 @@ # define FMT_NODISCARD #endif -#ifdef FMT_DEPRECATED -// Use the provided definition. -#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated) -# define FMT_DEPRECATED [[deprecated]] -#else -# define FMT_DEPRECATED /* deprecated */ -#endif - #if FMT_GCC_VERSION || FMT_CLANG_VERSION # define FMT_VISIBILITY(value) __attribute__((visibility(value))) #else @@ -242,6 +236,7 @@ FMT_PRAGMA_GCC(optimize("Og")) # define FMT_GCC_OPTIMIZED #endif FMT_PRAGMA_CLANG(diagnostic push) +FMT_PRAGMA_GCC(diagnostic push) #ifdef FMT_ALWAYS_INLINE // Use the provided definition. @@ -260,7 +255,7 @@ FMT_PRAGMA_CLANG(diagnostic push) #ifndef FMT_BEGIN_NAMESPACE # define FMT_BEGIN_NAMESPACE \ namespace fmt { \ - inline namespace v11 { + inline namespace v12 { # define FMT_END_NAMESPACE \ } \ } @@ -356,6 +351,9 @@ template <typename T> constexpr auto max_of(T a, T b) -> T { return a > b ? a : b; } +FMT_NORETURN FMT_API void assert_fail(const char* file, int line, + const char* message); + namespace detail { // Suppresses "unused variable" warnings with the method described in // https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. @@ -396,7 +394,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, # define FMT_ASSERT(condition, message) \ ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ ? (void)0 \ - : fmt::detail::assert_fail(__FILE__, __LINE__, (message))) + : ::fmt::assert_fail(__FILE__, __LINE__, (message))) #endif #ifdef FMT_USE_INT128 @@ -419,8 +417,12 @@ inline auto map(int128_opt) -> monostate { return {}; } inline auto map(uint128_opt) -> monostate { return {}; } #endif -#ifndef FMT_USE_BITINT -# define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1500) +#ifdef FMT_USE_BITINT +// Use the provided definition. +#elif FMT_CLANG_VERSION >= 1500 && !defined(__CUDACC__) +# define FMT_USE_BITINT 1 +#else +# define FMT_USE_BITINT 0 #endif #if FMT_USE_BITINT @@ -463,12 +465,13 @@ enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled }; static_assert(!FMT_UNICODE || use_utf8, "Unicode support requires compiling with /utf-8"); -template <typename T> constexpr const char* narrow(const T*) { return nullptr; } -constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; } +template <typename T> constexpr auto narrow(T*) -> char* { return nullptr; } +constexpr FMT_ALWAYS_INLINE auto narrow(const char* s) -> const char* { + return s; +} template <typename Char> -FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n) - -> int { +FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, size_t n) -> int { if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n); for (; n != 0; ++s1, ++s2, --n) { if (*s1 < *s2) return -1; @@ -540,7 +543,7 @@ template <typename Char> class basic_string_view { FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) { #if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION if (std::is_same<Char, char>::value && !detail::is_constant_evaluated()) { - size_ = __builtin_strlen(detail::narrow(s)); // strlen is not costexpr. + size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr. return; } #endif @@ -616,19 +619,6 @@ template <typename Char> class basic_string_view { using string_view = basic_string_view<char>; -// DEPRECATED! Will be merged with is_char and moved to detail. -template <typename T> struct is_xchar : std::false_type {}; -template <> struct is_xchar<wchar_t> : std::true_type {}; -template <> struct is_xchar<char16_t> : std::true_type {}; -template <> struct is_xchar<char32_t> : std::true_type {}; -#ifdef __cpp_char8_t -template <> struct is_xchar<char8_t> : std::true_type {}; -#endif - -// Specifies if `T` is a character (code unit) type. -template <typename T> struct is_char : is_xchar<T> {}; -template <> struct is_char<char> : std::true_type {}; - template <typename T> class basic_appender; using appender = basic_appender<char>; @@ -781,7 +771,7 @@ class basic_specs { (static_cast<unsigned>(p) << precision_shift); } - constexpr bool dynamic() const { + constexpr auto dynamic() const -> bool { return (data_ & (width_mask | precision_mask)) != 0; } @@ -921,14 +911,50 @@ template <typename Char = char> class parse_context { FMT_CONSTEXPR void check_dynamic_spec(int arg_id); }; +#ifndef FMT_USE_LOCALE +# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1) +#endif + +// A type-erased reference to std::locale to avoid the heavy <locale> include. +class locale_ref { +#if FMT_USE_LOCALE + private: + const void* locale_; // A type-erased pointer to std::locale. + + public: + constexpr locale_ref() : locale_(nullptr) {} + + template <typename Locale, FMT_ENABLE_IF(sizeof(Locale::collate) != 0)> + locale_ref(const Locale& loc) : locale_(&loc) { + // Check if std::isalpha is found via ADL to reduce the chance of misuse. + isalpha('x', loc); + } + + inline explicit operator bool() const noexcept { return locale_ != nullptr; } +#endif // FMT_USE_LOCALE + + public: + template <typename Locale> auto get() const -> Locale; +}; + FMT_END_EXPORT namespace detail { +// Specifies if `T` is a code unit type. +template <typename T> struct is_code_unit : std::false_type {}; +template <> struct is_code_unit<char> : std::true_type {}; +template <> struct is_code_unit<wchar_t> : std::true_type {}; +template <> struct is_code_unit<char16_t> : std::true_type {}; +template <> struct is_code_unit<char32_t> : std::true_type {}; +#ifdef __cpp_char8_t +template <> struct is_code_unit<char8_t> : bool_constant<is_utf8_enabled> {}; +#endif + // Constructs fmt::basic_string_view<Char> from types implicitly convertible // to it, deducing Char. Explicitly convertible types such as the ones returned // from FMT_STRING are intentionally excluded. -template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)> +template <typename Char, FMT_ENABLE_IF(is_code_unit<Char>::value)> constexpr auto to_string_view(const Char* s) -> basic_string_view<Char> { return s; } @@ -1057,11 +1083,11 @@ template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int { return (B1 ? 1 : 0) + count<B2, Tail...>(); } -template <typename... Args> constexpr auto count_named_args() -> int { - return count<is_named_arg<Args>::value...>(); +template <typename... T> constexpr auto count_named_args() -> int { + return count<is_named_arg<T>::value...>(); } -template <typename... Args> constexpr auto count_static_named_args() -> int { - return count<is_static_named_arg<Args>::value...>(); +template <typename... T> constexpr auto count_static_named_args() -> int { + return count<is_static_named_arg<T>::value...>(); } template <typename Char> struct named_arg_info { @@ -1069,7 +1095,7 @@ template <typename Char> struct named_arg_info { int id; }; -// named_args is non-const to suppress a bogus -Wmaybe-uninitalized in gcc 13. +// named_args is non-const to suppress a bogus -Wmaybe-uninitialized in gcc 13. template <typename Char> FMT_CONSTEXPR void check_for_duplicate(named_arg_info<Char>* named_args, int named_arg_index, @@ -1173,7 +1199,7 @@ template <typename Char> struct type_mapper { static auto map(ubitint<N>) -> conditional_t<N <= 64, unsigned long long, void>; - template <typename T, FMT_ENABLE_IF(is_char<T>::value)> + template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)> static auto map(T) -> conditional_t< std::is_same<T, char>::value || std::is_same<T, Char>::value, Char, void>; @@ -1679,12 +1705,12 @@ template <typename... T> struct arg_pack {}; template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES> class format_string_checker { private: - type types_[max_of(1, NUM_ARGS)]; - named_arg_info<Char> named_args_[max_of(1, NUM_NAMED_ARGS)]; + type types_[max_of<size_t>(1, NUM_ARGS)]; + named_arg_info<Char> named_args_[max_of<size_t>(1, NUM_NAMED_ARGS)]; compile_parse_context<Char> context_; using parse_func = auto (*)(parse_context<Char>&) -> const Char*; - parse_func parse_funcs_[max_of(1, NUM_ARGS)]; + parse_func parse_funcs_[max_of<size_t>(1, NUM_ARGS)]; public: template <typename... T> @@ -1828,12 +1854,17 @@ template <typename T> class buffer { void append(const U* begin, const U* end) { while (begin != end) { + auto size = size_; + auto free_cap = capacity_ - size; auto count = to_unsigned(end - begin); - try_reserve(size_ + count); - auto free_cap = capacity_ - size_; - if (free_cap < count) count = free_cap; + if (free_cap < count) { + grow_(*this, size + count); + size = size_; + free_cap = capacity_ - size; + count = count < free_cap ? count : free_cap; + } // A loop is faster than memcpy on small sizes. - T* out = ptr_ + size_; + T* out = ptr_ + size; for (size_t i = 0; i < count; ++i) out[i] = begin[i]; size_ += count; begin += count; @@ -2033,6 +2064,17 @@ struct has_back_insert_iterator_container_append< .append(std::declval<InputIt>(), std::declval<InputIt>()))>> : std::true_type {}; +template <typename OutputIt, typename InputIt, typename = void> +struct has_back_insert_iterator_container_insert_at_end : std::false_type {}; + +template <typename OutputIt, typename InputIt> +struct has_back_insert_iterator_container_insert_at_end< + OutputIt, InputIt, + void_t<decltype(get_container(std::declval<OutputIt>()) + .insert(get_container(std::declval<OutputIt>()).end(), + std::declval<InputIt>(), + std::declval<InputIt>()))>> : std::true_type {}; + // An optimized version of std::copy with the output value type (T). template <typename T, typename InputIt, typename OutputIt, FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&& @@ -2047,6 +2089,8 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) template <typename T, typename InputIt, typename OutputIt, FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value && !has_back_insert_iterator_container_append< + OutputIt, InputIt>::value && + has_back_insert_iterator_container_insert_at_end< OutputIt, InputIt>::value)> FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt { @@ -2056,7 +2100,11 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) } template <typename T, typename InputIt, typename OutputIt, - FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>::value)> + FMT_ENABLE_IF(!(is_back_insert_iterator<OutputIt>::value && + (has_back_insert_iterator_container_append< + OutputIt, InputIt>::value || + has_back_insert_iterator_container_insert_at_end< + OutputIt, InputIt>::value)))> FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt { while (begin != end) *out++ = static_cast<T>(*begin++); return out; @@ -2176,7 +2224,7 @@ template <typename Context> class value { static_assert(N <= 64, "unsupported _BitInt"); } - template <typename T, FMT_ENABLE_IF(is_char<T>::value)> + template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)> constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) { static_assert( std::is_same<T, char>::value || std::is_same<T, char_type>::value, @@ -2252,7 +2300,7 @@ template <typename Context> class value { custom.value = const_cast<value_type*>(&x); #endif } - custom.format = format_custom<value_type, formatter<value_type, char_type>>; + custom.format = format_custom<value_type>; } template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())> @@ -2263,10 +2311,10 @@ template <typename Context> class value { } // Formats an argument of a custom type, such as a user-defined class. - template <typename T, typename Formatter> + template <typename T> static void format_custom(void* arg, parse_context<char_type>& parse_ctx, Context& ctx) { - auto f = Formatter(); + auto f = formatter<T, char_type>(); parse_ctx.advance_to(f.parse(parse_ctx)); using qualified_type = conditional_t<has_formatter<const T, char_type>(), const T, T>; @@ -2293,35 +2341,14 @@ struct is_output_iterator< enable_if_t<std::is_assignable<decltype(*std::declval<decay_t<It>&>()++), T>::value>> : std::true_type {}; -#ifndef FMT_USE_LOCALE -# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1) -#endif - -// A type-erased reference to an std::locale to avoid a heavy <locale> include. -class locale_ref { -#if FMT_USE_LOCALE - private: - const void* locale_; // A type-erased pointer to std::locale. - - public: - constexpr locale_ref() : locale_(nullptr) {} - template <typename Locale> locale_ref(const Locale& loc); - - inline explicit operator bool() const noexcept { return locale_ != nullptr; } -#endif // FMT_USE_LOCALE - - public: - template <typename Locale> auto get() const -> Locale; -}; - template <typename> constexpr auto encode_types() -> unsigned long long { return 0; } -template <typename Context, typename Arg, typename... Args> +template <typename Context, typename First, typename... T> constexpr auto encode_types() -> unsigned long long { - return static_cast<unsigned>(stored_type_constant<Arg, Context>::value) | - (encode_types<Context, Args...>() << packed_arg_bits); + return static_cast<unsigned>(stored_type_constant<First, Context>::value) | + (encode_types<Context, T...>() << packed_arg_bits); } template <typename Context, typename... T, size_t NUM_ARGS = sizeof...(T)> @@ -2338,8 +2365,9 @@ template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC> struct named_arg_store { // args_[0].named_args points to named_args to avoid bloating format_args. - arg_t<Context, NUM_ARGS> args[1 + NUM_ARGS]; - named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS]; + arg_t<Context, NUM_ARGS> args[1u + NUM_ARGS]; + named_arg_info<typename Context::char_type> + named_args[static_cast<size_t>(NUM_NAMED_ARGS)]; template <typename... T> FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values) @@ -2358,8 +2386,8 @@ struct named_arg_store { } named_arg_store(const named_arg_store& rhs) = delete; - named_arg_store& operator=(const named_arg_store& rhs) = delete; - named_arg_store& operator=(named_arg_store&& rhs) = delete; + auto operator=(const named_arg_store& rhs) -> named_arg_store& = delete; + auto operator=(named_arg_store&& rhs) -> named_arg_store& = delete; operator const arg_t<Context, NUM_ARGS>*() const { return args + 1; } }; @@ -2372,7 +2400,7 @@ struct format_arg_store { // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. using type = conditional_t<NUM_NAMED_ARGS == 0, - arg_t<Context, NUM_ARGS>[max_of(1, NUM_ARGS)], + arg_t<Context, NUM_ARGS>[max_of<size_t>(1, NUM_ARGS)], named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>; type args; }; @@ -2656,22 +2684,17 @@ class context { private: appender out_; format_args args_; - FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_; + FMT_NO_UNIQUE_ADDRESS locale_ref loc_; public: - /// The character type for the output. - using char_type = char; - + using char_type = char; ///< The character type for the output. using iterator = appender; using format_arg = basic_format_arg<context>; - using parse_context_type FMT_DEPRECATED = parse_context<>; - template <typename T> using formatter_type FMT_DEPRECATED = formatter<T>; enum { builtin_types = FMT_BUILTIN_TYPES }; /// Constructs a `context` object. References to the arguments are stored /// in the object so make sure they have appropriate lifetimes. - FMT_CONSTEXPR context(iterator out, format_args args, - detail::locale_ref loc = {}) + FMT_CONSTEXPR context(iterator out, format_args args, locale_ref loc = {}) : out_(out), args_(args), loc_(loc) {} context(context&&) = default; context(const context&) = delete; @@ -2692,7 +2715,7 @@ class context { // Advances the begin iterator to `it`. FMT_CONSTEXPR void advance_to(iterator) {} - FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; } + FMT_CONSTEXPR auto locale() const -> locale_ref { return loc_; } }; template <typename Char = char> struct runtime_format_string { @@ -2779,9 +2802,6 @@ template <typename T, typename Char = char> concept formattable = is_formattable<remove_reference_t<T>, Char>::value; #endif -template <typename T, typename Char> -using has_formatter FMT_DEPRECATED = std::is_constructible<formatter<T, Char>>; - // A formatter specialization for natively supported types. template <typename T, typename Char> struct formatter<T, Char, @@ -2978,9 +2998,10 @@ FMT_INLINE void println(format_string<T...> fmt, T&&... args) { return fmt::println(stdout, fmt, static_cast<T&&>(args)...); } -FMT_END_EXPORT +FMT_PRAGMA_GCC(diagnostic pop) FMT_PRAGMA_CLANG(diagnostic pop) FMT_PRAGMA_GCC(pop_options) +FMT_END_EXPORT FMT_END_NAMESPACE #ifdef FMT_HEADER_ONLY diff --git a/vendor/fmt/color.h b/vendor/fmt/color.h index 638f15b43..2cbc53ca3 100644 --- a/vendor/fmt/color.h +++ b/vendor/fmt/color.h @@ -375,19 +375,17 @@ template <typename Char> struct ansi_color_escape { // 10 more. if (is_background) value += 10u; - size_t index = 0; - buffer[index++] = static_cast<Char>('\x1b'); - buffer[index++] = static_cast<Char>('['); + buffer[size++] = static_cast<Char>('\x1b'); + buffer[size++] = static_cast<Char>('['); if (value >= 100u) { - buffer[index++] = static_cast<Char>('1'); + buffer[size++] = static_cast<Char>('1'); value %= 100u; } - buffer[index++] = static_cast<Char>('0' + value / 10u); - buffer[index++] = static_cast<Char>('0' + value % 10u); + buffer[size++] = static_cast<Char>('0' + value / 10u); + buffer[size++] = static_cast<Char>('0' + value % 10u); - buffer[index++] = static_cast<Char>('m'); - buffer[index++] = static_cast<Char>('\0'); + buffer[size++] = static_cast<Char>('m'); return; } @@ -398,7 +396,7 @@ template <typename Char> struct ansi_color_escape { to_esc(color.r, buffer + 7, ';'); to_esc(color.g, buffer + 11, ';'); to_esc(color.b, buffer + 15, 'm'); - buffer[19] = static_cast<Char>(0); + size = 19; } FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept { uint8_t em_codes[num_emphases] = {}; @@ -411,26 +409,28 @@ template <typename Char> struct ansi_color_escape { if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; - size_t index = 0; + buffer[size++] = static_cast<Char>('\x1b'); + buffer[size++] = static_cast<Char>('['); + for (size_t i = 0; i < num_emphases; ++i) { if (!em_codes[i]) continue; - buffer[index++] = static_cast<Char>('\x1b'); - buffer[index++] = static_cast<Char>('['); - buffer[index++] = static_cast<Char>('0' + em_codes[i]); - buffer[index++] = static_cast<Char>('m'); + buffer[size++] = static_cast<Char>('0' + em_codes[i]); + buffer[size++] = static_cast<Char>(';'); } - buffer[index++] = static_cast<Char>(0); + + buffer[size - 1] = static_cast<Char>('m'); } FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; } - FMT_CONSTEXPR20 auto end() const noexcept -> const Char* { - return buffer + basic_string_view<Char>(buffer).size(); + FMT_CONSTEXPR auto end() const noexcept -> const Char* { + return buffer + size; } private: static constexpr size_t num_emphases = 8; - Char buffer[7u + 3u * num_emphases + 1u]; + Char buffer[7u + 4u * num_emphases] = {}; + size_t size = 0; static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, char delimiter) noexcept { diff --git a/vendor/fmt/compile.h b/vendor/fmt/compile.h index 08d9427ff..64eb7a20d 100644 --- a/vendor/fmt/compile.h +++ b/vendor/fmt/compile.h @@ -15,15 +15,14 @@ #include "format.h" FMT_BEGIN_NAMESPACE +FMT_BEGIN_EXPORT // A compile-time string which is compiled into fast formatting code. -FMT_EXPORT class compiled_string {}; +class compiled_string {}; template <typename S> struct is_compiled_string : std::is_base_of<compiled_string, S> {}; -namespace detail { - /** * Converts a string literal `s` into a format string that will be parsed at * compile time and converted into efficient formatting code. Requires C++17 @@ -41,18 +40,42 @@ namespace detail { # define FMT_COMPILE(s) FMT_STRING(s) #endif +/** + * Converts a string literal into a format string that will be parsed at + * compile time and converted into efficient formatting code. Requires support + * for class types in constant template parameters (a C++20 feature). + * + * **Example**: + * + * // Converts 42 into std::string using the most efficient method and no + * // runtime format string processing. + * using namespace fmt::literals; + * std::string s = fmt::format("{}"_cf, 42); + */ +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +inline namespace literals { +template <detail::fixed_string Str> constexpr auto operator""_cf() { + return FMT_COMPILE(Str.data); +} +} // namespace literals +#endif + +FMT_END_EXPORT + +namespace detail { + template <typename T, typename... Tail> -auto first(const T& value, const Tail&...) -> const T& { +constexpr auto first(const T& value, const Tail&...) -> const T& { return value; } #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) -template <typename... Args> struct type_list {}; +template <typename... T> struct type_list {}; // Returns a reference to the argument at index N from [first, rest...]. template <int N, typename T, typename... Args> -constexpr const auto& get([[maybe_unused]] const T& first, - [[maybe_unused]] const Args&... rest) { +constexpr auto get([[maybe_unused]] const T& first, + [[maybe_unused]] const Args&... rest) -> const auto& { static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); if constexpr (N == 0) return first; @@ -84,8 +107,8 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int { } template <typename Char, typename... Args> -constexpr int get_arg_index_by_name(basic_string_view<Char> name, - type_list<Args...>) { +constexpr auto get_arg_index_by_name(basic_string_view<Char> name, + type_list<Args...>) -> int { return get_arg_index_by_name<Args...>(name); } @@ -105,8 +128,8 @@ template <typename Char> struct text { basic_string_view<Char> data; using char_type = Char; - template <typename OutputIt, typename... Args> - constexpr OutputIt format(OutputIt out, const Args&...) const { + template <typename OutputIt, typename... T> + constexpr auto format(OutputIt out, const T&...) const -> OutputIt { return write<Char>(out, data); } }; @@ -115,8 +138,8 @@ template <typename Char> struct is_compiled_format<text<Char>> : std::true_type {}; template <typename Char> -constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos, - size_t size) { +constexpr auto make_text(basic_string_view<Char> s, size_t pos, size_t size) + -> text<Char> { return {{&s[pos], size}}; } @@ -124,8 +147,8 @@ template <typename Char> struct code_unit { Char value; using char_type = Char; - template <typename OutputIt, typename... Args> - constexpr OutputIt format(OutputIt out, const Args&...) const { + template <typename OutputIt, typename... T> + constexpr auto format(OutputIt out, const T&...) const -> OutputIt { *out++ = value; return out; } @@ -133,7 +156,7 @@ template <typename Char> struct code_unit { // This ensures that the argument type is convertible to `const T&`. template <typename T, int N, typename... Args> -constexpr const T& get_arg_checked(const Args&... args) { +constexpr auto get_arg_checked(const Args&... args) -> const T& { const auto& arg = detail::get<N>(args...); if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) { return arg.value; @@ -146,13 +169,13 @@ template <typename Char> struct is_compiled_format<code_unit<Char>> : std::true_type {}; // A replacement field that refers to argument N. -template <typename Char, typename T, int N> struct field { +template <typename Char, typename V, int N> struct field { using char_type = Char; - template <typename OutputIt, typename... Args> - constexpr OutputIt format(OutputIt out, const Args&... args) const { - const T& arg = get_arg_checked<T, N>(args...); - if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) { + template <typename OutputIt, typename... T> + constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { + const V& arg = get_arg_checked<V, N>(args...); + if constexpr (std::is_convertible<V, basic_string_view<Char>>::value) { auto s = basic_string_view<Char>(arg); return copy<Char>(s.begin(), s.end(), out); } else { @@ -170,10 +193,10 @@ template <typename Char> struct runtime_named_field { basic_string_view<Char> name; template <typename OutputIt, typename T> - constexpr static bool try_format_argument( + constexpr static auto try_format_argument( OutputIt& out, // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9 - [[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) { + [[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) -> bool { if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) { if (arg_name == arg.name) { out = write<Char>(out, arg.value); @@ -183,8 +206,8 @@ template <typename Char> struct runtime_named_field { return false; } - template <typename OutputIt, typename... Args> - constexpr OutputIt format(OutputIt out, const Args&... args) const { + template <typename OutputIt, typename... T> + constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { bool found = (try_format_argument(out, name, args) || ...); if (!found) { FMT_THROW(format_error("argument with specified name is not found")); @@ -197,17 +220,17 @@ template <typename Char> struct is_compiled_format<runtime_named_field<Char>> : std::true_type {}; // A replacement field that refers to argument N and has format specifiers. -template <typename Char, typename T, int N> struct spec_field { +template <typename Char, typename V, int N> struct spec_field { using char_type = Char; - formatter<T, Char> fmt; + formatter<V, Char> fmt; - template <typename OutputIt, typename... Args> - constexpr FMT_INLINE OutputIt format(OutputIt out, - const Args&... args) const { + template <typename OutputIt, typename... T> + constexpr FMT_INLINE auto format(OutputIt out, const T&... args) const + -> OutputIt { const auto& vargs = fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...); basic_format_context<OutputIt, Char> ctx(out, vargs); - return fmt.format(get_arg_checked<T, N>(args...), ctx); + return fmt.format(get_arg_checked<V, N>(args...), ctx); } }; @@ -219,8 +242,8 @@ template <typename L, typename R> struct concat { R rhs; using char_type = typename L::char_type; - template <typename OutputIt, typename... Args> - constexpr OutputIt format(OutputIt out, const Args&... args) const { + template <typename OutputIt, typename... T> + constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { out = lhs.format(out, args...); return rhs.format(out, args...); } @@ -230,14 +253,14 @@ template <typename L, typename R> struct is_compiled_format<concat<L, R>> : std::true_type {}; template <typename L, typename R> -constexpr concat<L, R> make_concat(L lhs, R rhs) { +constexpr auto make_concat(L lhs, R rhs) -> concat<L, R> { return {lhs, rhs}; } struct unknown_format {}; template <typename Char> -constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) { +constexpr auto parse_text(basic_string_view<Char> str, size_t pos) -> size_t { for (size_t size = str.size(); pos != size; ++pos) { if (str[pos] == '{' || str[pos] == '}') break; } @@ -270,8 +293,8 @@ template <typename T, typename Char> struct parse_specs_result { enum { manual_indexing_id = -1 }; template <typename T, typename Char> -constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, - size_t pos, int next_arg_id) { +constexpr auto parse_specs(basic_string_view<Char> str, size_t pos, + int next_arg_id) -> parse_specs_result<T, Char> { str.remove_prefix(pos); auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id); @@ -285,16 +308,16 @@ template <typename Char> struct arg_id_handler { arg_id_kind kind; arg_ref<Char> arg_id; - constexpr int on_auto() { + constexpr auto on_auto() -> int { FMT_ASSERT(false, "handler cannot be used with automatic indexing"); return 0; } - constexpr int on_index(int id) { + constexpr auto on_index(int id) -> int { kind = arg_id_kind::index; arg_id = arg_ref<Char>(id); return 0; } - constexpr int on_name(basic_string_view<Char> id) { + constexpr auto on_name(basic_string_view<Char> id) -> int { kind = arg_id_kind::name; arg_id = arg_ref<Char>(id); return 0; @@ -433,27 +456,28 @@ FMT_BEGIN_EXPORT #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) -template <typename CompiledFormat, typename... Args, +template <typename CompiledFormat, typename... T, typename Char = typename CompiledFormat::char_type, FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> -FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf, - const Args&... args) { +FMT_INLINE FMT_CONSTEXPR_STRING auto format(const CompiledFormat& cf, + const T&... args) + -> std::basic_string<Char> { auto s = std::basic_string<Char>(); cf.format(std::back_inserter(s), args...); return s; } -template <typename OutputIt, typename CompiledFormat, typename... Args, +template <typename OutputIt, typename CompiledFormat, typename... T, FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> -constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, - const Args&... args) { +constexpr FMT_INLINE auto format_to(OutputIt out, const CompiledFormat& cf, + const T&... args) -> OutputIt { return cf.format(out, args...); } -template <typename S, typename... Args, +template <typename S, typename... T, FMT_ENABLE_IF(is_compiled_string<S>::value)> -FMT_INLINE std::basic_string<typename S::char_type> format(const S&, - Args&&... args) { +FMT_INLINE FMT_CONSTEXPR_STRING auto format(const S&, T&&... args) + -> std::basic_string<typename S::char_type> { if constexpr (std::is_same<typename S::char_type, char>::value) { constexpr auto str = basic_string_view<typename S::char_type>(S()); if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { @@ -466,72 +490,97 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&, } } } - constexpr auto compiled = detail::compile<Args...>(S()); + constexpr auto compiled = detail::compile<T...>(S()); if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, detail::unknown_format>()) { return fmt::format( static_cast<basic_string_view<typename S::char_type>>(S()), - std::forward<Args>(args)...); + std::forward<T>(args)...); } else { - return fmt::format(compiled, std::forward<Args>(args)...); + return fmt::format(compiled, std::forward<T>(args)...); } } -template <typename OutputIt, typename S, typename... Args, +template <typename OutputIt, typename S, typename... T, FMT_ENABLE_IF(is_compiled_string<S>::value)> -FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { - constexpr auto compiled = detail::compile<Args...>(S()); +FMT_CONSTEXPR auto format_to(OutputIt out, const S&, T&&... args) -> OutputIt { + constexpr auto compiled = detail::compile<T...>(S()); if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, detail::unknown_format>()) { return fmt::format_to( out, static_cast<basic_string_view<typename S::char_type>>(S()), - std::forward<Args>(args)...); + std::forward<T>(args)...); } else { - return fmt::format_to(out, compiled, std::forward<Args>(args)...); + return fmt::format_to(out, compiled, std::forward<T>(args)...); } } #endif -template <typename OutputIt, typename S, typename... Args, +template <typename OutputIt, typename S, typename... T, FMT_ENABLE_IF(is_compiled_string<S>::value)> -auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args) +auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args) -> format_to_n_result<OutputIt> { using traits = detail::fixed_buffer_traits; auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n); - fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...); + fmt::format_to(std::back_inserter(buf), fmt, std::forward<T>(args)...); return {buf.out(), buf.count()}; } -template <typename S, typename... Args, +template <typename S, typename... T, FMT_ENABLE_IF(is_compiled_string<S>::value)> -FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args) - -> size_t { +FMT_CONSTEXPR20 auto formatted_size(const S& fmt, T&&... args) -> size_t { auto buf = detail::counting_buffer<>(); - fmt::format_to(appender(buf), fmt, args...); + fmt::format_to(appender(buf), fmt, std::forward<T>(args)...); return buf.count(); } -template <typename S, typename... Args, +template <typename S, typename... T, FMT_ENABLE_IF(is_compiled_string<S>::value)> -void print(std::FILE* f, const S& fmt, const Args&... args) { +void print(std::FILE* f, const S& fmt, T&&... args) { auto buf = memory_buffer(); - fmt::format_to(appender(buf), fmt, args...); + fmt::format_to(appender(buf), fmt, std::forward<T>(args)...); detail::print(f, {buf.data(), buf.size()}); } -template <typename S, typename... Args, +template <typename S, typename... T, FMT_ENABLE_IF(is_compiled_string<S>::value)> -void print(const S& fmt, const Args&... args) { - print(stdout, fmt, args...); +void print(const S& fmt, T&&... args) { + print(stdout, fmt, std::forward<T>(args)...); } -#if FMT_USE_NONTYPE_TEMPLATE_ARGS -inline namespace literals { -template <detail::fixed_string Str> constexpr auto operator""_cf() { - return FMT_COMPILE(Str.data); -} -} // namespace literals -#endif +template <size_t N> class static_format_result { + private: + char data[N]; + + public: + template <typename S, typename... T, + FMT_ENABLE_IF(is_compiled_string<S>::value)> + explicit FMT_CONSTEXPR static_format_result(const S& fmt, T&&... args) { + *fmt::format_to(data, fmt, std::forward<T>(args)...) = '\0'; + } + + auto str() const -> fmt::string_view { return {data, N - 1}; } + auto c_str() const -> const char* { return data; } +}; + +/** + * Formats arguments according to the format string `fmt_str` and produces + * a string of the exact required size at compile time. Both the format string + * and the arguments must be compile-time expressions. + * + * The resulting string can be accessed as a C string via `c_str()` or as + * a `fmt::string_view` via `str()`. + * + * **Example**: + * + * // Produces the static string "42" at compile time. + * static constexpr auto result = FMT_STATIC_FORMAT("{}", 42); + * const char* s = result.c_str(); + */ +#define FMT_STATIC_FORMAT(fmt_str, ...) \ + fmt::static_format_result< \ + fmt::formatted_size(FMT_COMPILE(fmt_str), __VA_ARGS__) + 1>( \ + FMT_COMPILE(fmt_str), __VA_ARGS__) FMT_END_EXPORT FMT_END_NAMESPACE diff --git a/vendor/fmt/format-inl.h b/vendor/fmt/format-inl.h index a1e016611..945cb912a 100644 --- a/vendor/fmt/format-inl.h +++ b/vendor/fmt/format-inl.h @@ -22,7 +22,7 @@ #include "format.h" -#if FMT_USE_LOCALE +#if FMT_USE_LOCALE && !defined(FMT_MODULE) # include <locale> #endif @@ -31,14 +31,44 @@ #endif FMT_BEGIN_NAMESPACE -namespace detail { +#ifndef FMT_CUSTOM_ASSERT_FAIL FMT_FUNC void assert_fail(const char* file, int line, const char* message) { // Use unchecked std::fprintf to avoid triggering another assertion when // writing to stderr fails. - fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); + std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); abort(); } +#endif + +#if FMT_USE_LOCALE +namespace detail { +using std::locale; +using std::numpunct; +using std::use_facet; +} // namespace detail +#else +namespace detail { +struct locale {}; +template <typename Char> struct numpunct { + auto grouping() const -> std::string { return "\03"; } + auto thousands_sep() const -> Char { return ','; } + auto decimal_point() const -> Char { return '.'; } +}; +template <typename Facet> Facet use_facet(locale) { return {}; } +} // namespace detail +#endif // FMT_USE_LOCALE + +template <typename Locale> auto locale_ref::get() const -> Locale { + using namespace detail; + static_assert(std::is_same<Locale, locale>::value, ""); +#if FMT_USE_LOCALE + if (locale_) return *static_cast<const locale*>(locale_); +#endif + return locale(); +} + +namespace detail { FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code, string_view message) noexcept { @@ -79,33 +109,6 @@ inline void fwrite_all(const void* ptr, size_t count, FILE* stream) { FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); } -#if FMT_USE_LOCALE -using std::locale; -using std::numpunct; -using std::use_facet; - -template <typename Locale> -locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { - static_assert(std::is_same<Locale, locale>::value, ""); -} -#else -struct locale {}; -template <typename Char> struct numpunct { - auto grouping() const -> std::string { return "\03"; } - auto thousands_sep() const -> Char { return ','; } - auto decimal_point() const -> Char { return '.'; } -}; -template <typename Facet> Facet use_facet(locale) { return {}; } -#endif // FMT_USE_LOCALE - -template <typename Locale> auto locale_ref::get() const -> Locale { - static_assert(std::is_same<Locale, locale>::value, ""); -#if FMT_USE_LOCALE - if (locale_) return *static_cast<const locale*>(locale_); -#endif - return locale(); -} - template <typename Char> FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> { auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>()); @@ -133,14 +136,13 @@ FMT_FUNC auto write_loc(appender out, loc_value value, } // namespace detail FMT_FUNC void report_error(const char* message) { -#if FMT_USE_EXCEPTIONS - // Use FMT_THROW instead of throw to avoid bogus unreachable code warnings - // from MSVC. - FMT_THROW(format_error(message)); -#else - fputs(message, stderr); - abort(); +#if FMT_MSC_VERSION || defined(__NVCC__) + // Silence unreachable code warnings in MSVC and NVCC because these + // are nearly impossible to fix in a generic code. + volatile bool b = true; + if (!b) return; #endif + FMT_THROW(format_error(message)); } template <typename Locale> typename Locale::id format_facet<Locale>::id; @@ -174,11 +176,11 @@ inline auto operator==(basic_fp<F> x, basic_fp<F> y) -> bool { } // Compilers should be able to optimize this into the ror instruction. -FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t { +FMT_INLINE auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t { r &= 31; return (n >> r) | (n << (32 - r)); } -FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t { +FMT_INLINE auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t { r &= 63; return (n >> r) | (n << (64 - r)); } @@ -275,7 +277,7 @@ template <> struct cache_accessor<float> { static auto get_cached_power(int k) noexcept -> uint64_t { FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k, "k is out of range"); - static constexpr const uint64_t pow10_significands[] = { + static constexpr uint64_t pow10_significands[] = { 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, @@ -370,7 +372,7 @@ template <> struct cache_accessor<double> { FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k, "k is out of range"); - static constexpr const uint128_fallback pow10_significands[] = { + static constexpr uint128_fallback pow10_significands[] = { #if FMT_USE_FULL_CACHE_DRAGONBOX {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0x9faacf3df73609b1, 0x77b191618c54e9ad}, @@ -1037,7 +1039,7 @@ template <> struct cache_accessor<double> { #if FMT_USE_FULL_CACHE_DRAGONBOX return pow10_significands[k - float_info<double>::min_k]; #else - static constexpr const uint64_t powers_of_5_64[] = { + static constexpr uint64_t powers_of_5_64[] = { 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, @@ -1149,8 +1151,8 @@ auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool { exponent <= case_shorter_interval_left_endpoint_upper_threshold; } -// Remove trailing zeros from n and return the number of zeros removed (float) -FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept { +// Remove trailing zeros from n and return the number of zeros removed (float). +FMT_INLINE auto remove_trailing_zeros(uint32_t& n, int s = 0) noexcept -> int { FMT_ASSERT(n != 0, ""); // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1. constexpr uint32_t mod_inv_5 = 0xcccccccd; @@ -1170,22 +1172,19 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept { return s; } -// Removes trailing zeros and returns the number of zeros removed (double) -FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { +// Removes trailing zeros and returns the number of zeros removed (double). +FMT_INLINE auto remove_trailing_zeros(uint64_t& n) noexcept -> int { FMT_ASSERT(n != 0, ""); - // This magic number is ceil(2^90 / 10^8). - constexpr uint64_t magic_number = 12379400392853802749ull; - auto nm = umul128(n, magic_number); - // Is n is divisible by 10^8? - if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) { + constexpr uint32_t ten_pow_8 = 100000000u; + if ((n % ten_pow_8) == 0) { // If yes, work with the quotient... - auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64)); + auto n32 = static_cast<uint32_t>(n / ten_pow_8); // ... and use the 32 bit variant of the function - int s = remove_trailing_zeros(n32, 8); + int num_zeros = remove_trailing_zeros(n32, 8); n = n32; - return s; + return num_zeros; } // If n is not divisible by 10^8, work with n itself. @@ -1210,7 +1209,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { // The main algorithm for shorter interval case template <typename T> -FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept { +FMT_INLINE auto shorter_interval_case(int exponent) noexcept -> decimal_fp<T> { decimal_fp<T> ret_value; // Compute k and beta const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); @@ -1454,8 +1453,8 @@ FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args, auto out = appender(buf); if (fmt.size() == 2 && equal2(fmt.data(), "{}")) return args.get(0).visit(default_arg_formatter<char>{out}); - parse_format_string( - fmt, format_handler<char>{parse_context<char>(fmt), {out, args, loc}}); + parse_format_string(fmt, + format_handler<>{parse_context<>(fmt), {out, args, loc}}); } template <typename T> struct span { @@ -1546,10 +1545,11 @@ template <typename F> class glibc_file : public file_base<F> { void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; } - bool needs_flush() const { + auto needs_flush() const -> bool { if ((this->file_->_flags & line_buffered) == 0) return false; char* end = this->file_->_IO_write_end; - return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end)); + auto size = max_of<ptrdiff_t>(this->file_->_IO_write_ptr - end, 0); + return memchr(end, '\n', static_cast<size_t>(size)); } void flush() { fflush_unlocked(this->file_); } @@ -1573,7 +1573,7 @@ template <typename F> class apple_file : public file_base<F> { void init_buffer() { if (this->file_->_p) return; // Force buffer initialization by placing and removing a char in a buffer. - putc_unlocked(0, this->file_); + if (!FMT_CLANG_ANALYZER) putc_unlocked(0, this->file_); --this->file_->_p; ++this->file_->_w; } @@ -1594,7 +1594,7 @@ template <typename F> class apple_file : public file_base<F> { this->file_->_w -= size; } - bool needs_flush() const { + auto needs_flush() const -> bool { if ((this->file_->_flags & line_buffered) == 0) return false; return memchr(this->file_->_p + this->file_->_w, '\n', to_unsigned(-this->file_->_w)); diff --git a/vendor/fmt/format.cc b/vendor/fmt/format.cc index 3ccd80684..526082e34 100644 --- a/vendor/fmt/format.cc +++ b/vendor/fmt/format.cc @@ -8,6 +8,12 @@ #include "fmt/format-inl.h" FMT_BEGIN_NAMESPACE + +#if FMT_USE_LOCALE +template FMT_API locale_ref::locale_ref(const std::locale& loc); // DEPRECATED! +template FMT_API auto locale_ref::get<std::locale>() const -> std::locale; +#endif + namespace detail { template FMT_API auto dragonbox::to_decimal(float x) noexcept @@ -15,12 +21,6 @@ template FMT_API auto dragonbox::to_decimal(float x) noexcept template FMT_API auto dragonbox::to_decimal(double x) noexcept -> dragonbox::decimal_fp<double>; -#if FMT_USE_LOCALE -// DEPRECATED! locale_ref in the detail namespace -template FMT_API locale_ref::locale_ref(const std::locale& loc); -template FMT_API auto locale_ref::get<std::locale>() const -> std::locale; -#endif - // Explicit instantiations for char. template FMT_API auto thousands_sep_impl(locale_ref) @@ -30,16 +30,13 @@ template FMT_API auto decimal_point_impl(locale_ref) -> char; // DEPRECATED! template FMT_API void buffer<char>::append(const char*, const char*); -// DEPRECATED! -template FMT_API void vformat_to(buffer<char>&, string_view, - typename vformat_args<>::type, locale_ref); - // Explicit instantiations for wchar_t. template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result<wchar_t>; template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; +// DEPRECATED! template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*); } // namespace detail diff --git a/vendor/fmt/format.h b/vendor/fmt/format.h index 50e571442..4a6530072 100644 --- a/vendor/fmt/format.h +++ b/vendor/fmt/format.h @@ -40,7 +40,15 @@ #include "base.h" +// libc++ supports string_view in pre-c++17. +#if FMT_HAS_INCLUDE(<string_view>) && \ + (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) +# define FMT_USE_STRING_VIEW +#endif + #ifndef FMT_MODULE +# include <stdlib.h> // malloc, free + # include <cmath> // std::signbit # include <cstddef> // std::byte # include <cstdint> // uint32_t @@ -60,11 +68,8 @@ # include <bit> // std::bit_cast # endif -// libc++ supports string_view in pre-c++17. -# if FMT_HAS_INCLUDE(<string_view>) && \ - (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) +# if defined(FMT_USE_STRING_VIEW) # include <string_view> -# define FMT_USE_STRING_VIEW # endif # if FMT_MSC_VERSION @@ -117,6 +122,41 @@ # define FMT_NOINLINE #endif +#ifdef FMT_DEPRECATED +// Use the provided definition. +#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated) +# define FMT_DEPRECATED [[deprecated]] +#else +# define FMT_DEPRECATED /* deprecated */ +#endif + +// Detect constexpr std::string. +#if !FMT_USE_CONSTEVAL +# define FMT_USE_CONSTEXPR_STRING 0 +#elif defined(__cpp_lib_constexpr_string) && \ + __cpp_lib_constexpr_string >= 201907L +# if FMT_CLANG_VERSION && FMT_GLIBCXX_RELEASE +// clang + libstdc++ are able to work only starting with gcc13.3 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113294 +# if FMT_GLIBCXX_RELEASE < 13 +# define FMT_USE_CONSTEXPR_STRING 0 +# elif FMT_GLIBCXX_RELEASE == 13 && __GLIBCXX__ < 20240521 +# define FMT_USE_CONSTEXPR_STRING 0 +# else +# define FMT_USE_CONSTEXPR_STRING 1 +# endif +# else +# define FMT_USE_CONSTEXPR_STRING 1 +# endif +#else +# define FMT_USE_CONSTEXPR_STRING 0 +#endif +#if FMT_USE_CONSTEXPR_STRING +# define FMT_CONSTEXPR_STRING constexpr +#else +# define FMT_CONSTEXPR_STRING +#endif + // GCC 4.9 doesn't support qualified names in specializations. namespace std { template <typename T> struct iterator_traits<fmt::basic_appender<T>> { @@ -129,28 +169,19 @@ template <typename T> struct iterator_traits<fmt::basic_appender<T>> { }; } // namespace std -#ifndef FMT_THROW -# if FMT_USE_EXCEPTIONS -# if FMT_MSC_VERSION || defined(__NVCC__) -FMT_BEGIN_NAMESPACE -namespace detail { -template <typename Exception> inline void do_throw(const Exception& x) { - // Silence unreachable code warnings in MSVC and NVCC because these - // are nearly impossible to fix in a generic code. - volatile bool b = true; - if (b) throw x; -} -} // namespace detail -FMT_END_NAMESPACE -# define FMT_THROW(x) detail::do_throw(x) -# else -# define FMT_THROW(x) throw x -# endif -# else -# define FMT_THROW(x) \ - ::fmt::detail::assert_fail(__FILE__, __LINE__, (x).what()) -# endif // FMT_USE_EXCEPTIONS -#endif // FMT_THROW +#ifdef FMT_THROW +// Use the provided definition. +#elif FMT_USE_EXCEPTIONS +# define FMT_THROW(x) throw x +#else +# define FMT_THROW(x) ::fmt::assert_fail(__FILE__, __LINE__, (x).what()) +#endif + +#ifdef __clang_analyzer__ +# define FMT_CLANG_ANALYZER 1 +#else +# define FMT_CLANG_ANALYZER 0 +#endif // Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of // integer formatter template instantiations to just one by only using the @@ -491,6 +522,11 @@ template <typename T, typename OutputIt> constexpr auto to_pointer(OutputIt, size_t) -> T* { return nullptr; } +template <typename T> FMT_CONSTEXPR auto to_pointer(T*& ptr, size_t n) -> T* { + T* begin = ptr; + ptr += n; + return begin; +} template <typename T> FMT_CONSTEXPR20 auto to_pointer(basic_appender<T> it, size_t n) -> T* { buffer<T>& buf = get_container(it); @@ -526,6 +562,8 @@ FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) template <typename T, typename Size> FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* { if (is_constant_evaluated()) return fill_n<T*, Size, T>(out, count, value); + static_assert(sizeof(T) == 1, + "sizeof(T) must be 1 to use char for initialization"); std::memset(out, value, to_unsigned(count)); return out + count; } @@ -555,10 +593,10 @@ FMT_CONSTEXPR FMT_NOINLINE auto copy_noinline(InputIt begin, InputIt end, */ FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) -> const char* { - constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; - constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; - constexpr const int shiftc[] = {0, 18, 12, 6, 0}; - constexpr const int shifte[] = {0, 6, 4, 2, 0}; + constexpr int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; + constexpr uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; + constexpr int shiftc[] = {0, 18, 12, 6, 0}; + constexpr int shifte[] = {0, 6, 4, 2, 0}; int len = "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4" [static_cast<unsigned char>(*s) >> 3]; @@ -629,21 +667,9 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { } while (buf_ptr < buf + num_chars_left); } -template <typename Char> -inline auto compute_width(basic_string_view<Char> s) -> size_t { - return s.size(); -} - -// Computes approximate display width of a UTF-8 string. -FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t { - size_t num_code_points = 0; - // It is not a lambda for compatibility with C++14. - struct count_code_points { - size_t* count; - FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool { - *count += to_unsigned( - 1 + - (cp >= 0x1100 && +FMT_CONSTEXPR inline auto display_width_of(uint32_t cp) noexcept -> size_t { + return to_unsigned( + 1 + (cp >= 0x1100 && (cp <= 0x115f || // Hangul Jamo init. consonants cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET @@ -661,32 +687,6 @@ FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t { (cp >= 0x1f300 && cp <= 0x1f64f) || // Supplemental Symbols and Pictographs: (cp >= 0x1f900 && cp <= 0x1f9ff)))); - return true; - } - }; - // We could avoid branches by using utf8_decode directly. - for_each_codepoint(s, count_code_points{&num_code_points}); - return num_code_points; -} - -template <typename Char> -inline auto code_point_index(basic_string_view<Char> s, size_t n) -> size_t { - return min_of(n, s.size()); -} - -// Calculates the index of the nth code point in a UTF-8 string. -inline auto code_point_index(string_view s, size_t n) -> size_t { - size_t result = s.size(); - const char* begin = s.begin(); - for_each_codepoint(s, [begin, &n, &result](uint32_t, string_view sv) { - if (n != 0) { - --n; - return true; - } - result = to_unsigned(sv.begin() - begin); - return false; - }); - return result; } template <typename T> struct is_integral : std::is_integral<T> {}; @@ -731,6 +731,9 @@ struct is_fast_float : bool_constant<std::numeric_limits<T>::is_iec559 && template <typename T> struct is_fast_float<T, false> : std::false_type {}; template <typename T> +using fast_float_t = conditional_t<sizeof(T) == sizeof(double), double, float>; + +template <typename T> using is_double_double = bool_constant<std::numeric_limits<T>::digits == 106>; #ifndef FMT_USE_FULL_CACHE_DRAGONBOX @@ -738,11 +741,12 @@ using is_double_double = bool_constant<std::numeric_limits<T>::digits == 106>; #endif // An allocator that uses malloc/free to allow removing dependency on the C++ -// standard libary runtime. -template <typename T> struct allocator { +// standard libary runtime. std::decay is used for back_inserter to be found by +// ADL when applied to memory_buffer. +template <typename T> struct allocator : private std::decay<void> { using value_type = T; - T* allocate(size_t n) { + auto allocate(size_t n) -> T* { FMT_ASSERT(n <= max_value<size_t>() / sizeof(T), ""); T* p = static_cast<T*>(malloc(n * sizeof(T))); if (!p) FMT_THROW(std::bad_alloc()); @@ -750,8 +754,23 @@ template <typename T> struct allocator { } void deallocate(T* p, size_t) { free(p); } + + constexpr friend auto operator==(allocator, allocator) noexcept -> bool { + return true; // All instances of this allocator are equivalent. + } + constexpr friend auto operator!=(allocator, allocator) noexcept -> bool { + return false; + } }; +template <typename Formatter> +FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set) + -> decltype(f.set_debug_format(set)) { + f.set_debug_format(set); +} +template <typename Formatter> +FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {} + } // namespace detail FMT_BEGIN_EXPORT @@ -825,11 +844,32 @@ class basic_memory_buffer : public detail::buffer<T> { FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); } private: + template <typename Alloc = Allocator, + FMT_ENABLE_IF(std::allocator_traits<Alloc>:: + propagate_on_container_move_assignment::value)> + FMT_CONSTEXPR20 auto move_alloc(basic_memory_buffer& other) -> bool { + alloc_ = std::move(other.alloc_); + return true; + } + // If the allocator does not propagate then copy the data from other. + template <typename Alloc = Allocator, + FMT_ENABLE_IF(!std::allocator_traits<Alloc>:: + propagate_on_container_move_assignment::value)> + FMT_CONSTEXPR20 auto move_alloc(basic_memory_buffer& other) -> bool { + T* data = other.data(); + if (alloc_ == other.alloc_ || data == other.store_) return true; + size_t size = other.size(); + // Perform copy operation, allocators are different. + this->resize(size); + detail::copy<T>(data, data + size, this->data()); + return false; + } + // Move data from other to this buffer. FMT_CONSTEXPR20 void move(basic_memory_buffer& other) { - alloc_ = std::move(other.alloc_); T* data = other.data(); size_t size = other.size(), capacity = other.capacity(); + if (!move_alloc(other)) return; if (data == other.store_) { this->set(store_, capacity); detail::copy<T>(other.store_, other.store_ + size, store_); @@ -918,7 +958,7 @@ class string_buffer { inline string_buffer() : buf_(str_) {} inline operator writer() { return buf_; } - inline std::string& str() { return str_; } + inline auto str() -> std::string& { return str_; } }; template <typename T, size_t SIZE, typename Allocator> @@ -1044,7 +1084,7 @@ inline auto do_count_digits(uint64_t n) -> int { 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; - static constexpr const uint64_t zero_or_powers_of_10[] = { + static constexpr uint64_t zero_or_powers_of_10[] = { 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), 10000000000000000000ULL}; return t - (n < zero_or_powers_of_10[t]); @@ -1225,7 +1265,7 @@ FMT_CONSTEXPR auto do_format_base2e(int base_bits, Char* out, UInt value, out += size; do { const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; - unsigned digit = static_cast<unsigned>(value & ((1 << base_bits) - 1)); + unsigned digit = static_cast<unsigned>(value & ((1u << base_bits) - 1)); *--out = static_cast<Char>(base_bits < 4 ? static_cast<char>('0' + digit) : digits[digit]); } while ((value >>= base_bits) != 0); @@ -1283,10 +1323,11 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 { explicit to_utf8(basic_string_view<WChar> s, to_utf8_error_policy policy = to_utf8_error_policy::abort) { static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4, - "Expect utf16 or utf32"); - if (!convert(s, policy)) + "expected utf16 or utf32"); + if (!convert(s, policy)) { FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16" : "invalid utf32")); + } } operator string_view() const { return string_view(&buffer_[0], size()); } auto size() const -> size_t { return buffer_.size() - 1; } @@ -1316,9 +1357,8 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 { buf.append(string_view("\xEF\xBF\xBD")); --p; continue; - } else { - c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00; } + c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00; } if (c < 0x80) { buf.push_back(static_cast<char>(c)); @@ -1343,7 +1383,7 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 { }; // Computes 128-bit result of multiplication of two 64-bit unsigned integers. -inline auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback { +FMT_INLINE auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback { #if FMT_USE_INT128 auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y); return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)}; @@ -1487,6 +1527,13 @@ template <typename Float> constexpr auto exponent_bias() -> int { : std::numeric_limits<Float>::max_exponent - 1; } +FMT_CONSTEXPR inline auto compute_exp_size(int exp) -> int { + auto prefix_size = 2; // sign + 'e' + auto abs_exp = exp >= 0 ? exp : -exp; + if (abs_exp < 100) return prefix_size + 2; + return prefix_size + (abs_exp >= 1000 ? 4 : 3); +} + // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. template <typename Char, typename OutputIt> FMT_CONSTEXPR auto write_exponent(int exp, OutputIt out) -> OutputIt { @@ -1519,7 +1566,7 @@ template <typename F> struct basic_fp { F f; int e; - static constexpr const int num_significand_bits = + static constexpr int num_significand_bits = static_cast<int>(sizeof(F) * num_bits<unsigned char>()); constexpr basic_fp() : f(0), e(0) {} @@ -1612,6 +1659,15 @@ constexpr auto convert_float(T value) -> convert_float_result<T> { return static_cast<convert_float_result<T>>(value); } +template <bool C, typename T, typename F, FMT_ENABLE_IF(C)> +auto select(T true_value, F) -> T { + return true_value; +} +template <bool C, typename T, typename F, FMT_ENABLE_IF(!C)> +auto select(T, F false_value) -> F { + return false_value; +} + template <typename Char, typename OutputIt> FMT_CONSTEXPR FMT_NOINLINE auto fill(OutputIt it, size_t n, const basic_specs& specs) -> OutputIt { @@ -1808,16 +1864,6 @@ FMT_CONSTEXPR auto write_char(OutputIt out, Char value, return it; }); } -template <typename Char, typename OutputIt> -FMT_CONSTEXPR auto write(OutputIt out, Char value, const format_specs& specs, - locale_ref loc = {}) -> OutputIt { - // char is formatted as unsigned char for consistency across platforms. - using unsigned_type = - conditional_t<std::is_same<Char, char>::value, unsigned char, unsigned>; - return check_char_specs(specs) - ? write_char<Char>(out, value, specs) - : write<Char>(out, static_cast<unsigned_type>(value), specs, loc); -} template <typename Char> class digit_grouping { private: @@ -1841,9 +1887,7 @@ template <typename Char> class digit_grouping { } public: - template <typename Locale, - FMT_ENABLE_IF(std::is_same<Locale, locale_ref>::value)> - explicit digit_grouping(Locale loc, bool localized = true) { + explicit digit_grouping(locale_ref loc, bool localized = true) { if (!localized) return; auto sep = thousands_sep<Char>(loc); grouping_ = sep.grouping; @@ -1861,7 +1905,7 @@ template <typename Char> class digit_grouping { return count; } - // Applies grouping to digits and write the output to out. + // Applies grouping to digits and writes the output to out. template <typename Out, typename C> auto apply(Out out, basic_string_view<C> digits) const -> Out { auto num_digits = static_cast<int>(digits.size()); @@ -1943,6 +1987,8 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, // Writes a localized value. FMT_API auto write_loc(appender out, loc_value value, const format_specs& specs, locale_ref loc) -> bool; +auto write_loc(basic_appender<wchar_t> out, loc_value value, + const format_specs& specs, locale_ref loc) -> bool; #endif template <typename OutputIt> inline auto write_loc(OutputIt, const loc_value&, const format_specs&, @@ -1964,8 +2010,7 @@ FMT_CONSTEXPR auto make_write_int_arg(T value, sign s) prefix = 0x01000000 | '-'; abs_value = 0 - abs_value; } else { - constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', - 0x1000000u | ' '}; + constexpr unsigned prefixes[4] = {0, 0, 0x1000000u | '+', 0x1000000u | ' '}; prefix = prefixes[static_cast<int>(s)]; } return {abs_value, prefix}; @@ -2018,7 +2063,7 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg, const format_specs& specs) -> OutputIt { static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, ""); - constexpr int buffer_size = num_bits<T>(); + constexpr size_t buffer_size = num_bits<T>(); char buffer[buffer_size]; if (is_constant_evaluated()) fill_n(buffer, buffer_size, '\0'); const char* begin = nullptr; @@ -2111,12 +2156,108 @@ FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, } template <typename Char, typename OutputIt> +FMT_CONSTEXPR auto write(OutputIt out, Char value, const format_specs& specs, + locale_ref loc = {}) -> OutputIt { + // char is formatted as unsigned char for consistency across platforms. + using unsigned_type = + conditional_t<std::is_same<Char, char>::value, unsigned char, unsigned>; + return check_char_specs(specs) + ? write_char<Char>(out, value, specs) + : write<Char>(out, static_cast<unsigned_type>(value), specs, loc); +} + +template <typename Char, typename OutputIt, + FMT_ENABLE_IF(std::is_same<Char, char>::value)> +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s, + const format_specs& specs) -> OutputIt { + bool is_debug = specs.type() == presentation_type::debug; + if (specs.precision < 0 && specs.width == 0) { + auto&& it = reserve(out, s.size()); + return is_debug ? write_escaped_string(it, s) : copy<char>(s, it); + } + + size_t display_width_limit = + specs.precision < 0 ? SIZE_MAX : to_unsigned(specs.precision); + size_t display_width = + !is_debug || specs.precision == 0 ? 0 : 1; // Account for opening '"'. + size_t size = !is_debug || specs.precision == 0 ? 0 : 1; + for_each_codepoint(s, [&](uint32_t cp, string_view sv) { + if (is_debug && needs_escape(cp)) { + counting_buffer<char> buf; + write_escaped_cp(basic_appender<char>(buf), + find_escape_result<char>{sv.begin(), sv.end(), cp}); + // We're reinterpreting bytes as display width. That's okay + // because write_escaped_cp() only writes ASCII characters. + size_t cp_width = buf.count(); + if (display_width + cp_width <= display_width_limit) { + display_width += cp_width; + size += cp_width; + // If this is the end of the string, account for closing '"'. + if (display_width < display_width_limit && sv.end() == s.end()) { + ++display_width; + ++size; + } + return true; + } + + size += display_width_limit - display_width; + display_width = display_width_limit; + return false; + } + + size_t cp_width = display_width_of(cp); + if (cp_width + display_width <= display_width_limit) { + display_width += cp_width; + size += sv.size(); + // If this is the end of the string, account for closing '"'. + if (is_debug && display_width < display_width_limit && + sv.end() == s.end()) { + ++display_width; + ++size; + } + return true; + } + + return false; + }); + + struct bounded_output_iterator { + reserve_iterator<OutputIt> underlying_iterator; + size_t bound; + + FMT_CONSTEXPR auto operator*() -> bounded_output_iterator& { return *this; } + FMT_CONSTEXPR auto operator++() -> bounded_output_iterator& { + return *this; + } + FMT_CONSTEXPR auto operator++(int) -> bounded_output_iterator& { + return *this; + } + FMT_CONSTEXPR auto operator=(char c) -> bounded_output_iterator& { + if (bound > 0) { + *underlying_iterator++ = c; + --bound; + } + return *this; + } + }; + + return write_padded<char>( + out, specs, size, display_width, [=](reserve_iterator<OutputIt> it) { + return is_debug + ? write_escaped_string(bounded_output_iterator{it, size}, s) + .underlying_iterator + : copy<char>(s.data(), s.data() + size, it); + }); +} + +template <typename Char, typename OutputIt, + FMT_ENABLE_IF(!std::is_same<Char, char>::value)> FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s, const format_specs& specs) -> OutputIt { auto data = s.data(); auto size = s.size(); if (specs.precision >= 0 && to_unsigned(specs.precision) < size) - size = code_point_index(s, to_unsigned(specs.precision)); + size = to_unsigned(specs.precision); bool is_debug = specs.type() == presentation_type::debug; if (is_debug) { @@ -2125,22 +2266,19 @@ FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s, size = buf.count(); } - size_t width = 0; - if (specs.width != 0) { - width = - is_debug ? size : compute_width(basic_string_view<Char>(data, size)); - } return write_padded<Char>( - out, specs, size, width, [=](reserve_iterator<OutputIt> it) { + out, specs, size, [=](reserve_iterator<OutputIt> it) { return is_debug ? write_escaped_string(it, s) : copy<Char>(data, data + size, it); }); } + template <typename Char, typename OutputIt> FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s, const format_specs& specs, locale_ref) -> OutputIt { return write<Char>(out, s, specs); } + template <typename Char, typename OutputIt> FMT_CONSTEXPR auto write(OutputIt out, const Char* s, const format_specs& specs, locale_ref) -> OutputIt { @@ -2274,7 +2412,7 @@ inline auto write_significand(Char* out, UInt significand, int significand_size, int floating_size = significand_size - integral_size; for (int i = floating_size / 2; i > 0; --i) { out -= 2; - write2digits(out, static_cast<std::size_t>(significand % 100)); + write2digits(out, static_cast<size_t>(significand % 100)); significand /= 100; } if (floating_size % 2 != 0) { @@ -2328,93 +2466,80 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, buffer.end(), out); } -template <typename Char, typename OutputIt, typename DecimalFP, - typename Grouping = digit_grouping<Char>> -FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, - const format_specs& specs, sign s, - int exp_upper, locale_ref loc) -> OutputIt { - auto significand = f.significand; - int significand_size = get_significand_size(f); - const Char zero = static_cast<Char>('0'); - size_t size = to_unsigned(significand_size) + (s != sign::none ? 1 : 0); - using iterator = reserve_iterator<OutputIt>; +// Numbers with exponents greater or equal to the returned value will use +// the exponential notation. +template <typename T> FMT_CONSTEVAL auto exp_upper() -> int { + return std::numeric_limits<T>::digits10 != 0 + ? min_of(16, std::numeric_limits<T>::digits10 + 1) + : 16; +} - Char decimal_point = specs.localized() ? detail::decimal_point<Char>(loc) - : static_cast<Char>('.'); +// Use the fixed notation if the exponent is in [-4, exp_upper), +// e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. +constexpr auto use_fixed(int exp, int exp_upper) -> bool { + return exp >= -4 && exp < exp_upper; +} - int output_exp = f.exponent + significand_size - 1; - auto use_exp_format = [=]() { - if (specs.type() == presentation_type::exp) return true; - if (specs.type() == presentation_type::fixed) return false; - // Use the fixed notation if the exponent is in [exp_lower, exp_upper), - // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. - const int exp_lower = -4; - return output_exp < exp_lower || - output_exp >= (specs.precision > 0 ? specs.precision : exp_upper); - }; - if (use_exp_format()) { - int num_zeros = 0; - if (specs.alt()) { - num_zeros = specs.precision - significand_size; - if (num_zeros < 0) num_zeros = 0; - size += to_unsigned(num_zeros); - } else if (significand_size == 1) { - decimal_point = Char(); - } - auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp; - int exp_digits = 2; - if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3; +template <typename Char> class fallback_digit_grouping { + public: + constexpr fallback_digit_grouping(locale_ref, bool) {} - size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits); - char exp_char = specs.upper() ? 'E' : 'e'; - auto write = [=](iterator it) { - if (s != sign::none) *it++ = detail::getsign<Char>(s); - // Insert a decimal point after the first digit and add an exponent. - it = write_significand(it, significand, significand_size, 1, - decimal_point); - if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero); - *it++ = static_cast<Char>(exp_char); - return write_exponent<Char>(output_exp, it); - }; - return specs.width > 0 - ? write_padded<Char, align::right>(out, specs, size, write) - : base_iterator(out, write(reserve(out, size))); + constexpr auto has_separator() const -> bool { return false; } + + constexpr auto count_separators(int) const -> int { return 0; } + + template <typename Out, typename C> + constexpr auto apply(Out out, basic_string_view<C>) const -> Out { + return out; } +}; + +template <typename Char, typename Grouping, typename OutputIt, + typename DecimalFP> +FMT_CONSTEXPR20 auto write_fixed(OutputIt out, const DecimalFP& f, + int significand_size, Char decimal_point, + const format_specs& specs, sign s, + locale_ref loc = {}) -> OutputIt { + using iterator = reserve_iterator<OutputIt>; int exp = f.exponent + significand_size; + long long size = significand_size + (s != sign::none ? 1 : 0); if (f.exponent >= 0) { // 1234e5 -> 123400000[.0+] - size += to_unsigned(f.exponent); + size += f.exponent; int num_zeros = specs.precision - exp; abort_fuzzing_if(num_zeros > 5000); if (specs.alt()) { ++size; if (num_zeros <= 0 && specs.type() != presentation_type::fixed) num_zeros = 0; - if (num_zeros > 0) size += to_unsigned(num_zeros); + if (num_zeros > 0) size += num_zeros; } auto grouping = Grouping(loc, specs.localized()); - size += to_unsigned(grouping.count_separators(exp)); - return write_padded<Char, align::right>(out, specs, size, [&](iterator it) { - if (s != sign::none) *it++ = detail::getsign<Char>(s); - it = write_significand<Char>(it, significand, significand_size, - f.exponent, grouping); - if (!specs.alt()) return it; - *it++ = decimal_point; - return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; - }); - } else if (exp > 0) { + size += grouping.count_separators(exp); + return write_padded<Char, align::right>( + out, specs, static_cast<size_t>(size), [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign<Char>(s); + it = write_significand<Char>(it, f.significand, significand_size, + f.exponent, grouping); + if (!specs.alt()) return it; + *it++ = decimal_point; + return num_zeros > 0 ? detail::fill_n(it, num_zeros, Char('0')) : it; + }); + } + if (exp > 0) { // 1234e-2 -> 12.34[0+] int num_zeros = specs.alt() ? specs.precision - significand_size : 0; - size += 1 + static_cast<unsigned>(max_of(num_zeros, 0)); + size += 1 + max_of(num_zeros, 0); auto grouping = Grouping(loc, specs.localized()); - size += to_unsigned(grouping.count_separators(exp)); - return write_padded<Char, align::right>(out, specs, size, [&](iterator it) { - if (s != sign::none) *it++ = detail::getsign<Char>(s); - it = write_significand(it, significand, significand_size, exp, - decimal_point, grouping); - return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; - }); + size += grouping.count_separators(exp); + return write_padded<Char, align::right>( + out, specs, to_unsigned(size), [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign<Char>(s); + it = write_significand(it, f.significand, significand_size, exp, + decimal_point, grouping); + return num_zeros > 0 ? detail::fill_n(it, num_zeros, Char('0')) : it; + }); } // 1234e-6 -> 0.001234 int num_zeros = -exp; @@ -2423,41 +2548,68 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, num_zeros = specs.precision; } bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt(); - size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros); - return write_padded<Char, align::right>(out, specs, size, [&](iterator it) { - if (s != sign::none) *it++ = detail::getsign<Char>(s); - *it++ = zero; - if (!pointy) return it; - *it++ = decimal_point; - it = detail::fill_n(it, num_zeros, zero); - return write_significand<Char>(it, significand, significand_size); - }); + size += 1 + (pointy ? 1 : 0) + num_zeros; + return write_padded<Char, align::right>( + out, specs, to_unsigned(size), [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign<Char>(s); + *it++ = Char('0'); + if (!pointy) return it; + *it++ = decimal_point; + it = detail::fill_n(it, num_zeros, Char('0')); + return write_significand<Char>(it, f.significand, significand_size); + }); } -template <typename Char> class fallback_digit_grouping { - public: - constexpr fallback_digit_grouping(locale_ref, bool) {} - - constexpr auto has_separator() const -> bool { return false; } - - constexpr auto count_separators(int) const -> int { return 0; } +template <typename Char, typename Grouping, typename OutputIt, + typename DecimalFP> +FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, + const format_specs& specs, sign s, + int exp_upper, locale_ref loc) -> OutputIt { + Char point = specs.localized() ? detail::decimal_point<Char>(loc) : Char('.'); + int significand_size = get_significand_size(f); + int exp = f.exponent + significand_size - 1; + if (specs.type() == presentation_type::fixed || + (specs.type() != presentation_type::exp && + use_fixed(exp, specs.precision > 0 ? specs.precision : exp_upper))) { + return write_fixed<Char, Grouping>(out, f, significand_size, point, specs, + s, loc); + } - template <typename Out, typename C> - constexpr auto apply(Out out, basic_string_view<C>) const -> Out { - return out; + // Write value in the exponential format. + int num_zeros = 0; + long long size = significand_size + (s != sign::none ? 1 : 0); + if (specs.alt()) { + num_zeros = max_of(specs.precision - significand_size, 0); + size += num_zeros; + } else if (significand_size == 1) { + point = Char(); } -}; + size += (point ? 1 : 0) + compute_exp_size(exp); + char exp_char = specs.upper() ? 'E' : 'e'; + auto write = [=](reserve_iterator<OutputIt> it) { + if (s != sign::none) *it++ = detail::getsign<Char>(s); + // Insert a decimal point after the first digit and add an exponent. + it = write_significand(it, f.significand, significand_size, 1, point); + if (num_zeros > 0) it = detail::fill_n(it, num_zeros, Char('0')); + *it++ = Char(exp_char); + return write_exponent<Char>(exp, it); + }; + auto usize = to_unsigned(size); + return specs.width > 0 + ? write_padded<Char, align::right>(out, specs, usize, write) + : base_iterator(out, write(reserve(out, usize))); +} template <typename Char, typename OutputIt, typename DecimalFP> FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, const format_specs& specs, sign s, int exp_upper, locale_ref loc) -> OutputIt { if (is_constant_evaluated()) { - return do_write_float<Char, OutputIt, DecimalFP, - fallback_digit_grouping<Char>>(out, f, specs, s, - exp_upper, loc); + return do_write_float<Char, fallback_digit_grouping<Char>>(out, f, specs, s, + exp_upper, loc); } else { - return do_write_float<Char>(out, f, specs, s, exp_upper, loc); + return do_write_float<Char, digit_grouping<Char>>(out, f, specs, s, + exp_upper, loc); } } @@ -2488,7 +2640,7 @@ FMT_CONSTEXPR auto isfinite(T value) -> bool { } template <typename T, FMT_ENABLE_IF(is_floating_point<T>::value)> -FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { +FMT_INLINE FMT_CONSTEXPR auto signbit(T value) -> bool { if (is_constant_evaluated()) { #ifdef __cpp_if_constexpr if constexpr (std::numeric_limits<double>::is_iec559) { @@ -2728,7 +2880,7 @@ class bigint { bigits_.resize(to_unsigned(num_bigits + exp_difference)); for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) bigits_[j] = bigits_[i]; - memset(bigits_.data(), 0, to_unsigned(exp_difference) * sizeof(bigit)); + fill_n(bigits_.data(), to_unsigned(exp_difference), 0U); exp_ -= exp_difference; } @@ -3289,17 +3441,12 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, return exp; } -// Numbers with exponents greater or equal to the returned value will use -// the exponential notation. -template <typename T> constexpr auto exp_upper() -> int { - return std::numeric_limits<T>::digits10 != 0 - ? min_of(16, std::numeric_limits<T>::digits10 + 1) - : 16; -} +template <typename Char, typename OutputIt, typename T, + FMT_ENABLE_IF(is_floating_point<T>::value)> +FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, + locale_ref loc = {}) -> OutputIt { + if (specs.localized() && write_loc(out, value, specs, loc)) return out; -template <typename Char, typename OutputIt, typename T> -FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, - locale_ref loc) -> OutputIt { // Use signbit because value < 0 is false for NaN. sign s = detail::signbit(value) ? sign::minus : specs.sign(); @@ -3312,15 +3459,14 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, if (specs.width != 0) --specs.width; } - constexpr int exp_upper = detail::exp_upper<T>(); + const int exp_upper = detail::exp_upper<T>(); int precision = specs.precision; if (precision < 0) { if (specs.type() != presentation_type::none) { precision = 6; } else if (is_fast_float<T>::value && !is_constant_evaluated()) { // Use Dragonbox for the shortest format. - using floaty = conditional_t<sizeof(T) >= sizeof(double), double, float>; - auto dec = dragonbox::to_decimal(static_cast<floaty>(value)); + auto dec = dragonbox::to_decimal(static_cast<fast_float_t<T>>(value)); return write_float<Char>(out, dec, specs, s, exp_upper, loc); } } @@ -3353,37 +3499,76 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, } template <typename Char, typename OutputIt, typename T, - FMT_ENABLE_IF(is_floating_point<T>::value)> -FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, - locale_ref loc = {}) -> OutputIt { - return specs.localized() && write_loc(out, value, specs, loc) - ? out - : write_float<Char>(out, value, specs, loc); -} - -template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(is_fast_float<T>::value)> FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { if (is_constant_evaluated()) return write<Char>(out, value, format_specs()); auto s = detail::signbit(value) ? sign::minus : sign::none; + auto mask = exponent_mask<fast_float_t<T>>(); + if ((bit_cast<decltype(mask)>(value) & mask) == mask) + return write_nonfinite<Char>(out, std::isnan(value), {}, s); - constexpr auto specs = format_specs(); - using floaty = conditional_t<sizeof(T) >= sizeof(double), double, float>; - using floaty_uint = typename dragonbox::float_info<floaty>::carrier_uint; - floaty_uint mask = exponent_mask<floaty>(); - if ((bit_cast<floaty_uint>(value) & mask) == mask) - return write_nonfinite<Char>(out, std::isnan(value), specs, s); + auto dec = dragonbox::to_decimal(static_cast<fast_float_t<T>>(value)); + auto significand = dec.significand; + int significand_size = count_digits(significand); + int exponent = dec.exponent + significand_size - 1; + if (use_fixed(exponent, detail::exp_upper<T>())) { + return write_fixed<Char, fallback_digit_grouping<Char>>( + out, dec, significand_size, Char('.'), {}, s); + } - auto dec = dragonbox::to_decimal(static_cast<floaty>(value)); - return write_float<Char>(out, dec, specs, s, exp_upper<T>(), {}); + // Write value in the exponential format. + const char* prefix = "e+"; + int abs_exponent = exponent; + if (exponent < 0) { + abs_exponent = -exponent; + prefix = "e-"; + } + auto has_decimal_point = significand_size != 1; + size_t size = std::is_pointer<OutputIt>::value + ? 0u + : to_unsigned((s != sign::none ? 1 : 0) + significand_size + + (has_decimal_point ? 1 : 0) + + (abs_exponent >= 100 ? 5 : 4)); + if (auto ptr = to_pointer<Char>(out, size)) { + if (s != sign::none) *ptr++ = Char('-'); + if (has_decimal_point) { + auto begin = ptr; + ptr = format_decimal<Char>(ptr, significand, significand_size + 1); + *begin = begin[1]; + begin[1] = '.'; + } else { + *ptr++ = static_cast<Char>('0' + significand); + } + if (std::is_same<Char, char>::value) { + memcpy(ptr, prefix, 2); + ptr += 2; + } else { + *ptr++ = prefix[0]; + *ptr++ = prefix[1]; + } + if (abs_exponent >= 100) { + *ptr++ = static_cast<Char>('0' + abs_exponent / 100); + abs_exponent %= 100; + } + write2digits(ptr, static_cast<unsigned>(abs_exponent)); + return select<std::is_pointer<OutputIt>::value>(ptr + 2, out); + } + auto it = reserve(out, size); + if (s != sign::none) *it++ = Char('-'); + // Insert a decimal point after the first digit and add an exponent. + it = write_significand(it, significand, significand_size, 1, + has_decimal_point ? Char('.') : Char()); + *it++ = Char('e'); + it = write_exponent<Char>(exponent, it); + return base_iterator(out, it); } template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(is_floating_point<T>::value && !is_fast_float<T>::value)> inline auto write(OutputIt out, T value) -> OutputIt { - return write<Char>(out, value, format_specs()); + return write<Char>(out, value, {}); } template <typename Char, typename OutputIt> @@ -3523,32 +3708,18 @@ struct dynamic_spec_getter { } }; -template <typename Context, typename ID> -FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> basic_format_arg<Context> { - auto arg = ctx.arg(id); - if (!arg) report_error("argument not found"); - return arg; -} - template <typename Context> -FMT_CONSTEXPR int get_dynamic_spec( - arg_id_kind kind, const arg_ref<typename Context::char_type>& ref, - Context& ctx) { - FMT_ASSERT(kind != arg_id_kind::none, ""); +FMT_CONSTEXPR void handle_dynamic_spec( + arg_id_kind kind, int& value, + const arg_ref<typename Context::char_type>& ref, Context& ctx) { + if (kind == arg_id_kind::none) return; auto arg = kind == arg_id_kind::index ? ctx.arg(ref.index) : ctx.arg(ref.name); if (!arg) report_error("argument not found"); - unsigned long long value = arg.visit(dynamic_spec_getter()); - if (value > to_unsigned(max_value<int>())) + unsigned long long result = arg.visit(dynamic_spec_getter()); + if (result > to_unsigned(max_value<int>())) report_error("width/precision is out of range"); - return static_cast<int>(value); -} - -template <typename Context> -FMT_CONSTEXPR void handle_dynamic_spec( - arg_id_kind kind, int& value, - const arg_ref<typename Context::char_type>& ref, Context& ctx) { - if (kind != arg_id_kind::none) value = get_dynamic_spec(kind, ref, ctx); + value = static_cast<int>(result); } #if FMT_USE_NONTYPE_TEMPLATE_ARGS @@ -3586,7 +3757,7 @@ template <typename Char> struct udl_arg { }; #endif // FMT_USE_NONTYPE_TEMPLATE_ARGS -template <typename Char> struct format_handler { +template <typename Char = char> struct format_handler { parse_context<Char> parse_ctx; buffered_context<Char> ctx; @@ -3612,7 +3783,8 @@ template <typename Char> struct format_handler { auto on_format_specs(int id, const Char* begin, const Char* end) -> const Char* { - auto arg = get_arg(ctx, id); + auto arg = ctx.arg(id); + if (!arg) report_error("argument not found"); // Not using a visitor for custom types gives better codegen. if (arg.format_custom(begin, parse_ctx, ctx)) return parse_ctx.begin(); @@ -3632,6 +3804,7 @@ template <typename Char> struct format_handler { FMT_NORETURN void on_error(const char* message) { report_error(message); } }; +// It is used in format-inl.h and os.cc. using format_func = void (*)(detail::buffer<char>&, int, const char*); FMT_API void do_report_error(format_func func, int error_code, const char* message) noexcept; @@ -3652,28 +3825,6 @@ FMT_CONSTEXPR auto native_formatter<T, Char, TYPE>::format( specs_.precision_ref, ctx); return write<Char>(ctx.out(), val, specs, ctx.locale()); } - -// DEPRECATED! https://github.com/fmtlib/fmt/issues/4292. -template <typename T, typename Enable = void> -struct is_locale : std::false_type {}; -template <typename T> -struct is_locale<T, void_t<decltype(T::classic())>> : std::true_type {}; - -// DEPRECATED! -template <typename Char = char> struct vformat_args { - using type = basic_format_args<buffered_context<Char>>; -}; -template <> struct vformat_args<char> { - using type = format_args; -}; - -template <typename Char> -void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt, - typename vformat_args<Char>::type args, locale_ref loc = {}) { - auto out = basic_appender<Char>(buf); - parse_format_string( - fmt, format_handler<Char>{parse_context<Char>(fmt), {out, args, loc}}); -} } // namespace detail FMT_BEGIN_EXPORT @@ -3685,19 +3836,16 @@ template <typename OutputIt, typename Char> class generic_context { private: OutputIt out_; basic_format_args<generic_context> args_; - detail::locale_ref loc_; + locale_ref loc_; public: using char_type = Char; using iterator = OutputIt; - using parse_context_type FMT_DEPRECATED = parse_context<Char>; - template <typename T> - using formatter_type FMT_DEPRECATED = formatter<T, Char>; enum { builtin_types = FMT_BUILTIN_TYPES }; constexpr generic_context(OutputIt out, basic_format_args<generic_context> args, - detail::locale_ref loc = {}) + locale_ref loc = {}) : out_(out), args_(args), loc_(loc) {} generic_context(generic_context&&) = default; generic_context(const generic_context&) = delete; @@ -3720,7 +3868,7 @@ template <typename OutputIt, typename Char> class generic_context { if (!detail::is_back_insert_iterator<iterator>()) out_ = it; } - constexpr auto locale() const -> detail::locale_ref { return loc_; } + constexpr auto locale() const -> locale_ref { return loc_; } }; class loc_value { @@ -3825,7 +3973,7 @@ struct formatter<T, Char, void_t<detail::format_as_result<T>>> * auto s = fmt::format("{}", fmt::ptr(p)); */ template <typename T> auto ptr(T p) -> const void* { - static_assert(std::is_pointer<T>::value, ""); + static_assert(std::is_pointer<T>::value, "fmt::ptr used with non-pointer"); return detail::bit_cast<const void*>(p); } @@ -3850,13 +3998,14 @@ constexpr auto format_as(Enum e) noexcept -> underlying_t<Enum> { } // namespace enums #ifdef __cpp_lib_byte -template <> struct formatter<std::byte> : formatter<unsigned> { +template <typename Char> +struct formatter<std::byte, Char> : formatter<unsigned, Char> { static auto format_as(std::byte b) -> unsigned char { return static_cast<unsigned char>(b); } template <typename Context> auto format(std::byte b, Context& ctx) const -> decltype(ctx.out()) { - return formatter<unsigned>::format(format_as(b), ctx); + return formatter<unsigned, Char>::format(format_as(b), ctx); } }; #endif @@ -4070,21 +4219,26 @@ class format_int { inline auto str() const -> std::string { return {str_, size()}; } }; -#define FMT_STRING_IMPL(s, base) \ - [] { \ - /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ - /* Use a macro-like name to avoid shadowing warnings. */ \ - struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \ - using char_type = fmt::remove_cvref_t<decltype(s[0])>; \ - constexpr explicit operator fmt::basic_string_view<char_type>() const { \ - return fmt::detail::compile_string_to_view<char_type>(s); \ - } \ - }; \ - using FMT_STRING_VIEW = \ - fmt::basic_string_view<typename FMT_COMPILE_STRING::char_type>; \ - fmt::detail::ignore_unused(FMT_STRING_VIEW(FMT_COMPILE_STRING())); \ - return FMT_COMPILE_STRING(); \ - }() +#if FMT_CLANG_ANALYZER +# define FMT_STRING_IMPL(s, base) s +#else +# define FMT_STRING_IMPL(s, base) \ + [] { \ + /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ + /* Use a macro-like name to avoid shadowing warnings. */ \ + struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \ + using char_type = fmt::remove_cvref_t<decltype(s[0])>; \ + constexpr explicit operator fmt::basic_string_view<char_type>() \ + const { \ + return fmt::detail::compile_string_to_view<char_type>(s); \ + } \ + }; \ + using FMT_STRING_VIEW = \ + fmt::basic_string_view<typename FMT_COMPILE_STRING::char_type>; \ + fmt::detail::ignore_unused(FMT_STRING_VIEW(FMT_COMPILE_STRING())); \ + return FMT_COMPILE_STRING(); \ + }() +#endif // FMT_CLANG_ANALYZER /** * Constructs a legacy compile-time format string from a string literal `s`. @@ -4140,46 +4294,41 @@ FMT_API void format_system_error(detail::buffer<char>& out, int error_code, // Can be used to report errors from destructors. FMT_API void report_system_error(int error_code, const char* message) noexcept; -template <typename Locale, FMT_ENABLE_IF(detail::is_locale<Locale>::value)> -inline auto vformat(const Locale& loc, string_view fmt, format_args args) +inline auto vformat(locale_ref loc, string_view fmt, format_args args) -> std::string { auto buf = memory_buffer(); - detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); + detail::vformat_to(buf, fmt, args, loc); return {buf.data(), buf.size()}; } -template <typename Locale, typename... T, - FMT_ENABLE_IF(detail::is_locale<Locale>::value)> -FMT_INLINE auto format(const Locale& loc, format_string<T...> fmt, T&&... args) +template <typename... T> +FMT_INLINE auto format(locale_ref loc, format_string<T...> fmt, T&&... args) -> std::string { return vformat(loc, fmt.str, vargs<T...>{{args...}}); } -template <typename OutputIt, typename Locale, +template <typename OutputIt, FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)> -auto vformat_to(OutputIt out, const Locale& loc, string_view fmt, - format_args args) -> OutputIt { +auto vformat_to(OutputIt out, locale_ref loc, string_view fmt, format_args args) + -> OutputIt { auto&& buf = detail::get_buffer<char>(out); - detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); + detail::vformat_to(buf, fmt, args, loc); return detail::get_iterator(buf, out); } -template <typename OutputIt, typename Locale, typename... T, - FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&& - detail::is_locale<Locale>::value)> -FMT_INLINE auto format_to(OutputIt out, const Locale& loc, - format_string<T...> fmt, T&&... args) -> OutputIt { +template <typename OutputIt, typename... T, + FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)> +FMT_INLINE auto format_to(OutputIt out, locale_ref loc, format_string<T...> fmt, + T&&... args) -> OutputIt { return fmt::vformat_to(out, loc, fmt.str, vargs<T...>{{args...}}); } -template <typename Locale, typename... T, - FMT_ENABLE_IF(detail::is_locale<Locale>::value)> -FMT_NODISCARD FMT_INLINE auto formatted_size(const Locale& loc, +template <typename... T> +FMT_NODISCARD FMT_INLINE auto formatted_size(locale_ref loc, format_string<T...> fmt, T&&... args) -> size_t { auto buf = detail::counting_buffer<>(); - detail::vformat_to(buf, fmt.str, vargs<T...>{{args...}}, - detail::locale_ref(loc)); + detail::vformat_to(buf, fmt.str, vargs<T...>{{args...}}, loc); return buf.count(); } @@ -4208,7 +4357,7 @@ FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T&&... args) * std::string answer = fmt::to_string(42); */ template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> -FMT_NODISCARD auto to_string(T value) -> std::string { +FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(T value) -> std::string { // The buffer should be large enough to store the number including the sign // or "false" for bool. char buffer[max_of(detail::digits10<T>() + 2, 5)]; @@ -4216,13 +4365,15 @@ FMT_NODISCARD auto to_string(T value) -> std::string { } template <typename T, FMT_ENABLE_IF(detail::use_format_as<T>::value)> -FMT_NODISCARD auto to_string(const T& value) -> std::string { +FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(const T& value) + -> std::string { return to_string(format_as(value)); } template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value && !detail::use_format_as<T>::value)> -FMT_NODISCARD auto to_string(const T& value) -> std::string { +FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(const T& value) + -> std::string { auto buffer = memory_buffer(); detail::write<char>(appender(buffer), value); return {buffer.data(), buffer.size()}; diff --git a/vendor/lyra/lyra.hpp b/vendor/lyra/lyra.hpp index d97c511f1..4e8470f4d 100644 --- a/vendor/lyra/lyra.hpp +++ b/vendor/lyra/lyra.hpp @@ -1,4 +1,4 @@ -// Copyright 2018-2022 René Ferdinand Rivera Morell +// Copyright René Ferdinand Rivera Morell // Copyright 2021 Max Ferger // Copyright 2017 Two Blue Cubes Ltd. All rights reserved. // @@ -13,8 +13,8 @@ #define LYRA_VERSION_HPP #define LYRA_VERSION_MAJOR 1 -#define LYRA_VERSION_MINOR 6 -#define LYRA_VERSION_PATCH 1 +#define LYRA_VERSION_MINOR 7 +#define LYRA_VERSION_PATCH 0 #define LYRA_VERSION \ (((LYRA_VERSION_MAJOR)*10000000) + ((LYRA_VERSION_MINOR)*100000) \ @@ -27,143 +27,6 @@ #define LYRA_ARG_HPP -#ifndef LYRA_DETAIL_PRINT_HPP -#define LYRA_DETAIL_PRINT_HPP - -#include <iostream> -#include <string> - -#ifndef LYRA_DEBUG -# define LYRA_DEBUG 0 -#endif - -namespace lyra { namespace detail { - -constexpr bool is_debug = LYRA_DEBUG; - -template <typename T> -std::string to_string(T && t) -{ - return std::string(std::move(t)); -} - -using std::to_string; - -struct print -{ - print(const char * scope_name = nullptr) - : scope(scope_name) - { - if (is_debug) print::depth() += 1; - if (scope) debug(scope, "..."); - } - - ~print() - { - if (scope) debug("...", scope); - if (is_debug) print::depth() -= 1; - } - - template <typename... A> - void debug(A... arg) - { - if (is_debug) - { - std::cerr << "[DEBUG]" - << std::string((print::depth() - 1) * 2, ' '); - std::string args[] = { to_string(arg)... }; - for (auto & arg_string : args) - { - std::cerr << " " << arg_string; - } - std::cerr << "\n"; - } - } - - private: - const char * scope; - - static std::size_t & depth() - { - static std::size_t d = 0; - return d; - } -}; - -}} // namespace lyra::detail - -#if LYRA_DEBUG -# define LYRA_PRINT_SCOPE ::lyra::detail::print lyra_print_scope -# define LYRA_PRINT_DEBUG lyra_print_scope.debug -#else -# define LYRA_PRINT_SCOPE(...) while (false) -# define LYRA_PRINT_DEBUG(...) while (false) -#endif - -#endif - -#ifndef LYRA_PARSER_HPP -#define LYRA_PARSER_HPP - - -#ifndef LYRA_ARGS_HPP -#define LYRA_ARGS_HPP - -#include <initializer_list> -#include <string> -#include <vector> - -namespace lyra { - -/* tag::reference[] - -[#lyra_args] -= `lyra::args` - -Transport for raw args (copied from main args, supplied via init list, or from -a pair of iterators). - -*/ // end::reference[] -class args -{ - public: - args(int argc, char const * const * argv) - : m_exeName(argv[0]) - , m_args(argv + 1, argv + argc) - {} - - args(std::initializer_list<std::string> args_list) - : m_exeName(*args_list.begin()) - , m_args(args_list.begin() + 1, args_list.end()) - {} - - template <typename It> - args(const It & start, const It & end) - : m_exeName(*start) - , m_args(start + 1, end) - {} - - std::string exe_name() const { return m_exeName; } - - std::vector<std::string>::const_iterator begin() const - { - return m_args.begin(); - } - - std::vector<std::string>::const_iterator end() const - { - return m_args.end(); - } - - private: - std::string m_exeName; - std::vector<std::string> m_args; -}; - -} // namespace lyra - -#endif - #ifndef LYRA_DETAIL_BOUND_HPP #define LYRA_DETAIL_BOUND_HPP @@ -190,15 +53,14 @@ struct is_callable template <class U> static auto test(...) -> decltype(std::false_type()); - static constexpr bool value = decltype(test<F>(0))::value; + static constexpr bool value = decltype(test<F>(nullptr))::value; }; template <class T> struct remove_cvref { - typedef - typename std::remove_cv<typename std::remove_reference<T>::type>::type - type; + using type = + typename std::remove_cv<typename std::remove_reference<T>::type>::type; }; template <class F> @@ -212,7 +74,7 @@ struct is_invocable static auto test(...) -> decltype(std::false_type()); static constexpr bool value - = decltype(test<typename remove_cvref<F>::type>(0))::value; + = decltype(test<typename remove_cvref<F>::type>(nullptr))::value; }; template <typename... Ts> @@ -230,21 +92,40 @@ template <template <class...> class Primary, class... Args> struct is_specialization_of<Primary<Args...>, Primary> : std::true_type {}; +template <typename C> +struct is_character +{ + using bare_t = typename remove_cvref<C>::type; + static constexpr bool value = false || std::is_same<char, bare_t>::value + || std::is_same<signed char, bare_t>::value + || std::is_same<unsigned char, bare_t>::value + || std::is_same<wchar_t, bare_t>::value +#if (__cplusplus >= 202002L) + || std::is_same<char8_t, bare_t>::value +#endif + || std::is_same<char16_t, bare_t>::value + || std::is_same<char32_t, bare_t>::value; +}; + }} // namespace lyra::detail #endif -#include <algorithm> +#include <cctype> #include <sstream> #include <string> #include <type_traits> #ifndef LYRA_CONFIG_OPTIONAL_TYPE -# ifdef __has_include -# if __has_include(<optional>) && __cplusplus >= 201703L -# include <optional> -# define LYRA_CONFIG_OPTIONAL_TYPE std::optional -# endif +# if defined(__has_include) && __has_include(<version>) +# include <version> +# elif defined(__has_include) && __has_include(<ciso646>) +# include <ciso646> +# endif +# if defined(__has_include) && __has_include(<optional>) \ + && defined(__cpp_lib_optional) && (__cpp_lib_optional >= 201606L) +# include <optional> +# define LYRA_CONFIG_OPTIONAL_TYPE std::optional # endif #endif @@ -363,8 +244,8 @@ inline bool from_string(S const & source, bool & target) { std::string srcLC; to_string(source, srcLC); - std::transform(srcLC.begin(), srcLC.end(), srcLC.begin(), - [](char c) { return static_cast<char>(::tolower(c)); }); + for (std::string::value_type & c : srcLC) + c = static_cast<std::string::value_type>(std::tolower(c)); if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") target = true; @@ -389,8 +270,8 @@ inline bool from_string(S const & source, LYRA_CONFIG_OPTIONAL_TYPE<T> & target) { std::string srcLC; to_string(source, srcLC); - std::transform(srcLC.begin(), srcLC.end(), srcLC.begin(), - [](char c) { return static_cast<char>(::tolower(c)); }); + for (std::string::value_type & c : srcLC) + c = static_cast<std::string::value_type>(::tolower(c)); if (srcLC == "<nullopt>") { target.reset(); @@ -437,7 +318,6 @@ class result_base explicit operator bool() const { return is_ok(); } bool is_ok() const { return kind_ == result_kind::ok; } std::string message() const { return message_; } - [[deprecated]] std::string errorMessage() const { return message(); } protected: enum class result_kind @@ -578,6 +458,7 @@ class basic_result<void> : public result_value_base<void> }} // namespace lyra::detail #endif + #include <string> namespace lyra { @@ -586,7 +467,8 @@ enum class parser_result_type { matched, no_match, - short_circuit_all + short_circuit_all, + empty_match }; inline std::string to_string(parser_result_type v) @@ -596,6 +478,7 @@ inline std::string to_string(parser_result_type v) case parser_result_type::matched: return "matched"; case parser_result_type::no_match: return "no_match"; case parser_result_type::short_circuit_all: return "short_circuit_all"; + case parser_result_type::empty_match: return "empty_match"; } return "?"; } @@ -608,8 +491,6 @@ using parser_result = detail::basic_result<parser_result_type>; #endif -#include <string> - namespace lyra { namespace detail { template <typename S, typename T> @@ -693,7 +574,12 @@ inline parser_result invokeLambda(L const & lambda, std::string const & arg) }} // namespace lyra::detail #endif + +#include <cstddef> +#include <memory> #include <string> +#include <type_traits> +#include <vector> namespace lyra { namespace detail { @@ -741,14 +627,14 @@ struct BoundValueRef : BoundValueRefBase return parse_string(arg, m_ref); } - virtual size_t get_value_count() const override { return 1; } - virtual std::string get_value(size_t i) const override + size_t get_value_count() const override { return 1; } + std::string get_value(size_t i) const override { if (i == 0) { - std::string result; - detail::to_string(m_ref, result); - return result; + std::string text; + detail::to_string(m_ref, text); + return text; } return ""; } @@ -773,8 +659,8 @@ struct BoundValueRef<std::vector<T>> : BoundValueRefBase return str_result; } - virtual size_t get_value_count() const override { return m_ref.size(); } - virtual std::string get_value(size_t i) const override + size_t get_value_count() const override { return m_ref.size(); } + std::string get_value(size_t i) const override { if (i < m_ref.size()) { @@ -800,8 +686,8 @@ struct BoundFlagRef : BoundFlagRefBase return parser_result::ok(parser_result_type::matched); } - virtual size_t get_value_count() const override { return 1; } - virtual std::string get_value(size_t i) const override + size_t get_value_count() const override { return 1; } + std::string get_value(size_t i) const override { if (i == 0) return m_ref ? "true" : "false"; return ""; @@ -815,9 +701,16 @@ struct BoundLambda : BoundValueRefBase static_assert(unary_lambda_traits<L>::isValid, "Supplied lambda must take exactly one argument"); + static_assert( + std::is_same<L, typename detail::remove_cvref<L>::type>::value, + "Supplied lambda must not be a reference"); + explicit BoundLambda(L const & lambda) : m_lambda(lambda) {} + explicit BoundLambda(L && lambda) + : m_lambda(std::move(lambda)) + {} auto setValue(std::string const & arg) -> parser_result override { @@ -834,6 +727,9 @@ struct BoundFlagLambda : BoundFlagRefBase static_assert(unary_lambda_traits<L>::isValid, "Supplied lambda must take exactly one argument"); static_assert( + std::is_same<L, typename detail::remove_cvref<L>::type>::value, + "Supplied lambda must not be a reference"); + static_assert( std::is_same<typename unary_lambda_traits<L>::ArgType, bool>::value, "flags must be boolean"); @@ -841,6 +737,10 @@ struct BoundFlagLambda : BoundFlagRefBase : m_lambda(lambda) {} + explicit BoundFlagLambda(L && lambda) + : m_lambda(std::move(lambda)) + {} + auto setFlag(bool flag) -> parser_result override { return LambdaInvoker< @@ -859,7 +759,7 @@ struct BoundVal : BoundValueRef<T> , value(v) {} - BoundVal(BoundVal && other) + BoundVal(BoundVal && other) noexcept : BoundValueRef<T>(value) , value(std::move(other.value)) {} @@ -874,136 +774,87 @@ struct BoundVal : BoundValueRef<T> #endif -#ifndef LYRA_DETAIL_CHOICES_HPP -#define LYRA_DETAIL_CHOICES_HPP +#ifndef LYRA_DETAIL_PRINT_HPP +#define LYRA_DETAIL_PRINT_HPP + +#if LYRA_DEBUG +# include <iostream> +#endif -#include <algorithm> -#include <initializer_list> #include <string> -#include <type_traits> -#include <vector> + +#ifndef LYRA_DEBUG +# define LYRA_DEBUG 0 +#endif namespace lyra { namespace detail { -/* -Type erased base for set of choices. I.e. it's an "interface". -*/ -struct choices_base -{ - virtual ~choices_base() = default; - virtual parser_result contains_value(std::string const & val) const = 0; -}; +constexpr bool is_debug = LYRA_DEBUG; -/* -Stores a set of choice values and provides checking if a given parsed -string value is one of the choices. -*/ template <typename T> -struct choices_set : choices_base +std::string to_string(T && t) { - std::vector<T> values; + return std::string(std::forward<T>(t)); +} - template <typename... Vals> - explicit choices_set(Vals... vals) - : choices_set({ vals... }) - {} +using std::to_string; - explicit choices_set(const std::vector<T> & vals) - : values(vals) - {} +#if LYRA_DEBUG - parser_result contains_value(std::string const & val) const override +struct print +{ + print(const char * scope_name = nullptr) + : scope(scope_name) { - T value; - auto parse = parse_string(val, value); - if (!parse) - { - return parser_result::error( - parser_result_type::no_match, parse.message()); - } - bool result = std::count(values.begin(), values.end(), value) > 0; - if (result) - { - return parser_result::ok(parser_result_type::matched); - } - return parser_result::error(parser_result_type::no_match, - "Value '" + val - + "' not expected. Allowed values are: " + this->to_string()); + if (is_debug) print::depth() += 1; + if (scope) debug(scope, "..."); } - std::string to_string() const + ~print() { - std::string result; - for (const T & val : values) + if (scope) debug("...", scope); + if (is_debug) print::depth() -= 1; + } + + template <typename... A> + void debug(A... arg) + { + if (is_debug) { - if (!result.empty()) result += ", "; - std::string val_string; - if (detail::to_string(val, val_string)) - { - result += val_string; - } - else + static auto indent = " | : | : | : | : | : | : | : | : | : | : "; + std::cerr << "[DEBUG]" + << std::string(indent, (print::depth() - 1) * 2); + std::string args[] = { to_string(arg)... }; + for (auto & arg_string : args) { - result += "<value error>"; + std::cerr << " " << arg_string; } + std::cerr << "\n"; } - return result; } - protected: - explicit choices_set(std::initializer_list<T> const & vals) - : values(vals) - {} -}; - -template <> -struct choices_set<const char *> : choices_set<std::string> -{ - template <typename... Vals> - explicit choices_set(Vals... vals) - : choices_set<std::string>(vals...) - {} -}; - -/* -Calls a designated function to check if the choice is valid. -*/ -template <typename Lambda> -struct choices_check : choices_base -{ - static_assert(unary_lambda_traits<Lambda>::isValid, - "Supplied lambda must take exactly one argument"); - static_assert(std::is_same<bool, - typename unary_lambda_traits<Lambda>::ReturnType>::value, - "Supplied lambda must return bool"); - - Lambda checker; - using value_type = typename unary_lambda_traits<Lambda>::ArgType; - - explicit choices_check(Lambda const & checker_function) - : checker(checker_function) - {} + private: + const char * scope; - parser_result contains_value(std::string const & val) const override + static std::size_t & depth() { - value_type value; - auto parse = parse_string(val, value); - if (!parse) - { - return parser_result::error( - parser_result_type::no_match, parse.message()); - } - if (checker(value)) - { - return parser_result::ok(parser_result_type::matched); - } - return parser_result::error( - parser_result_type::no_match, "Value '" + val + "' not expected."); + static std::size_t d = 0; + return d; } }; +#endif + }} // namespace lyra::detail +#if LYRA_DEBUG +# define LYRA_PRINT_SCOPE ::lyra::detail::print lyra_print_scope +# define LYRA_PRINT_DEBUG lyra_print_scope.debug +#else +# define LYRA_PRINT_SCOPE(...) while (false) +# define LYRA_PRINT_DEBUG(...) while (false) +#endif + #endif #ifndef LYRA_DETAIL_TOKENS_HPP @@ -1013,7 +864,9 @@ struct choices_check : choices_base #ifndef LYRA_OPTION_STYLE_HPP #define LYRA_OPTION_STYLE_HPP +#include <cstddef> #include <string> +#include <utility> namespace lyra { @@ -1031,6 +884,7 @@ std::string long_option_prefix; std::size_t long_option_size = 0; std::string short_option_prefix; std::size_t short_option_size = 0; +opt_print_order options_print_order = opt_print_order::per_declaration; ---- * `value_delimiters` -- Specifies a set of characters that are accepted as a @@ -1044,27 +898,46 @@ std::size_t short_option_size = 0; prefix for short options (i.e. single-char multi-options). * `short_option_size` -- The number of prefix characters that indicates a short option. A value of zero (0) indicates that short options are not accepted. +* `options_print_order` -- The order to print the options section of the help + text. Possible values: `per_declaration`, `sorted_short_first`, + `sorted_long_first`. +* `indent_size` -- The character count to indent argument detail description. + This affects the indenting of usage detail, the arguments, and any + sub-commands/arguments recursively. The default is "2". end::reference[] */ struct option_style { + enum class opt_print_order : unsigned char + { + per_declaration = 0, + sorted_short_first, + sorted_long_first + }; + std::string value_delimiters; std::string long_option_prefix; std::size_t long_option_size = 0; std::string short_option_prefix; std::size_t short_option_size = 0; + opt_print_order options_print_order = opt_print_order::per_declaration; + std::size_t indent_size = 2; option_style(std::string && value_delimiters_chars, std::string && long_option_prefix_chars = {}, std::size_t long_option_prefix_size = 0, std::string && short_option_prefix_chars = {}, - std::size_t short_option_prefix_size = 0) + std::size_t short_option_prefix_size = 0, + opt_print_order options_print_order_ = opt_print_order::per_declaration, + std::size_t indent_size_ = 2) : value_delimiters(std::move(value_delimiters_chars)) , long_option_prefix(std::move(long_option_prefix_chars)) , long_option_size(long_option_prefix_size) , short_option_prefix(std::move(short_option_prefix_chars)) , short_option_size(short_option_prefix_size) + , options_print_order(options_print_order_) + , indent_size(indent_size_) {} @@ -1075,6 +948,26 @@ struct option_style static const option_style & posix(); static const option_style & posix_brief(); static const option_style & windows(); + + + bool opt_print_order_less( + const std::string & a, const std::string & b) const + { + const auto l = long_option_string(); + const auto s = short_option_string(); + const bool a_l = a.substr(0, l.size()) == l; + const bool a_s = !a_l && (a.substr(0, s.size()) == s); + const bool b_l = b.substr(0, l.size()) == l; + const bool b_s = !b_l && (b.substr(0, s.size()) == s); + if (!a_l && !a_s) return false; + if (!b_l && !b_s) return true; + if ((a_l == b_l) && (a_s == b_s)) return a < b; + if (options_print_order == opt_print_order::sorted_short_first) + return (a_s && b_l); + else if (options_print_order == opt_print_order::sorted_long_first) + return (b_s && a_l); + return a < b; + } }; /* tag::reference[] @@ -1175,6 +1068,8 @@ inline const option_style & option_style::windows() #endif +#include <cstddef> +#include <iterator> #include <string> #include <vector> @@ -1366,7 +1261,11 @@ class token_iterator token value() const { - if (has_option_prefix() && has_value_delimiter()) + if (has_short_option_prefix() + && (args_i->find_first_of(style.value_delimiters) + == (style.short_option_size + 1))) + return token(token_type::argument, args_i->substr(3)); + else if (has_long_option_prefix() && has_value_delimiter()) return token(token_type::argument, args_i->substr( args_i->find_first_of(style.value_delimiters) + 1)); @@ -1437,48 +1336,293 @@ class token_iterator #endif -#ifndef LYRA_VAL_HPP -#define LYRA_VAL_HPP +#ifndef LYRA_PARSER_HPP +#define LYRA_PARSER_HPP + + +#ifndef LYRA_DETAIL_CHOICES_HPP +#define LYRA_DETAIL_CHOICES_HPP + +#include <initializer_list> +#include <string> +#include <type_traits> +#include <vector> + +namespace lyra { namespace detail { + +/* +Type erased base for set of choices. I.e. it's an "interface". +*/ +struct choices_base +{ + virtual ~choices_base() = default; + virtual parser_result contains_value(std::string const & val) const = 0; +}; + +/* +Stores a set of choice values and provides checking if a given parsed +string value is one of the choices. +*/ +template <typename T> +struct choices_set : choices_base +{ + std::vector<T> values; + + template <typename... Vals> + explicit choices_set(Vals... vals) + : choices_set({ vals... }) + {} + + explicit choices_set(const std::vector<T> & vals) + : values(vals) + {} + + parser_result contains_value(std::string const & val) const override + { + T value; + auto parse = parse_string(val, value); + if (!parse) + { + return parser_result::error( + parser_result_type::no_match, parse.message()); + } + for (const T & allowed_value : values) + { + if (allowed_value == value) + return parser_result::ok(parser_result_type::matched); + } + return parser_result::error(parser_result_type::no_match, + "Value '" + val + + "' not expected. Allowed values are: " + this->to_string()); + } + + std::string to_string() const + { + std::string text; + for (const T & val : values) + { + if (!text.empty()) text += ", "; + std::string val_string; + if (detail::to_string(val, val_string)) + text += val_string; + else + text += "<value error>"; + } + return text; + } + + protected: + explicit choices_set(std::initializer_list<T> const & vals) + : values(vals) + {} +}; + +template <> +struct choices_set<const char *> : choices_set<std::string> +{ + template <typename... Vals> + explicit choices_set(Vals... vals) + : choices_set<std::string>(vals...) + {} +}; + +/* +Calls a designated function to check if the choice is valid. +*/ +template <typename Lambda> +struct choices_check : choices_base +{ + static_assert(unary_lambda_traits<Lambda>::isValid, + "Supplied lambda must take exactly one argument"); + static_assert(std::is_same<bool, + typename unary_lambda_traits<Lambda>::ReturnType>::value, + "Supplied lambda must return bool"); + + Lambda checker; + using value_type = typename unary_lambda_traits<Lambda>::ArgType; + + explicit choices_check(Lambda const & checker_function) + : checker(checker_function) + {} + + parser_result contains_value(std::string const & val) const override + { + value_type value; + auto parse = parse_string(val, value); + if (!parse) + { + return parser_result::error( + parser_result_type::no_match, parse.message()); + } + if (checker(value)) + { + return parser_result::ok(parser_result_type::matched); + } + return parser_result::error( + parser_result_type::no_match, "Value '" + val + "' not expected."); + } +}; + +}} // namespace lyra::detail +#endif + +#ifndef LYRA_PRINTER_HPP +#define LYRA_PRINTER_HPP + + +#include <cstddef> #include <memory> +#include <ostream> +#include <string> namespace lyra { /* tag::reference[] -[#lyra_val] -= `lyra::val` +[#lyra_printer] += `lyra::printer` + +A `printer` is an interface to manage formatting for output. Mostly the output +is for the help text of lyra cli parsers. The interface abstracts away specifics +of the output device and any visual arrangement, i.e. padding, coloring, etc. [source] ---- -auto val(T && v); -auto val(const char * v); +virtual printer & printer::heading( + const option_style & style, + const std::string & txt) = 0; +virtual printer & printer::paragraph( + const option_style & style, + const std::string & txt) = 0; +virtual printer & printer::option( + const option_style & style, + const std::string & opt, + const std::string & description) = 0; ---- -Makes a bound self-contained value of the type of the given r-value. The created -bound values can be used in place of the value references for arguments. And can -be retrieved with the -<<lyra_cli_array_ref>> call. +Indenting levels is implemented at the base and the indent is available for +concrete implementations to apply as needed. -*/ // end::reference[] -template <typename T> -detail::BoundVal<T> val(T && v) +[source] +---- +virtual printer & indent(int levels = 1); +virtual printer & dedent(int levels = 1); +virtual int get_indent_level() const; +---- + +You can customize the printing output by implementing a subclass of +`lyra::printer` and implementing a corresponding `make_printer` factory +function which matches the output to the printer. For example: + +[source] +---- +inline std::unique_ptr<my_printer> make_printer(my_output & os_) { - return detail::BoundVal<T>(std::forward<T>(v)); + return std::unique_ptr<my_printer>(new my_output(os_)); } +---- -inline detail::BoundVal<std::string> val(const char * v) +*/ // end::reference[] +class printer { - return detail::BoundVal<std::string>(v); + public: + virtual ~printer() = default; + virtual printer & heading( + const option_style & style, const std::string & txt) + = 0; + virtual printer & paragraph( + const option_style & style, const std::string & txt) + = 0; + virtual printer & option(const option_style & style, + const std::string & opt, + const std::string & description) + = 0; + virtual printer & indent(int levels = 1) + { + indent_level += levels; + return *this; + } + virtual printer & dedent(int levels = 1) + { + indent_level -= levels; + return *this; + } + virtual int get_indent_level() const + { + if (indent_level < 0) return 0; + return indent_level; + } + + protected: + int indent_level = 0; +}; + +/* tag::reference[] + +[#lyra_ostream_printer] += `lyra::ostream_printer` + +A <<lyra_printer>> that uses `std::ostream` for output. This is the one used in +the case of printing to the standard output channels of `std::cout` and +`std::cerr`. + +*/ // end::reference[] +class ostream_printer : public printer +{ + public: + explicit ostream_printer(std::ostream & os_) + : os(os_) + {} + printer & heading(const option_style &, const std::string & txt) override + { + os << txt << "\n"; + return *this; + } + printer & paragraph( + const option_style & style, const std::string & txt) override + { + const std::string indent_str( + get_indent_level() * style.indent_size, ' '); + os << indent_str << txt << "\n\n"; + return *this; + } + printer & option(const option_style & style, + const std::string & opt, + const std::string & description) override + { + const std::string indent_str( + get_indent_level() * style.indent_size, ' '); + const std::string opt_pad( + 26 - get_indent_level() * style.indent_size - 1, ' '); + if (opt.size() > opt_pad.size()) + os << indent_str << opt << "\n" + << indent_str << opt_pad << " " << description << "\n"; + else + os << indent_str << opt + << opt_pad.substr(0, opt_pad.size() - opt.size()) << " " + << description << "\n"; + return *this; + } + + protected: + std::ostream & os; +}; + +inline std::unique_ptr<printer> make_printer(std::ostream & os_) +{ + return std::unique_ptr<printer>(new ostream_printer(os_)); } } // namespace lyra #endif +#include <algorithm> +#include <cstddef> #include <memory> +#include <numeric> #include <string> -#include <type_traits> namespace lyra { @@ -1489,7 +1633,7 @@ class parse_state public: parse_state(parser_result_type type, token_iterator const & remaining_tokens, - size_t parsed_tokens = 0) + std::size_t parsed_tokens = 0) : result_type(type) , tokens(remaining_tokens) { @@ -1507,12 +1651,12 @@ class parse_state struct parser_cardinality { - size_t minimum = 0; - size_t maximum = 0; + std::size_t minimum = 0; + std::size_t maximum = 0; parser_cardinality() = default; - parser_cardinality(size_t a, size_t b) + parser_cardinality(std::size_t a, std::size_t b) : minimum(a) , maximum(b) {} @@ -1530,21 +1674,40 @@ struct parser_cardinality minimum = 0; maximum = 1; } - void required(size_t n = 1) + void required(std::size_t n = 1) { minimum = n; maximum = n; } - void counted(size_t n) + void counted(std::size_t n) { minimum = n; maximum = n; } - void bounded(size_t n, size_t m) + void bounded(std::size_t n, std::size_t m) { minimum = n; maximum = m; } + + bool includes(std::size_t v) const + { + return is_bounded() && (minimum <= v) && (v <= maximum); + } + + bool is_maximum(std::size_t v) const + { + return is_bounded() && v == maximum; + } +}; + +enum class ctor_lambda_e : char +{ + val +}; +enum class ctor_ref_e : char +{ + val }; } // namespace detail @@ -1581,19 +1744,6 @@ end::reference[] */ class parser { public: - struct help_text_item - { - std::string option; - std::string description; - }; - - using help_text = std::vector<help_text_item>; - - [[deprecated]] help_text get_help_text() const { return {}; } - [[deprecated]] std::string get_usage_text() const { return ""; } - [[deprecated]] std::string get_description_text() const { return ""; } - - virtual help_text get_help_text(const option_style &) const { return {}; } virtual std::string get_usage_text(const option_style &) const { return ""; @@ -1620,40 +1770,71 @@ class parser if (is_named(n)) return this; return nullptr; } - virtual size_t get_value_count() const { return 0; } - virtual std::string get_value(size_t i) const + virtual std::size_t get_value_count() const { return 0; } + virtual std::string get_value(std::size_t i) const { (void)i; return ""; } - virtual parse_result parse(detail::token_iterator const & tokens, - const option_style & style) const = 0; + virtual parse_result parse( + detail::token_iterator const & tokens, const option_style & style) const + = 0; + + virtual std::string get_print_order_key(const option_style &) const + { + return ""; + } + + virtual void print_help_text_details(printer &, const option_style &) const + {} protected: - void print_help_text(std::ostream & os, const option_style & style) const + virtual void print_help_text(printer & p, const option_style & style) const + { + print_help_text_summary(p, style); + p.heading(style, "OPTIONS, ARGUMENTS:"); + p.indent(); + print_help_text_details(p, style); + p.dedent(); + } + + virtual void print_help_text_summary( + printer & p, const option_style & style) const { - std::string usage_test = get_usage_text(style); - if (!usage_test.empty()) - os << "USAGE:\n" - << " " << get_usage_text(style) << "\n\n"; + std::string usage_text = get_usage_text(style); + if (!usage_text.empty()) + p.heading(style, "USAGE:") + .indent() + .paragraph(style, usage_text) + .dedent(); - std::string description_test = get_description_text(style); - if (!description_test.empty()) - os << get_description_text(style) << "\n"; + std::string description_text = get_description_text(style); + if (!description_text.empty()) p.paragraph(style, description_text); + } - os << "OPTIONS, ARGUMENTS:\n"; - const std::string::size_type left_col_size = 26 - 3; - const std::string left_pad(left_col_size, ' '); - for (auto const & cols : get_help_text(style)) + template <typename I, typename F> + void for_each_print_ordered_parser( + const option_style & style, I b, I e, F f) const + { + if (style.options_print_order + != option_style::opt_print_order::per_declaration) { - if (cols.option.size() > left_pad.size()) - os << " " << cols.option << "\n " << left_pad << " " - << cols.description << "\n"; - else - os << " " << cols.option - << left_pad.substr(0, left_pad.size() - cols.option.size()) - << " " << cols.description << "\n"; + std::vector<std::size_t> order_index(std::distance(b, e)); + std::iota(order_index.begin(), order_index.end(), 0); + std::stable_sort(order_index.begin(), order_index.end(), + [&](std::size_t i, std::size_t j) { + const parser & pa = **(b + i); + const parser & pb = **(b + j); + return style.opt_print_order_less( + pa.get_print_order_key(style), + pb.get_print_order_key(style)); + }); + for (auto i : order_index) f(style, **(b + i)); + } + else + { + while (b != e) f(style, **(b++)); } } }; @@ -1663,47 +1844,6 @@ class parser [#lyra_parser_specification] == Specification -[#lyra_parser_help_text_item] -=== `lyra::parser::help_text_item` - -[source] ----- -struct lyra::parser::help_text_item -{ - std::string option; - std::string description; -}; ----- - -Holds the help information for a single argument option. The `option` member is -the long name of the option. And the `description` is the text describing the -option. A list of them is returned from the `lyra::parser::get_help_text` -method. - -[#lyra_parser_help_text] -=== `lyra::parser::help_text` - -[source] ----- -using help_text = std::vector<help_text_item>; ----- - -The set of help texts for any options in the sub-parsers to this one, if any. - -[#lyra_parser_get_help_text] -=== `lyra::parser::get_help_text` - -[source] ----- -virtual help_text get_help_text(const option_style &) const; ----- - -Collects, and returns, the set of help items for the sub-parser arguments in -this parser, if any. The default is to return an empty set. Which is what most -parsers will return. Parsers like `arguments`, `group`, and `cli` will return a -set for the arguments defined. This is called to print out the help text from -the stream operator. - [#lyra_parser_get_usage_text] === `lyra::parser::get_usage_text` @@ -1782,23 +1922,36 @@ class bound_parser : public composable_parser<Derived> m_cardinality = { 0, 0 }; else m_cardinality = { 0, 1 }; + if (!m_ref->isFlag() && !m_ref->isContainer() + && m_ref->get_value_count() == 1) + { + auto value_zero = m_ref->get_value(0); + if (!value_zero.empty()) + m_description = "[default: " + value_zero + "]"; + } } public: - enum class ctor_lambda_e - { - val - }; - template <typename Reference> - bound_parser(Reference & ref, std::string const & hint); + bound_parser(Reference & ref, + std::string const & hint, + typename std::enable_if<!detail::is_invocable<Reference>::value, + detail::ctor_ref_e>::type + = detail::ctor_ref_e::val); template <typename Lambda> bound_parser(Lambda const & ref, std::string const & hint, typename std::enable_if<detail::is_invocable<Lambda>::value, - ctor_lambda_e>::type - = ctor_lambda_e::val); + detail::ctor_lambda_e>::type + = detail::ctor_lambda_e::val); + + template <typename Lambda> + bound_parser(Lambda && ref, + std::string const & hint, + typename std::enable_if<detail::is_invocable<Lambda>::value, + detail::ctor_lambda_e>::type + = detail::ctor_lambda_e::val); template <typename T> explicit bound_parser(detail::BoundVal<T> && val) @@ -1812,9 +1965,9 @@ class bound_parser : public composable_parser<Derived> Derived & help(const std::string & text); Derived & operator()(std::string const & description); Derived & optional(); - Derived & required(size_t n = 1); - Derived & cardinality(size_t n); - Derived & cardinality(size_t n, size_t m); + Derived & required(std::size_t n = 1); + Derived & cardinality(std::size_t n); + Derived & cardinality(std::size_t n, std::size_t m); detail::parser_cardinality cardinality() const override { return m_cardinality; @@ -1830,23 +1983,22 @@ class bound_parser : public composable_parser<Derived> typename std::enable_if<detail::is_invocable<Lambda>::value, int>::type = 1> Derived & choices(Lambda const & check_choice); - template <typename T, std::size_t N> + template <typename T, + std::size_t N, + typename std::enable_if<!detail::is_character<T>::value, int>::type = 2> Derived & choices(const T (&choice_values)[N]); - virtual std::unique_ptr<parser> clone() const override + std::unique_ptr<parser> clone() const override { return make_clone<Derived>(this); } - virtual bool is_named(const std::string & n) const override - { - return n == m_hint; - } - virtual size_t get_value_count() const override + bool is_named(const std::string & n) const override { return n == m_hint; } + std::size_t get_value_count() const override { return m_ref->get_value_count(); } - virtual std::string get_value(size_t i) const override + std::string get_value(std::size_t i) const override { return m_ref->get_value(i); } @@ -1869,6 +2021,10 @@ bound_parser<Derived>::bound_parser(Reference& ref, std::string const& hint); template <typename Derived> template <typename Lambda> bound_parser<Derived>::bound_parser(Lambda const& ref, std::string const& hint); + +template <typename Derived> +template <typename Lambda> +bound_parser<Derived>::bound_parser(Lambda && ref, std::string const& hint); ---- Constructs a value option with a target typed variable or callback. These are @@ -1883,9 +2039,12 @@ contain all the specified values. end::reference[] */ template <typename Derived> template <typename Reference> -bound_parser<Derived>::bound_parser(Reference & ref, std::string const & hint) +bound_parser<Derived>::bound_parser(Reference & ref, + std::string const & hint, + typename std::enable_if<!detail::is_invocable<Reference>::value, + detail::ctor_ref_e>::type) : bound_parser( - std::make_shared<detail::BoundValueRef<Reference>>(ref), hint) + std::make_shared<detail::BoundValueRef<Reference>>(ref), hint) {} template <typename Derived> @@ -1893,8 +2052,25 @@ template <typename Lambda> bound_parser<Derived>::bound_parser(Lambda const & ref, std::string const & hint, typename std::enable_if<detail::is_invocable<Lambda>::value, - ctor_lambda_e>::type) - : bound_parser(std::make_shared<detail::BoundLambda<Lambda>>(ref), hint) + detail::ctor_lambda_e>::type) + : bound_parser( + std::make_shared< + detail::BoundLambda<typename detail::remove_cvref<Lambda>::type>>( + ref), + hint) +{} + +template <typename Derived> +template <typename Lambda> +bound_parser<Derived>::bound_parser(Lambda && ref, + std::string const & hint, + typename std::enable_if<detail::is_invocable<Lambda>::value, + detail::ctor_lambda_e>::type) + : bound_parser( + std::make_shared< + detail::BoundLambda<typename detail::remove_cvref<Lambda>::type>>( + std::move(ref)), + hint) {} /* tag::reference[] @@ -1914,7 +2090,8 @@ end::reference[] */ template <typename Derived> Derived& bound_parser<Derived>::help(std::string const& help_description_text); template <typename Derived> -Derived& bound_parser<Derived>::operator()(std::string const& help_description_text); +Derived& bound_parser<Derived>::operator()(std::string const& +help_description_text); ---- Defines the help description of an argument. @@ -1923,11 +2100,13 @@ end::reference[] */ template <typename Derived> Derived & bound_parser<Derived>::help(const std::string & help_description_text) { - m_description = help_description_text; + m_description = help_description_text + + (m_description.empty() ? "" : (" " + m_description)); return static_cast<Derived &>(*this); } template <typename Derived> -Derived & bound_parser<Derived>::operator()(std::string const & help_description_text) +Derived & bound_parser<Derived>::operator()( + std::string const & help_description_text) { return this->help(help_description_text); } @@ -1961,7 +2140,7 @@ Derived & bound_parser<Derived>::optional() [source] ---- template <typename Derived> -Derived& bound_parser<Derived>::required(size_t n); +Derived& bound_parser<Derived>::required(std::size_t n); ---- Specifies that the argument needs to given the number of `n` times @@ -1969,7 +2148,7 @@ Specifies that the argument needs to given the number of `n` times end::reference[] */ template <typename Derived> -Derived & bound_parser<Derived>::required(size_t n) +Derived & bound_parser<Derived>::required(std::size_t n) { if (m_ref->isContainer()) return this->cardinality(1, 0); @@ -1985,10 +2164,10 @@ Derived & bound_parser<Derived>::required(size_t n) [source] ---- template <typename Derived> -Derived& bound_parser<Derived>::cardinality(size_t n); +Derived& bound_parser<Derived>::cardinality(std::size_t n); template <typename Derived> -Derived& bound_parser<Derived>::cardinality(size_t n, size_t m); +Derived& bound_parser<Derived>::cardinality(std::size_t n, std::size_t m); ---- Specifies the number of times the argument can and needs to appear in the list @@ -1998,14 +2177,14 @@ inclusive. end::reference[] */ template <typename Derived> -Derived & bound_parser<Derived>::cardinality(size_t n) +Derived & bound_parser<Derived>::cardinality(std::size_t n) { m_cardinality = { n, n }; return static_cast<Derived &>(*this); } template <typename Derived> -Derived & bound_parser<Derived>::cardinality(size_t n, size_t m) +Derived & bound_parser<Derived>::cardinality(std::size_t n, std::size_t m) { m_cardinality = { n, m }; return static_cast<Derived &>(*this); @@ -2054,7 +2233,9 @@ Derived & bound_parser<Derived>::choices(Lambda const & check_choice) } template <typename Derived> -template <typename T, std::size_t N> +template <typename T, + std::size_t N, + typename std::enable_if<!detail::is_character<T>::value, int>::type> Derived & bound_parser<Derived>::choices(const T (&choice_values)[N]) { value_choices = std::make_shared<detail::choices_set<T>>( @@ -2099,6 +2280,9 @@ Derived & bound_parser<Derived>::hint(std::string const & hint) #endif +#include <cstddef> +#include <string> + namespace lyra { /* tag::reference[] @@ -2115,37 +2299,43 @@ Is-a <<lyra_bound_parser>>. class arg : public bound_parser<arg> { public: - using bound_parser::bound_parser; + template <typename Reference> + arg(Reference & ref, std::string const & hint) + : bound_parser(ref, hint) + {} + template <typename Lambda> + arg(Lambda const & ref, std::string const & hint) + : bound_parser(ref, hint) + {} + template <typename Lambda> + arg(Lambda && ref, std::string const & hint) + : bound_parser(std::move(ref), hint) + {} - virtual std::string get_usage_text(const option_style &) const override + std::string get_usage_text(const option_style &) const override { - std::ostringstream oss; + std::string text; if (!m_hint.empty()) { auto c = cardinality(); if (c.is_required()) { - for (size_t i = 0; i < c.minimum; ++i) - oss << (i > 0 ? " " : "") << "<" << m_hint << ">"; + for (std::size_t i = 0; i < c.minimum; ++i) + (((text += (i > 0 ? " " : "")) += "<") += m_hint) += ">"; if (c.is_unbounded()) - oss << (c.is_required() ? " " : "") << "[<" << m_hint - << ">...]"; + (((text += (c.is_required() ? " " : "")) += "[<") += m_hint) + += ">...]"; } else if (c.is_unbounded()) { - oss << "[<" << m_hint << ">...]"; + ((text += "[<") += m_hint) += ">...]"; } else { - oss << "<" << m_hint << ">"; + ((text += "<") += m_hint) += ">"; } } - return oss.str(); - } - - virtual help_text get_help_text(const option_style & style) const override - { - return { { get_usage_text(style), m_description } }; + return text; } using parser::parse; @@ -2175,7 +2365,8 @@ class arg : public bound_parser<arg> { LYRA_PRINT_DEBUG( "(!)", get_usage_text(style), "!=", token.name); - return parse_result(choice_result); + return parse_result::ok( + detail::parse_state(parser_result_type::no_match, tokens)); } } @@ -2194,67 +2385,23 @@ class arg : public bound_parser<arg> parser_result_type::matched, remainingTokens)); } } -}; - -} // namespace lyra - -#endif - -#ifndef LYRA_ARGUMENTS_HPP -#define LYRA_ARGUMENTS_HPP - -#ifndef LYRA_EXE_NAME_HPP -#define LYRA_EXE_NAME_HPP - - -namespace lyra { - -/* tag::reference[] - -[#lyra_exe_name] -= `lyra::exe_name` - -Specifies the name of the executable. - -Is-a <<lyra_composable_parser>>. - -end::reference[] */ -class exe_name : public composable_parser<exe_name> -{ - public: - exe_name() - : m_name(std::make_shared<std::string>("<executable>")) - {} - - explicit exe_name(std::string & ref); - - template <typename LambdaT> - explicit exe_name(LambdaT const & lambda); - - std::string name() const; - parser_result set(std::string const & newName); - - virtual parse_result parse(detail::token_iterator const & tokens, - const option_style &) const override + protected: + std::string get_print_order_key(const option_style &) const override { - return parse_result::ok( - detail::parse_state(parser_result_type::no_match, tokens)); + return this->hint(); } - virtual std::unique_ptr<parser> clone() const override + void print_help_text_details( + printer & p, const option_style & style) const override { - return make_clone<exe_name>(this); + p.option(style, get_usage_text(style), m_description); } - - private: - std::shared_ptr<std::string> m_name; - std::shared_ptr<detail::BoundValueRefBase> m_ref; }; /* tag::reference[] -[#lyra_exe_name_ctor] +[#lyra_arg_ctor] == Construction end::reference[] */ @@ -2262,94 +2409,40 @@ end::reference[] */ /* tag::reference[] [source] ---- -exe_name::exe_name(std::string& ref) ----- - -Constructs with a target string to receive the name of the executable. When -the `cli` is run the target string will contain the exec name. - -end::reference[] */ -inline exe_name::exe_name(std::string & ref) - : exe_name() -{ - m_ref = std::make_shared<detail::BoundValueRef<std::string>>(ref); -} - -/* tag::reference[] -[source] ----- -template <typename LambdaT> -exe_name::exe_name(LambdaT const& lambda) ----- - -Construct with a callback that is called with the value of the executable name -when the `cli` runs. - -end::reference[] */ -template <typename LambdaT> -exe_name::exe_name(LambdaT const & lambda) - : exe_name() -{ - m_ref = std::make_shared<detail::BoundLambda<LambdaT>>(lambda); -} - -/* tag::reference[] - -[#lyra_exe_name_accessors] -== Accessors - -end::reference[] */ - -/* tag::reference[] - -[#lyra_exe_name_name] -=== `lyra::exe_name::name` - -[source] ----- -std::string exe_name::name() const ----- - -Returns the executable name when available. Otherwise it returns a default -value. - -end::reference[] */ -inline std::string exe_name::name() const { return *m_name; } - -/* tag::reference[] +template <typename Reference> +arg<Derived>::arg(Reference& ref, std::string const& hint); -[#lyra_exe_name_set] -=== `lyra::exe_name::set` +template <typename Lambda> +arg<Derived>::arg(Lambda const& ref, std::string const& hint); -[source] ----- -parser_result exe_name::set(std::string const& newName) +template <typename Lambda> +arg<Derived>::arg(Lambda && ref, std::string const& hint); ---- -Sets the executable name with the `newName` value. The new value is reflected -in the bound string reference or callback. +Constructs a value argument with a target typed variable or callback. These are +bare arguments that take a value as in `value`. In the first form the given +`ref` receives the value of the argument after parsing. The second form the +callback is invoked during the parse with the given value. Both take a +`hint` that is used in the help text. When the argument can be specified +multiple times the callback will be called consecutively for each argument value +given. And if a container is given as a reference on the first form it will +contain all the specified values. end::reference[] */ -inline parser_result exe_name::set(std::string const & newName) -{ - auto lastSlash = newName.find_last_of("\\/"); - auto filename = (lastSlash == std::string::npos) - ? newName - : newName.substr(lastSlash + 1); - - *m_name = filename; - if (m_ref) - return m_ref->setValue(filename); - else - return parser_result::ok(parser_result_type::matched); -} } // namespace lyra #endif -#include <functional> -#include <sstream> +#ifndef LYRA_ARGUMENTS_HPP +#define LYRA_ARGUMENTS_HPP + + +#include <cstddef> +#include <memory> +#include <string> +#include <type_traits> +#include <vector> namespace lyra { @@ -2358,8 +2451,8 @@ namespace lyra { [#lyra_arguments] = `lyra::arguments` -A Combined parser made up of any number of parsers. Creating and using -one of these as a basis one can incrementally compose other parsers into this +A combined parser made up of any number of sub-parsers. Creating and using +one of these as a basis, one can incrementally compose other parsers into this one. For example: [source] @@ -2378,10 +2471,11 @@ p.add_argument(lyra::opt(where, "where").name("--where") class arguments : public parser { public: - enum evaluation + enum evaluation : char { - any = 0, - sequence = 1 + eval_any = 0, + eval_sequence = 1, + eval_relaxed = 2, }; arguments() = default; @@ -2398,73 +2492,76 @@ class arguments : public parser arguments & add_argument(arguments const & other); arguments & operator|=(arguments const & other); - template <typename T> - arguments operator|(T const & other) const + template <typename T, typename U> + friend typename std::enable_if< + std::is_base_of<arguments, + typename detail::remove_cvref<T>::type>::value, + T &>::type + operator|(T & self, U const & other) + { + return static_cast<T &>(self.add_argument(other)); + } + template <typename T, typename U> + friend typename std::enable_if< + std::is_base_of<arguments, + typename detail::remove_cvref<T>::type>::value, + T &>::type + operator|(T && self, U const & other) { - return arguments(*this) |= other; + return static_cast<T &>(self.add_argument(other)); } arguments & sequential(); arguments & inclusive(); + arguments & relaxed(); + + arguments & require(std::size_t n, std::size_t m = 0); template <typename T> - T & get(size_t i); + T & get(std::size_t i); - virtual std::string get_usage_text( - const option_style & style) const override + std::string get_usage_text(const option_style & style) const override { - std::ostringstream os; + std::string text; for (auto const & p : parsers) { std::string usage_text = p->get_usage_text(style); if (usage_text.size() > 0) { - if (os.tellp() != std::ostringstream::pos_type(0)) os << " "; + if (!text.empty()) text += " "; if (p->is_group() && p->is_optional()) - os << "[ " << usage_text << " ]"; + ((text += "[ ") += usage_text) += " ]"; else if (p->is_group()) - os << "{ " << usage_text << " }"; + ((text += "{ ") += usage_text) += " }"; else if (p->is_optional()) - os << "[" << usage_text << "]"; + ((text += "[") += usage_text) += "]"; else - os << usage_text; + text += usage_text; } } - return os.str(); + return text; } - virtual std::string get_description_text( - const option_style & style) const override + std::string get_description_text(const option_style & style) const override { - std::ostringstream os; + std::string text; for (auto const & p : parsers) { if (p->is_group()) continue; auto child_description = p->get_description_text(style); - if (!child_description.empty()) os << child_description << "\n"; - } - return os.str(); - } - - virtual help_text get_help_text(const option_style & style) const override - { - help_text text; - for (auto const & p : parsers) - { - if (p->is_group()) text.push_back({ "", "" }); - auto child_help = p->get_help_text(style); - text.insert(text.end(), child_help.begin(), child_help.end()); + if (!child_description.empty()) + { + if (!text.empty()) text += "\n"; + text += child_description; + } } return text; } - virtual detail::parser_cardinality cardinality() const override - { - return { 0, 0 }; - } + detail::parser_cardinality cardinality() const override { return { 0, 0 }; } - virtual result validate() const override + result validate() const override { for (auto const & p : parsers) { @@ -2479,93 +2576,133 @@ class arguments : public parser { switch (eval_mode) { - case any: return parse_any(tokens, style); - case sequence: return parse_sequence(tokens, style); + case eval_any: + case eval_relaxed: return parse_any(tokens, style); + case eval_sequence: return parse_sequence(tokens, style); } return parse_result::error( detail::parse_state(parser_result_type::no_match, tokens), - "Unknown evaluation mode; not one of 'any', or 'sequence'."); + "Unknown evaluation mode; not one of 'any', 'sequence', or 'relaxed'."); } parse_result parse_any( detail::token_iterator const & tokens, const option_style & style) const { LYRA_PRINT_SCOPE("arguments::parse_any"); - LYRA_PRINT_DEBUG("(?)", get_usage_text(style), - "?=", tokens ? tokens.argument().name : "", ".."); - struct ParserInfo - { - parser const * parser_p = nullptr; - size_t count = 0; - }; - std::vector<ParserInfo> parser_info(parsers.size()); - { - size_t i = 0; - for (auto const & p : parsers) parser_info[i++].parser_p = p.get(); - } - - auto p_result = parse_result::ok( - detail::parse_state(parser_result_type::matched, tokens)); - auto error_result = parse_result::ok( + std::vector<std::size_t> parsing_count(parsers.size(), 0); + std::size_t parsed_total = 0; + auto parsing_result = parse_result::ok( + detail::parse_state(parser_result_type::empty_match, tokens)); + auto nomatch_result = parse_result::ok( detail::parse_state(parser_result_type::no_match, tokens)); - while (p_result.value().remainingTokens()) + + while (parsing_result.value().remainingTokens() + && !parse_limit.is_maximum(parsed_total)) { + LYRA_PRINT_DEBUG("(?)", get_usage_text(style), "?=", + parsing_result.value().remainingTokens() + ? parsing_result.value().remainingTokens().argument().name + : "", + ".."); bool token_parsed = false; - for (auto & parse_info : parser_info) + auto parsing_count_i = parsing_count.begin(); + for (auto & p : parsers) { - auto parser_cardinality = parse_info.parser_p->cardinality(); + auto parser_cardinality = p->cardinality(); if (parser_cardinality.is_unbounded() - || parse_info.count < parser_cardinality.maximum) + || *parsing_count_i < parser_cardinality.maximum) { - auto subparse_result = parse_info.parser_p->parse( - p_result.value().remainingTokens(), style); + auto subparse_result = p->parse( + parsing_result.value().remainingTokens(), style); if (!subparse_result) { LYRA_PRINT_DEBUG("(!)", get_usage_text(style), "!=", - p_result.value().remainingTokens().argument().name); + parsing_result.value() + .remainingTokens() + .argument() + .name); if (subparse_result.has_value() && subparse_result.value().type() == parser_result_type::short_circuit_all) return subparse_result; - if (error_result) - error_result = parse_result(subparse_result); + else if (nomatch_result) + nomatch_result = parse_result(subparse_result); } else if (subparse_result && subparse_result.value().type() - != parser_result_type::no_match) + == parser_result_type::empty_match) { LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", - p_result.value().remainingTokens().argument().name, + parsing_result.value() + .remainingTokens() + .argument() + .name, + "==>", subparse_result.value().type()); + parsing_result = parse_result(subparse_result); + } + else if (subparse_result + && subparse_result.value().type() + != parser_result_type::no_match) + { + LYRA_PRINT_DEBUG("(=) #", parsed_total + 1, + get_usage_text(style), "==", + parsing_result.value() + .remainingTokens() + .argument() + .name, "==>", subparse_result.value().type()); - p_result = parse_result(subparse_result); + parsing_result = parse_result(subparse_result); token_parsed = true; - parse_info.count += 1; + *parsing_count_i += 1; + parsed_total += 1; break; } } + ++parsing_count_i; } - if (p_result.value().type() == parser_result_type::short_circuit_all) - return p_result; - if (!token_parsed && !error_result) return error_result; - if (!token_parsed) break; + if (parsing_result.value().type() + == parser_result_type::short_circuit_all) + return parsing_result; + if (!token_parsed) + { + if (eval_mode == eval_relaxed) + { + LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", + parsing_result.value() + .remainingTokens() + .argument() + .name, + "==> skipped"); + auto remainingTokens + = parsing_result.value().remainingTokens(); + remainingTokens.pop(remainingTokens.argument()); + parsing_result = parse_result::ok(detail::parse_state( + parser_result_type::empty_match, remainingTokens)); + } + else if (!nomatch_result) + return nomatch_result; + else + break; + } } - for (auto & parseInfo : parser_info) { - auto parser_cardinality = parseInfo.parser_p->cardinality(); - if ((parser_cardinality.is_bounded() - && (parseInfo.count < parser_cardinality.minimum - || parser_cardinality.maximum < parseInfo.count)) - || (parser_cardinality.is_required() - && (parseInfo.count < parser_cardinality.minimum))) + auto parsing_count_i = parsing_count.begin(); + for (auto & p : parsers) { - return parse_result::error(p_result.value(), - "Expected: " + parseInfo.parser_p->get_usage_text(style)); + auto parser_cardinality = p->cardinality(); + if ((parser_cardinality.is_bounded() + && (*parsing_count_i < parser_cardinality.minimum + || parser_cardinality.maximum < *parsing_count_i)) + || (parser_cardinality.is_required() + && (*parsing_count_i < parser_cardinality.minimum))) + return make_parse_error(tokens, *p, parsing_result, style); + ++parsing_count_i; } } - return p_result; + return parsing_result; } parse_result parse_sequence( @@ -2575,79 +2712,95 @@ class arguments : public parser LYRA_PRINT_DEBUG("(?)", get_usage_text(style), "?=", tokens ? tokens.argument().name : "", ".."); - struct ParserInfo - { - parser const * parser_p = nullptr; - size_t count = 0; - }; - std::vector<ParserInfo> parser_info(parsers.size()); - { - size_t i = 0; - for (auto const & p : parsers) parser_info[i++].parser_p = p.get(); - } - + std::vector<std::size_t> parsing_count(parsers.size(), 0); auto p_result = parse_result::ok( - detail::parse_state(parser_result_type::matched, tokens)); + detail::parse_state(parser_result_type::empty_match, tokens)); - for (std::size_t parser_i = 0; parser_i < parsers.size(); ++parser_i) + auto parsing_count_i = parsing_count.begin(); + for (auto & p : parsers) { - auto & parse_info = parser_info[parser_i]; - auto parser_cardinality = parse_info.parser_p->cardinality(); + auto parser_cardinality = p->cardinality(); do { - auto subresult = parse_info.parser_p->parse( - p_result.value().remainingTokens(), style); - if (!subresult) + auto subresult + = p->parse(p_result.value().remainingTokens(), style); + if (subresult.has_value() + && parser_result_type::short_circuit_all + == subresult.value().type()) + return subresult; + if (!subresult) break; + if (parser_result_type::no_match == subresult.value().type()) { + LYRA_PRINT_DEBUG("(!)", get_usage_text(style), "!=", + p_result.value().remainingTokens() + ? p_result.value().remainingTokens().argument().name + : "", + "==>", subresult.value().type()); break; } - if (subresult.value().type() - == parser_result_type::short_circuit_all) + if (parser_result_type::matched == subresult.value().type()) { - return subresult; + LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", + p_result.value().remainingTokens() + ? p_result.value().remainingTokens().argument().name + : "", + "==>", subresult.value().type()); + *parsing_count_i += 1; + p_result = subresult; } - if (subresult.value().type() != parser_result_type::no_match) + if (parser_result_type::empty_match == subresult.value().type()) { LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", p_result.value().remainingTokens() ? p_result.value().remainingTokens().argument().name : "", "==>", subresult.value().type()); - p_result = subresult; - parse_info.count += 1; + *parsing_count_i += 1; } } while (p_result.value().have_tokens() && (parser_cardinality.is_unbounded() - || parse_info.count < parser_cardinality.maximum)); + || *parsing_count_i < parser_cardinality.maximum)); if ((parser_cardinality.is_bounded() - && (parse_info.count < parser_cardinality.minimum - || parser_cardinality.maximum < parse_info.count)) + && (*parsing_count_i < parser_cardinality.minimum + || parser_cardinality.maximum < *parsing_count_i)) || (parser_cardinality.is_required() - && (parse_info.count < parser_cardinality.minimum))) - { - return parse_result::error(p_result.value(), - "Expected: " + parse_info.parser_p->get_usage_text(style)); - } + && (*parsing_count_i < parser_cardinality.minimum))) + return make_parse_error(tokens, *p, p_result, style); + ++parsing_count_i; } return p_result; } - virtual std::unique_ptr<parser> clone() const override + template <typename R> + parse_result make_parse_error(const detail::token_iterator & tokens, + const parser & p, + const R & p_result, + const option_style & style) const + { + if (tokens) + return parse_result::error(p_result.value(), + "Unrecognized argument '" + tokens.argument().name + + "' while parsing: " + p.get_usage_text(style)); + else + return parse_result::error( + p_result.value(), "Expected: " + p.get_usage_text(style)); + } + + std::unique_ptr<parser> clone() const override { return make_clone<arguments>(this); } - friend std::ostream & operator<<( - std::ostream & os, arguments const & parser) + template <typename T> + T & print_help(T & os) const { - const option_style & s - = parser.opt_style ? *parser.opt_style : option_style::posix(); - parser.print_help_text(os, s); + std::unique_ptr<printer> p = make_printer(os); + this->print_help_text(*p, get_option_style()); return os; } - virtual const parser * get_named(const std::string & n) const override + const parser * get_named(const std::string & n) const override { for (auto & p : parsers) { @@ -2659,10 +2812,30 @@ class arguments : public parser protected: std::shared_ptr<option_style> opt_style; - - private: std::vector<std::unique_ptr<parser>> parsers; - evaluation eval_mode = any; + evaluation eval_mode = eval_any; + detail::parser_cardinality parse_limit = { 0, 0 }; + + option_style get_option_style() const + { + return opt_style ? *opt_style : option_style::posix(); + } + + option_style & ref_option_style() + { + if (!opt_style) + opt_style = std::make_shared<option_style>(option_style::posix()); + return *opt_style; + } + + void print_help_text_details( + printer & p, const option_style & style) const override + { + for_each_print_ordered_parser(style, parsers.begin(), parsers.end(), + [&](const option_style & s, const parser & q) { + q.print_help_text_details(p, s); + }); + } }; /* tag::reference[] @@ -2702,6 +2875,7 @@ inline arguments::arguments(const arguments & other) : parser(other) , opt_style(other.opt_style) , eval_mode(other.eval_mode) + , parse_limit(other.parse_limit) { for (auto & other_parser : other.parsers) { @@ -2718,6 +2892,16 @@ end::reference[] */ /* tag::reference[] +[#lyra_arguments_specification_composition] +=== Composition + +This parser is composed of sub-parsers that consume the arguments as possible. +One can configure how the sub-parsers are used. And depending on the mode +the ordering of the sub-parsers can matter. + +end::reference[] */ + +/* tag::reference[] [#lyra_arguments_add_argument] === `lyra::arguments::add_argument` @@ -2765,7 +2949,17 @@ inline arguments & arguments::operator|=(arguments const & other) } /* tag::reference[] -=== `lyra::arguments::sequential` +[#lyra_arguments_specification_parsemode] +=== Parsing Mode + +The parsing mode controls how the parsing of the added arguments happens. +Depending on how this is specified different parsing algorithms get used to +match parsers with arguments. + +end::reference[] */ + +/* tag::reference[] +==== `lyra::arguments::sequential` [source] ---- @@ -2779,7 +2973,7 @@ This is useful for sub-commands and structured command lines. end::reference[] */ inline arguments & arguments::sequential() { - eval_mode = sequence; + eval_mode = eval_sequence; return *this; } @@ -2798,28 +2992,108 @@ parsers. This means that there is no ordering enforced. end::reference[] */ inline arguments & arguments::inclusive() { - eval_mode = any; + eval_mode = eval_any; + return *this; +} + +/* tag::reference[] +=== `lyra::arguments::relaxed` + +[source] +---- +arguments & arguments::relaxed(); +---- + +Sets the parsing mode for the arguments to "relaxed any". When parsing the +arguments it attempts to match each parsed argument with all the available +parsers. This means that there is no ordering enforced. Unknown, i.e. failed, +parsing are ignored. + +end::reference[] */ +inline arguments & arguments::relaxed() +{ + eval_mode = eval_relaxed; return *this; } /* tag::reference[] +[#lyra_arguments_specification_limits] +=== Limits + +The parsing of sub-parsers can be restrained with limits. The limits can +control how many parsed arguments are required or allowed. + +end::reference[] */ + +/* tag::reference[] +=== `lyra::arguments::require` + +[source] +---- +arguments & arguments::require(std::size_t n, std::size_t m); +---- + +Requires a minimum and/or maximum number of arguments *only* to be successfully +parsed by the sub-parsers to make this collection of arguments valid. Specifying +the minimum or maximum as zero (`0`) indicates it's undefined. I.e. there would +be no minimum or maximum. If a maximum is indicated, as soon as that maximum +is reached the parsing is considered successful and completes. + +WARNING: Side-effects of parsing arguments, even if overall the collection as +a whole fails for not satisfying the required bounds. + +end::reference[] */ +inline arguments & arguments::require(std::size_t n, std::size_t m) +{ + parse_limit.bounded(n, m); + return *this; +} + +/* tag::reference[] +[#lyra_arguments_specification_other] +=== Other + +end::reference[] */ + +/* tag::reference[] === `lyra::arguments::get` [source] ---- template <typename T> -T & arguments::get(size_t i); +T & arguments::get(std::size_t i); ---- -Get a modifyable reference to one of the parsers specified. +Get a modifiable reference to one of the parsers specified. end::reference[] */ template <typename T> -T & arguments::get(size_t i) +T & arguments::get(std::size_t i) { return static_cast<T &>(*parsers.at(i)); } +/* tag::reference[] +=== `lyra::operator<<` + +[source] +---- +template <typename T> +T & operator<<(T & os, arguments const & a); +---- + +Prints the help text for the arguments to the given stream `os`. The `os` stream +is not used directly for printing out. Instead a <<lyra_printer>> object is +created by calling `lyra::make_printer(os)`. This indirection allows one to +customize how the output is generated. + +end::reference[] */ +template <typename T> +T & operator<<(T & os, arguments const & a) +{ + return a.print_help(os); +} + } // namespace lyra #endif @@ -2828,6 +3102,66 @@ T & arguments::get(size_t i) #define LYRA_CLI_HPP +#ifndef LYRA_ARGS_HPP +#define LYRA_ARGS_HPP + +#include <initializer_list> +#include <string> +#include <vector> + +namespace lyra { + +/* tag::reference[] + +[#lyra_args] += `lyra::args` + +Transport for raw args (copied from main args, supplied via init list, or from +a pair of iterators). + +*/ // end::reference[] +class args +{ + public: + args(int argc, char const * const * argv) + : m_exeName((argv && (argc >= 1)) ? argv[0] : "") + , m_args((argv && (argc >= 1)) ? argv + 1 : nullptr, argv + argc) + {} + + args(std::initializer_list<std::string> args_list) + : m_exeName(args_list.size() >= 1 ? *args_list.begin() : "") + , m_args( + args_list.size() >= 1 ? args_list.begin() + 1 : args_list.end(), + args_list.end()) + {} + + template <typename It> + args(const It & start, const It & end) + : m_exeName(start != end ? *start : "") + , m_args(start != end ? start + 1 : end, end) + {} + + std::string exe_name() const { return m_exeName; } + + std::vector<std::string>::const_iterator begin() const + { + return m_args.begin(); + } + + std::vector<std::string>::const_iterator end() const + { + return m_args.end(); + } + + private: + std::string m_exeName; + std::vector<std::string> m_args; +}; + +} // namespace lyra + +#endif + #ifndef LYRA_DETAIL_DEPRECATED_PARSER_CUSTOMIZATION_HPP #define LYRA_DETAIL_DEPRECATED_PARSER_CUSTOMIZATION_HPP @@ -2863,6 +3197,7 @@ struct parser_customization { virtual std::string token_delimiters() const = 0; virtual std::string option_prefix() const = 0; + virtual ~parser_customization() {} }; /* tag::reference[] @@ -2887,10 +3222,166 @@ struct default_parser_customization : parser_customization #endif +#ifndef LYRA_EXE_NAME_HPP +#define LYRA_EXE_NAME_HPP + + +#include <memory> +#include <string> + +namespace lyra { + +/* tag::reference[] + +[#lyra_exe_name] += `lyra::exe_name` + +Specifies the name of the executable. + +Is-a <<lyra_composable_parser>>. + +end::reference[] */ +class exe_name : public composable_parser<exe_name> +{ + public: + exe_name() + : m_name(std::make_shared<std::string>("<executable>")) + {} + + explicit exe_name(std::string & ref); + + template <typename LambdaT> + explicit exe_name(LambdaT const & lambda); + + std::string name() const; + parser_result set(std::string const & newName); + + parse_result parse(detail::token_iterator const & tokens, + const option_style &) const override + { + return parse_result::ok( + detail::parse_state(parser_result_type::no_match, tokens)); + } + + std::unique_ptr<parser> clone() const override + { + return make_clone<exe_name>(this); + } + + protected: + std::string get_print_order_key(const option_style &) const override + { + return m_name ? *m_name : ""; + } + + private: + std::shared_ptr<std::string> m_name; + std::shared_ptr<detail::BoundValueRefBase> m_ref; +}; + +/* tag::reference[] + +[#lyra_exe_name_ctor] +== Construction + +end::reference[] */ + +/* tag::reference[] +[source] +---- +exe_name::exe_name(std::string& ref) +---- + +Constructs with a target string to receive the name of the executable. When +the `cli` is run the target string will contain the exec name. + +end::reference[] */ +inline exe_name::exe_name(std::string & ref) + : exe_name() +{ + m_ref = std::make_shared<detail::BoundValueRef<std::string>>(ref); +} + +/* tag::reference[] +[source] +---- +template <typename LambdaT> +exe_name::exe_name(LambdaT const& lambda) +---- + +Construct with a callback that is called with the value of the executable name +when the `cli` runs. + +end::reference[] */ +template <typename LambdaT> +exe_name::exe_name(LambdaT const & lambda) + : exe_name() +{ + m_ref = std::make_shared<detail::BoundLambda<LambdaT>>(lambda); +} + +/* tag::reference[] + +[#lyra_exe_name_accessors] +== Accessors + +end::reference[] */ + +/* tag::reference[] + +[#lyra_exe_name_name] +=== `lyra::exe_name::name` + +[source] +---- +std::string exe_name::name() const +---- + +Returns the executable name when available. Otherwise it returns a default +value. + +end::reference[] */ +inline std::string exe_name::name() const { return *m_name; } + +/* tag::reference[] + +[#lyra_exe_name_set] +=== `lyra::exe_name::set` + +[source] +---- +parser_result exe_name::set(std::string const& newName) +---- + +Sets the executable name with the `newName` value. The new value is reflected +in the bound string reference or callback. + +end::reference[] */ +inline parser_result exe_name::set(std::string const & newName) +{ + auto lastSlash = newName.find_last_of("\\/"); + auto filename = (lastSlash == std::string::npos) + ? newName + : newName.substr(lastSlash + 1); + + *m_name = filename; + if (m_ref) + return m_ref->setValue(filename); + else + return parser_result::ok(parser_result_type::matched); +} + +} // namespace lyra + +#endif + #ifndef LYRA_GROUP_HPP #define LYRA_GROUP_HPP + +#include <cstddef> #include <functional> +#include <memory> namespace lyra { @@ -2915,7 +3406,7 @@ class group : public arguments group(const group & other); explicit group(const std::function<void(const group &)> & f); - virtual bool is_group() const override { return true; } + bool is_group() const override { return true; } parse_result parse(detail::token_iterator const & tokens, const option_style & style) const override @@ -2924,7 +3415,10 @@ class group : public arguments LYRA_PRINT_DEBUG("(?)", get_usage_text(style), "?=", tokens ? tokens.argument().name : ""); parse_result p_result = arguments::parse(tokens, style); - if (p_result && p_result.value().type() != parser_result_type::no_match + if (p_result + && (p_result.value().type() == parser_result_type::matched + || p_result.value().type() + == parser_result_type::short_circuit_all) && success_signal) { this->success_signal(*this); @@ -2944,15 +3438,15 @@ class group : public arguments } group & optional(); - group & required(size_t n = 1); - group & cardinality(size_t n); - group & cardinality(size_t n, size_t m); + group & required(std::size_t n = 1); + group & cardinality(std::size_t n); + group & cardinality(std::size_t n, std::size_t m); detail::parser_cardinality cardinality() const override { return m_cardinality; } - virtual std::unique_ptr<parser> clone() const override + std::unique_ptr<parser> clone() const override { return make_clone<group>(this); } @@ -3048,14 +3542,14 @@ inline group & group::optional() [source] ---- -group & group::required(size_t n); +group & group::required(std::size_t n); ---- Specifies that the argument needs to given the number of `n` times (defaults to *1*). end::reference[] */ -inline group & group::required(size_t n) +inline group & group::required(std::size_t n) { m_cardinality.required(n); return *this; @@ -3068,8 +3562,8 @@ inline group & group::required(size_t n) [source] ---- -group & group::cardinality(size_t n); -group & group::cardinality(size_t n, size_t m); +group & group::cardinality(std::size_t n); +group & group::cardinality(std::size_t n, std::size_t m); ---- Specifies the number of times the argument can and needs to appear in the list @@ -3078,12 +3572,12 @@ the second form it specifies that the argument can appear from `n` to `m` times inclusive. end::reference[] */ -inline group & group::cardinality(size_t n) +inline group & group::cardinality(std::size_t n) { m_cardinality.counted(n); return *this; } -inline group & group::cardinality(size_t n, size_t m) +inline group & group::cardinality(std::size_t n, std::size_t m) { m_cardinality.bounded(n, m); return *this; @@ -3093,7 +3587,12 @@ inline group & group::cardinality(size_t n, size_t m) #endif +#include <cstddef> +#include <memory> +#include <string> #include <type_traits> +#include <utility> +#include <vector> namespace lyra { @@ -3168,7 +3667,7 @@ class cli : protected arguments std::vector<T> converted_value; if (parser_ref) { - for (size_t i = 0; i < parser_ref->get_value_count(); ++i) + for (std::size_t i = 0; i < parser_ref->get_value_count(); ++i) { T v; if (detail::from_string(parser_ref->get_value(i), v)) @@ -3192,11 +3691,11 @@ class cli : protected arguments cli & style(const option_style & style); cli & style(option_style && style); + cli & style_print_short_first(); + cli & style_print_long_first(); - friend std::ostream & operator<<(std::ostream & os, cli const & parser) - { - return os << static_cast<const arguments &>(parser); - } + template <typename T> + friend T & operator<<(T & os, cli const & c); parse_result parse(args const & args) const { @@ -3207,19 +3706,20 @@ class cli : protected arguments } parse_result parse(args const & args, const option_style & style) const; - [[deprecated]] parse_result parse( - args const & args, const parser_customization & customize) const + cli & sequential() { return arguments::sequential(), *this; } + cli & inclusive() { return arguments::inclusive(), *this; } + cli & relaxed() { return arguments::relaxed(), *this; } + + cli & require(std::size_t n, std::size_t m = 0) { - return this->parse(args, - option_style(customize.token_delimiters(), - customize.option_prefix(), 2, customize.option_prefix(), 1)); + return arguments::require(n, m), *this; } using arguments::parse; using arguments::get_named; - virtual std::unique_ptr<parser> clone() const override + std::unique_ptr<parser> clone() const override { return std::unique_ptr<parser>(new cli(*this)); } @@ -3227,8 +3727,7 @@ class cli : protected arguments protected: mutable exe_name m_exeName; - virtual std::string get_usage_text( - const option_style & style) const override + std::string get_usage_text(const option_style & style) const override { if (!m_exeName.name().empty()) return m_exeName.name() + " " + arguments::get_usage_text(style); @@ -3399,7 +3898,8 @@ inline parse_result cli::parse( parse_result p_result = parse(args_tokens, style); if (p_result && (p_result.value().type() == parser_result_type::no_match - || p_result.value().type() == parser_result_type::matched)) + || p_result.value().type() == parser_result_type::matched + || p_result.value().type() == parser_result_type::empty_match)) { if (p_result.value().have_tokens()) { @@ -3435,6 +3935,65 @@ inline cli & cli::style(option_style && style) return *this; } +/* tag::reference[] +[#lyra_cli_style_print_short_first] +=== `lyra::cli::style_print_short_first` + +[source] +---- +lyra::cli & lyra::cli::style_print_short_first() +---- + +Specifies print options sorted with short options appearing first. + +end::reference[] */ +inline cli & cli::style_print_short_first() +{ + ref_option_style().options_print_order + = option_style::opt_print_order::sorted_short_first; + return *this; +} + +/* tag::reference[] +[#lyra_cli_style_print_long_first] +=== `lyra::cli::style_print_long_first` + +[source] +---- +lyra::cli & lyra::cli::style_print_long_first() +---- + +Specifies print options sorted with long options appearing first. + +end::reference[] */ +inline cli & cli::style_print_long_first() +{ + ref_option_style().options_print_order + = option_style::opt_print_order::sorted_long_first; + return *this; +} + +/* tag::reference[] +=== `lyra::operator<<` + +[source] +---- +template <typename T> +T & operator<<(T & os, cli const & c); +---- + +Prints the help text for the cli to the given stream `os`. The `os` stream +is not used directly for printing out. Instead a <<lyra_printer>> object is +created by calling `lyra::make_printer(os)`. This indirection allows one to +customize how the output is generated. + +end::reference[] */ +template <typename T> +T & operator<<(T & os, cli const & c) +{ + return c.print_help(os); +} + } // namespace lyra #endif @@ -3458,6 +4017,8 @@ using cli_parser = cli; #ifndef LYRA_LITERAL_HPP #define LYRA_LITERAL_HPP + +#include <memory> #include <string> namespace lyra { @@ -3480,31 +4041,22 @@ class literal : public parser literal & help(const std::string & help_description_text); literal & operator()(std::string const & help_description_text); - virtual detail::parser_cardinality cardinality() const override - { - return { 1, 1 }; - } + detail::parser_cardinality cardinality() const override { return { 1, 1 }; } - virtual std::string get_usage_text(const option_style &) const override + std::string get_usage_text(const option_style &) const override { return name; } - virtual std::string get_description_text( - const option_style &) const override + std::string get_description_text(const option_style &) const override { return description; } - virtual help_text get_help_text(const option_style &) const override - { - return { { name, description } }; - } - using parser::parse; - virtual parse_result parse(detail::token_iterator const & tokens, + parse_result parse(detail::token_iterator const & tokens, const option_style &) const override { auto validationResult = validate(); @@ -3525,7 +4077,7 @@ class literal : public parser } } - virtual std::unique_ptr<parser> clone() const override + std::unique_ptr<parser> clone() const override { return make_clone<literal>(this); } @@ -3533,6 +4085,17 @@ class literal : public parser protected: std::string name; std::string description; + + std::string get_print_order_key(const option_style &) const override + { + return name; + } + + void print_help_text_details( + printer & p, const option_style & style) const override + { + p.option(style, name, description); + } }; /* tag::reference[] @@ -3594,6 +4157,8 @@ inline literal & literal::operator()(std::string const & help_description_text) } // namespace lyra #endif + +#include <cstddef> #include <functional> #include <string> @@ -3624,7 +4189,7 @@ lyra::command c = lyra::group() lyra::group & g = c.get<lyra::group>(1); ---- -I.e. it's conposed of a `literal` followed by the rest of the command arguments. +I.e. it's composed of a `literal` followed by the rest of the command arguments. Is-a <<lyra_group>>. @@ -3644,10 +4209,62 @@ class command : public group template <typename P> command & operator|=(P const & p); - virtual std::unique_ptr<parser> clone() const override + command & brief_help(bool brief = true); + command & optional() { return static_cast<command &>(group::optional()); } + command & required(std::size_t n = 1) + { + return static_cast<command &>(group::required(n)); + } + command & cardinality(std::size_t n) + { + return static_cast<command &>(group::cardinality(n)); + } + command & cardinality(std::size_t n, std::size_t m) + { + return static_cast<command &>(group::cardinality(n, m)); + } + + std::unique_ptr<parser> clone() const override { return make_clone<command>(this); } + detail::parser_cardinality cardinality() const override + { + return group::cardinality(); + } + + std::string get_usage_text(const option_style & style) const override + { + auto tail = parsers[1]->get_usage_text(style); + return parsers[0]->get_usage_text(style) + + (tail.empty() ? (tail) : (" " + tail)); + } + + protected: + bool expanded_help_details = true; + + std::string get_print_order_key(const option_style & style) const override + { + return parsers[0]->get_print_order_key(style); + } + + void print_help_text_details( + printer & p, const option_style & style) const override + { + if (expanded_help_details) + { + p.option(style, "", ""); + parsers[0]->print_help_text_details(p, style); + p.option(style, "", ""); + p.indent(); + parsers[1]->print_help_text_details(p, style); + p.dedent(); + } + else + { + parsers[0]->print_help_text_details(p, style); + } + } }; /* tag::reference[] @@ -3726,7 +4343,7 @@ template <typename P> command & command::operator|=(P const & p); ---- -Adds the given argument parser to the considered arguments for this `comand`. +Adds the given argument parser to the considered arguments for this `command`. The argument is added to the sub-group argument instead of this one. Hence it has the effect of adding arguments *after* the command name. @@ -3743,6 +4360,27 @@ command & command::operator|=(P const & p) return this->add_argument(p); } +/* tag::reference[] +[#lyra_command_brief_help] +=== `lyra::command::brief_help` + +[source] +---- +command & command::brief_help(bool brief = true); +---- + +Enables, or disables with `false`, brief output of the top level help. Brief +output only prints out the command name and description for the top level +help (i.e. `std::cout << cli`). You can output the full command, options, and +arguments by printing the command (i.e. `std::cout << command`). + +end::reference[] */ +inline command & command::brief_help(bool brief) +{ + expanded_help_details = !brief; + return *this; +} + } // namespace lyra #endif @@ -3754,7 +4392,12 @@ command & command::operator|=(P const & p) #ifndef LYRA_OPT_HPP #define LYRA_OPT_HPP + +#include <cstddef> #include <memory> +#include <string> +#include <type_traits> +#include <vector> namespace lyra { @@ -3772,38 +4415,42 @@ end::reference[] */ class opt : public bound_parser<opt> { public: - enum class ctor_lambda_e - { - val - }; - enum class ctor_ref_e - { - val - }; - explicit opt(bool & ref); template <typename L> explicit opt(L const & ref, typename std::enable_if<detail::is_invocable<L>::value, - ctor_lambda_e>::type - = ctor_lambda_e::val); + detail::ctor_lambda_e>::type + = detail::ctor_lambda_e::val); + + template <typename L> + explicit opt(L && ref, + typename std::enable_if<detail::is_invocable<L>::value, + detail::ctor_lambda_e>::type + = detail::ctor_lambda_e::val); template <typename T> opt(T & ref, std::string const & hint, typename std::enable_if<!detail::is_invocable<T>::value, - ctor_ref_e>::type - = ctor_ref_e::val); + detail::ctor_ref_e>::type + = detail::ctor_ref_e::val); template <typename L> opt(L const & ref, std::string const & hint, typename std::enable_if<detail::is_invocable<L>::value, - ctor_lambda_e>::type - = ctor_lambda_e::val); + detail::ctor_lambda_e>::type + = detail::ctor_lambda_e::val); + + template <typename L> + opt(L && ref, + std::string const & hint, + typename std::enable_if<detail::is_invocable<L>::value, + detail::ctor_lambda_e>::type + = detail::ctor_lambda_e::val); template <typename T> explicit opt(detail::BoundVal<T> && val) @@ -3819,8 +4466,7 @@ class opt : public bound_parser<opt> opt & operator[](std::string const & opt_name); - virtual std::string get_usage_text( - const option_style & style) const override + std::string get_usage_text(const option_style & style) const override { std::string usage; for (std::size_t o = 0; o < opt_names.size(); ++o) @@ -3832,27 +4478,12 @@ class opt : public bound_parser<opt> return usage; } - virtual help_text get_help_text(const option_style & style) const override - { - std::ostringstream oss; - bool first = true; - for (auto const & opt_name : opt_names) - { - if (first) - first = false; - else - oss << ", "; - oss << format_opt(opt_name, style); - } - if (!m_hint.empty()) oss << " <" << m_hint << ">"; - return { { oss.str(), m_description } }; - } - - virtual bool is_named(const std::string & n) const override + bool is_named(const std::string & n) const override { - return bound_parser::is_named(n) - || (std::find(opt_names.begin(), opt_names.end(), n) - != opt_names.end()); + if (bound_parser::is_named(n)) return true; + for (auto & name : opt_names) + if (n == name) return true; + return false; } using parser::parse; @@ -3887,7 +4518,8 @@ class opt : public bound_parser<opt> if (!flag_result) return parse_result(flag_result); LYRA_PRINT_DEBUG( "(=)", get_usage_text(style), "==", token.name); - if (flag_result.value() == parser_result_type::short_circuit_all) + if (flag_result.value() + == parser_result_type::short_circuit_all) return parse_result::ok(detail::parse_state( flag_result.value(), remainingTokens)); } @@ -3917,7 +4549,8 @@ class opt : public bound_parser<opt> } LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", token.name, argToken.name); - if (v_result.value() == parser_result_type::short_circuit_all) + if (v_result.value() + == parser_result_type::short_circuit_all) return parse_result::ok(detail::parse_state( v_result.value(), remainingTokens)); } @@ -3952,7 +4585,7 @@ class opt : public bound_parser<opt> return bound_parser::validate(); } - virtual std::unique_ptr<parser> clone() const override + std::unique_ptr<parser> clone() const override { return make_clone<opt>(this); } @@ -3963,15 +4596,15 @@ class opt : public bound_parser<opt> bool is_match( std::string const & opt_name, const option_style & style) const { - auto opt_normalized = normalise_opt(opt_name, style); + auto opt_normalized = normalize_opt(opt_name, style); for (auto const & name : opt_names) { - if (normalise_opt(name, style) == opt_normalized) return true; + if (normalize_opt(name, style) == opt_normalized) return true; } return false; } - std::string normalise_opt( + std::string normalize_opt( std::string const & opt_name, const option_style & style) const { if (detail::token_iterator::is_prefixed( @@ -3995,6 +4628,24 @@ class opt : public bound_parser<opt> else return opt_name; } + + std::string get_print_order_key(const option_style & style) const override + { + return format_opt(opt_names[0], style); + } + + void print_help_text_details( + printer & p, const option_style & style) const override + { + std::string text; + for (auto const & opt_name : opt_names) + { + if (!text.empty()) text += ", "; + text += format_opt(opt_name, style); + } + if (!m_hint.empty()) ((text += " <") += m_hint) += ">"; + p.option(style, text, m_description); + } }; /* tag::reference[] @@ -4015,6 +4666,9 @@ lyra::opt::opt(bool& ref); template <typename L> lyra::opt::opt(L const& ref); + +template <typename L> +lyra::opt::opt(L && ref); ---- Constructs a flag option with a target `bool` to indicate if the flag is @@ -4029,8 +4683,17 @@ inline opt::opt(bool & ref) template <typename L> opt::opt(L const & ref, typename std::enable_if<detail::is_invocable<L>::value, - opt::ctor_lambda_e>::type) - : bound_parser(std::make_shared<detail::BoundFlagLambda<L>>(ref)) + detail::ctor_lambda_e>::type) + : bound_parser(std::make_shared< + detail::BoundFlagLambda<typename detail::remove_cvref<L>::type>>(ref)) +{} +template <typename L> +opt::opt(L && ref, + typename std::enable_if<detail::is_invocable<L>::value, + detail::ctor_lambda_e>::type) + : bound_parser(std::make_shared< + detail::BoundFlagLambda<typename detail::remove_cvref<L>::type>>( + std::move(ref))) {} /* tag::reference[] @@ -4045,6 +4708,9 @@ lyra::opt::opt(T& ref, std::string const& hint); template <typename L> lyra::opt::opt(L const& ref, std::string const& hint) + +template <typename L> +lyra::opt::opt(L && ref, std::string const& hint) ---- Constructs a value option with a target `ref`. The first form takes a reference @@ -4056,16 +4722,23 @@ template <typename T> opt::opt(T & ref, std::string const & hint, typename std::enable_if<!detail::is_invocable<T>::value, - opt::ctor_ref_e>::type) + detail::ctor_ref_e>::type) : bound_parser(ref, hint) {} template <typename L> opt::opt(L const & ref, std::string const & hint, typename std::enable_if<detail::is_invocable<L>::value, - opt::ctor_lambda_e>::type) + detail::ctor_lambda_e>::type) : bound_parser(ref, hint) {} +template <typename L> +opt::opt(L && ref, + std::string const & hint, + typename std::enable_if<detail::is_invocable<L>::value, + detail::ctor_lambda_e>::type) + : bound_parser(std::move(ref), hint) +{} /* tag::reference[] @@ -4107,6 +4780,9 @@ inline opt & opt::operator[](const std::string & opt_name) #endif +#include <memory> +#include <string> + namespace lyra { /* tag::reference[] @@ -4139,13 +4815,12 @@ class help : public opt help & description(const std::string & text); - virtual std::string get_description_text( - const option_style &) const override + std::string get_description_text(const option_style &) const override { return description_text; } - virtual std::unique_ptr<parser> clone() const override + std::unique_ptr<parser> clone() const override { return make_clone<help>(this); } @@ -4179,7 +4854,49 @@ inline help & help::description(const std::string & text) #define LYRA_MAIN_HPP +#ifndef LYRA_VAL_HPP +#define LYRA_VAL_HPP + + +#include <string> + +namespace lyra { + +/* tag::reference[] + +[#lyra_val] += `lyra::val` + +[source] +---- +auto val(T && v); +auto val(const char * v); +---- + +Makes a bound self-contained value of the type of the given r-value. The created +bound values can be used in place of the value references for arguments. And can +be retrieved with the +<<lyra_cli_array_ref>> call. + +*/ // end::reference[] +template <typename T> +detail::BoundVal<T> val(T && v) +{ + return detail::BoundVal<T>(std::forward<T>(v)); +} + +inline detail::BoundVal<std::string> val(const char * v) +{ + return detail::BoundVal<std::string>(v); +} + +} // namespace lyra + +#endif + +#include <initializer_list> #include <iostream> +#include <string> namespace lyra { diff --git a/vendor/utfcpp/core.h b/vendor/utfcpp/core.h index 627133c61..064f838fd 100644 --- a/vendor/utfcpp/core.h +++ b/vendor/utfcpp/core.h @@ -43,9 +43,14 @@ DEALINGS IN THE SOFTWARE. #if UTF_CPP_CPLUSPLUS >= 201103L // C++ 11 or later #define UTF_CPP_OVERRIDE override #define UTF_CPP_NOEXCEPT noexcept + #define UTF_CPP_STATIC_ASSERT(condition) static_assert(condition, "UTFCPP static assert"); #else // C++ 98/03 #define UTF_CPP_OVERRIDE #define UTF_CPP_NOEXCEPT throw() + // Simulate static_assert: + template <bool Condition> struct StaticAssert {static void utf8_static_assert() {char static_assert_impl[Condition ? 1 : 0]; } }; + template <> struct StaticAssert<true> {static void utf8_static_assert() {}}; + #define UTF_CPP_STATIC_ASSERT(condition) StaticAssert<condition>::utf8_static_assert(); #endif // C++ 11 or later @@ -87,6 +92,7 @@ namespace internal { return static_cast<utfchar8_t>(0xff & oc); } + template<typename u16_type> inline utfchar16_t mask16(u16_type oc) { @@ -101,17 +107,17 @@ namespace internal inline bool is_lead_surrogate(utfchar32_t cp) { - return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX); + return (cp >= static_cast<utfchar32_t>(LEAD_SURROGATE_MIN) && cp <= static_cast<utfchar32_t>(LEAD_SURROGATE_MAX)); } inline bool is_trail_surrogate(utfchar32_t cp) { - return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); + return (cp >= static_cast<utfchar32_t>(TRAIL_SURROGATE_MIN) && cp <= static_cast<utfchar32_t>(TRAIL_SURROGATE_MAX)); } inline bool is_surrogate(utfchar32_t cp) { - return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); + return (cp >= static_cast<utfchar32_t>(LEAD_SURROGATE_MIN) && cp <= static_cast<utfchar32_t>(TRAIL_SURROGATE_MAX)); } inline bool is_code_point_valid(utfchar32_t cp) @@ -143,15 +149,15 @@ namespace internal inline bool is_overlong_sequence(utfchar32_t cp, int length) { if (cp < 0x80) { - if (length != 1) + if (length != 1) return true; } else if (cp < 0x800) { - if (length != 2) + if (length != 2) return true; } else if (cp < 0x10000) { - if (length != 3) + if (length != 3) return true; } return false; @@ -181,7 +187,7 @@ namespace internal if (it == end) return NOT_ENOUGH_ROOM; - code_point = utf8::internal::mask8(*it); + code_point = static_cast<utfchar32_t>(utf8::internal::mask8(*it)); return UTF8_OK; } @@ -189,10 +195,10 @@ namespace internal template <typename octet_iterator> utf_error get_sequence_2(octet_iterator& it, octet_iterator end, utfchar32_t& code_point) { - if (it == end) + if (it == end) return NOT_ENOUGH_ROOM; - code_point = utf8::internal::mask8(*it); + code_point = static_cast<utfchar32_t>(utf8::internal::mask8(*it)); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) @@ -206,8 +212,8 @@ namespace internal { if (it == end) return NOT_ENOUGH_ROOM; - - code_point = utf8::internal::mask8(*it); + + code_point = static_cast<utfchar32_t>(utf8::internal::mask8(*it)); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) @@ -226,7 +232,7 @@ namespace internal if (it == end) return NOT_ENOUGH_ROOM; - code_point = utf8::internal::mask8(*it); + code_point = static_cast<utfchar32_t>(utf8::internal::mask8(*it)); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) @@ -290,7 +296,7 @@ namespace internal else err = OVERLONG_SEQUENCE; } - else + else err = INVALID_CODE_POINT; } @@ -308,6 +314,10 @@ namespace internal template <typename word_iterator> utf_error validate_next16(word_iterator& it, word_iterator end, utfchar32_t& code_point) { + // Make sure the iterator dereferences a large enough type + typedef typename std::iterator_traits<word_iterator>::value_type word_type; + UTF_CPP_STATIC_ASSERT(sizeof(word_type) >= sizeof(utfchar16_t)); + // Check the edge case: if (it == end) return NOT_ENOUGH_ROOM; // Save the original value of it so we can go back in case of failure @@ -326,14 +336,14 @@ namespace internal err = NOT_ENOUGH_ROOM; else if (is_lead_surrogate(first_word)) { const utfchar16_t second_word = *it++; - if (is_trail_surrogate(second_word)) { - code_point = static_cast<utfchar32_t>(first_word << 10) + second_word + SURROGATE_OFFSET; + if (is_trail_surrogate(static_cast<utfchar32_t>(second_word))) { + code_point = static_cast<utfchar32_t>(first_word << 10) + static_cast<utfchar32_t>(second_word) + SURROGATE_OFFSET; return UTF8_OK; - } else - err = INCOMPLETE_SEQUENCE; - + } else + err = INCOMPLETE_SEQUENCE; + } else { - err = INVALID_LEAD; + err = INVALID_LEAD; } } // error branch @@ -365,7 +375,7 @@ namespace internal } return result; } - + // One of the following overloads will be invoked from the API calls // A simple (but dangerous) case: the caller appends byte(s) to a char array @@ -395,6 +405,7 @@ namespace internal // the word_type. template <typename word_iterator, typename word_type> word_iterator append16(utfchar32_t cp, word_iterator result) { + UTF_CPP_STATIC_ASSERT(sizeof(word_type) >= sizeof(utfchar16_t)); if (is_in_bmp(cp)) *(result++) = static_cast<word_type>(cp); else { @@ -444,7 +455,7 @@ namespace internal inline const char* find_invalid(const char* str) { const char* end = str + std::strlen(str); - return find_invalid(str, end); + return find_invalid(str, end); } inline std::size_t find_invalid(const std::string& s) @@ -484,9 +495,8 @@ namespace internal inline bool starts_with_bom(const std::string& s) { return starts_with_bom(s.begin(), s.end()); - } + } } // namespace utf8 #endif // header guard - diff --git a/vendor/utfcpp/unchecked.h b/vendor/utfcpp/unchecked.h index bf2917893..173d0302e 100644 --- a/vendor/utfcpp/unchecked.h +++ b/vendor/utfcpp/unchecked.h @@ -82,7 +82,7 @@ namespace utf8 template <typename octet_iterator, typename output_iterator> inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) { - static const utfchar32_t replacement_marker = utf8::internal::mask16(0xfffd); + static const utfchar32_t replacement_marker = static_cast<utfchar32_t>(utf8::internal::mask16(0xfffd)); return utf8::unchecked::replace_invalid(start, end, out, replacement_marker); } @@ -108,22 +108,22 @@ namespace utf8 case 1: break; case 2: - it++; + ++it; cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); break; case 3: - ++it; + ++it; cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); ++it; cp = static_cast<utfchar32_t>(cp + ((*it) & 0x3f)); break; case 4: ++it; - cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); + cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); ++it; cp = static_cast<utfchar32_t>(cp + ((utf8::internal::mask8(*it) << 6) & 0xfff)); ++it; - cp = static_cast<utfchar32_t>(cp + ((*it) & 0x3f)); + cp = static_cast<utfchar32_t>(cp + ((*it) & 0x3f)); break; } ++it; @@ -173,7 +173,7 @@ namespace utf8 distance(octet_iterator first, octet_iterator last) { typename std::iterator_traits<octet_iterator>::difference_type dist; - for (dist = 0; first < last; ++dist) + for (dist = 0; first < last; ++dist) utf8::unchecked::next(first); return dist; } @@ -247,15 +247,15 @@ namespace utf8 octet_iterator temp = it; return utf8::unchecked::next(temp); } - bool operator == (const iterator& rhs) const - { + bool operator == (const iterator& rhs) const + { return (it == rhs.it); } bool operator != (const iterator& rhs) const { return !(operator == (rhs)); } - iterator& operator ++ () + iterator& operator ++ () { ::std::advance(it, utf8::internal::sequence_length(it)); return *this; @@ -265,7 +265,7 @@ namespace utf8 iterator temp = *this; ::std::advance(it, utf8::internal::sequence_length(it)); return temp; - } + } iterator& operator -- () { utf8::unchecked::prior(it); @@ -280,8 +280,7 @@ namespace utf8 }; // class iterator } // namespace utf8::unchecked -} // namespace utf8 - +} // namespace utf8 #endif // header guard |
