aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Sadie Powell2025-10-31 20:45:44 +0000
committerGravatar Sadie Powell2025-10-31 20:45:44 +0000
commit088e5a4a257b616c0c6beb795ad59070fa5428bc (patch)
treec3ffa6e7ed741cf7adb84a968a4e809e72e67ecf
parentRevert "Remove some unnecessary casts in ssl_openssl". (diff)
Update the vendored libraries.
-rw-r--r--vendor/README.md6
-rw-r--r--vendor/fmt/base.h209
-rw-r--r--vendor/fmt/color.h36
-rw-r--r--vendor/fmt/compile.h203
-rw-r--r--vendor/fmt/format-inl.h122
-rw-r--r--vendor/fmt/format.cc17
-rw-r--r--vendor/fmt/format.h803
-rw-r--r--vendor/lyra/lyra.hpp2271
-rw-r--r--vendor/utfcpp/core.h56
-rw-r--r--vendor/utfcpp/unchecked.h23
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