![support@xmrig.com](/assets/img/avatar_default.png)
160 changed files with 20404 additions and 12700 deletions
File diff suppressed because it is too large
@ -1,699 +0,0 @@ |
|||
// Formatting library for C++ - experimental format string compilation
|
|||
//
|
|||
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
|
|||
// All rights reserved.
|
|||
//
|
|||
// For the license information refer to format.h.
|
|||
|
|||
#ifndef FMT_COMPILE_H_ |
|||
#define FMT_COMPILE_H_ |
|||
|
|||
#include <vector> |
|||
|
|||
#include "format.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
namespace detail { |
|||
|
|||
// A compile-time string which is compiled into fast formatting code.
|
|||
class compiled_string {}; |
|||
|
|||
template <typename S> |
|||
struct is_compiled_string : std::is_base_of<compiled_string, S> {}; |
|||
|
|||
/**
|
|||
\rst |
|||
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 |
|||
``constexpr if`` compiler support. |
|||
|
|||
**Example**:: |
|||
|
|||
// Converts 42 into std::string using the most efficient method and no
|
|||
// runtime format string processing.
|
|||
std::string s = fmt::format(FMT_COMPILE("{}"), 42); |
|||
\endrst |
|||
*/ |
|||
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string) |
|||
|
|||
template <typename T, typename... Tail> |
|||
const T& first(const T& value, const Tail&...) { |
|||
return value; |
|||
} |
|||
|
|||
// Part of a compiled format string. It can be either literal text or a
|
|||
// replacement field.
|
|||
template <typename Char> struct format_part { |
|||
enum class kind { arg_index, arg_name, text, replacement }; |
|||
|
|||
struct replacement { |
|||
arg_ref<Char> arg_id; |
|||
dynamic_format_specs<Char> specs; |
|||
}; |
|||
|
|||
kind part_kind; |
|||
union value { |
|||
int arg_index; |
|||
basic_string_view<Char> str; |
|||
replacement repl; |
|||
|
|||
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {} |
|||
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {} |
|||
FMT_CONSTEXPR value(replacement r) : repl(r) {} |
|||
} val; |
|||
// Position past the end of the argument id.
|
|||
const Char* arg_id_end = nullptr; |
|||
|
|||
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {}) |
|||
: part_kind(k), val(v) {} |
|||
|
|||
static FMT_CONSTEXPR format_part make_arg_index(int index) { |
|||
return format_part(kind::arg_index, index); |
|||
} |
|||
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) { |
|||
return format_part(kind::arg_name, name); |
|||
} |
|||
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) { |
|||
return format_part(kind::text, text); |
|||
} |
|||
static FMT_CONSTEXPR format_part make_replacement(replacement repl) { |
|||
return format_part(kind::replacement, repl); |
|||
} |
|||
}; |
|||
|
|||
template <typename Char> struct part_counter { |
|||
unsigned num_parts = 0; |
|||
|
|||
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { |
|||
if (begin != end) ++num_parts; |
|||
} |
|||
|
|||
FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; } |
|||
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; } |
|||
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) { |
|||
return ++num_parts, 0; |
|||
} |
|||
|
|||
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} |
|||
|
|||
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin, |
|||
const Char* end) { |
|||
// Find the matching brace.
|
|||
unsigned brace_counter = 0; |
|||
for (; begin != end; ++begin) { |
|||
if (*begin == '{') { |
|||
++brace_counter; |
|||
} else if (*begin == '}') { |
|||
if (brace_counter == 0u) break; |
|||
--brace_counter; |
|||
} |
|||
} |
|||
return begin; |
|||
} |
|||
|
|||
FMT_CONSTEXPR void on_error(const char*) {} |
|||
}; |
|||
|
|||
// Counts the number of parts in a format string.
|
|||
template <typename Char> |
|||
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) { |
|||
part_counter<Char> counter; |
|||
parse_format_string<true>(format_str, counter); |
|||
return counter.num_parts; |
|||
} |
|||
|
|||
template <typename Char, typename PartHandler> |
|||
class format_string_compiler : public error_handler { |
|||
private: |
|||
using part = format_part<Char>; |
|||
|
|||
PartHandler handler_; |
|||
part part_; |
|||
basic_string_view<Char> format_str_; |
|||
basic_format_parse_context<Char> parse_context_; |
|||
|
|||
public: |
|||
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str, |
|||
PartHandler handler) |
|||
: handler_(handler), |
|||
format_str_(format_str), |
|||
parse_context_(format_str) {} |
|||
|
|||
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { |
|||
if (begin != end) |
|||
handler_(part::make_text({begin, to_unsigned(end - begin)})); |
|||
} |
|||
|
|||
FMT_CONSTEXPR int on_arg_id() { |
|||
part_ = part::make_arg_index(parse_context_.next_arg_id()); |
|||
return 0; |
|||
} |
|||
|
|||
FMT_CONSTEXPR int on_arg_id(int id) { |
|||
parse_context_.check_arg_id(id); |
|||
part_ = part::make_arg_index(id); |
|||
return 0; |
|||
} |
|||
|
|||
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) { |
|||
part_ = part::make_arg_name(id); |
|||
return 0; |
|||
} |
|||
|
|||
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) { |
|||
part_.arg_id_end = ptr; |
|||
handler_(part_); |
|||
} |
|||
|
|||
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin, |
|||
const Char* end) { |
|||
auto repl = typename part::replacement(); |
|||
dynamic_specs_handler<basic_format_parse_context<Char>> handler( |
|||
repl.specs, parse_context_); |
|||
auto it = parse_format_specs(begin, end, handler); |
|||
if (*it != '}') on_error("missing '}' in format string"); |
|||
repl.arg_id = part_.part_kind == part::kind::arg_index |
|||
? arg_ref<Char>(part_.val.arg_index) |
|||
: arg_ref<Char>(part_.val.str); |
|||
auto part = part::make_replacement(repl); |
|||
part.arg_id_end = begin; |
|||
handler_(part); |
|||
return it; |
|||
} |
|||
}; |
|||
|
|||
// Compiles a format string and invokes handler(part) for each parsed part.
|
|||
template <bool IS_CONSTEXPR, typename Char, typename PartHandler> |
|||
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str, |
|||
PartHandler handler) { |
|||
parse_format_string<IS_CONSTEXPR>( |
|||
format_str, |
|||
format_string_compiler<Char, PartHandler>(format_str, handler)); |
|||
} |
|||
|
|||
template <typename OutputIt, typename Context, typename Id> |
|||
void format_arg( |
|||
basic_format_parse_context<typename Context::char_type>& parse_ctx, |
|||
Context& ctx, Id arg_id) { |
|||
ctx.advance_to(visit_format_arg( |
|||
arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx), |
|||
ctx.arg(arg_id))); |
|||
} |
|||
|
|||
// vformat_to is defined in a subnamespace to prevent ADL.
|
|||
namespace cf { |
|||
template <typename Context, typename OutputIt, typename CompiledFormat> |
|||
auto vformat_to(OutputIt out, CompiledFormat& cf, |
|||
basic_format_args<Context> args) -> typename Context::iterator { |
|||
using char_type = typename Context::char_type; |
|||
basic_format_parse_context<char_type> parse_ctx( |
|||
to_string_view(cf.format_str_)); |
|||
Context ctx(out, args); |
|||
|
|||
const auto& parts = cf.parts(); |
|||
for (auto part_it = std::begin(parts); part_it != std::end(parts); |
|||
++part_it) { |
|||
const auto& part = *part_it; |
|||
const auto& value = part.val; |
|||
|
|||
using format_part_t = format_part<char_type>; |
|||
switch (part.part_kind) { |
|||
case format_part_t::kind::text: { |
|||
const auto text = value.str; |
|||
auto output = ctx.out(); |
|||
auto&& it = reserve(output, text.size()); |
|||
it = std::copy_n(text.begin(), text.size(), it); |
|||
ctx.advance_to(output); |
|||
break; |
|||
} |
|||
|
|||
case format_part_t::kind::arg_index: |
|||
advance_to(parse_ctx, part.arg_id_end); |
|||
detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index); |
|||
break; |
|||
|
|||
case format_part_t::kind::arg_name: |
|||
advance_to(parse_ctx, part.arg_id_end); |
|||
detail::format_arg<OutputIt>(parse_ctx, ctx, value.str); |
|||
break; |
|||
|
|||
case format_part_t::kind::replacement: { |
|||
const auto& arg_id_value = value.repl.arg_id.val; |
|||
const auto arg = value.repl.arg_id.kind == arg_id_kind::index |
|||
? ctx.arg(arg_id_value.index) |
|||
: ctx.arg(arg_id_value.name); |
|||
|
|||
auto specs = value.repl.specs; |
|||
|
|||
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx); |
|||
handle_dynamic_spec<precision_checker>(specs.precision, |
|||
specs.precision_ref, ctx); |
|||
|
|||
error_handler h; |
|||
numeric_specs_checker<error_handler> checker(h, arg.type()); |
|||
if (specs.align == align::numeric) checker.require_numeric_argument(); |
|||
if (specs.sign != sign::none) checker.check_sign(); |
|||
if (specs.alt) checker.require_numeric_argument(); |
|||
if (specs.precision >= 0) checker.check_precision(); |
|||
|
|||
advance_to(parse_ctx, part.arg_id_end); |
|||
ctx.advance_to( |
|||
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>( |
|||
ctx, nullptr, &specs), |
|||
arg)); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
return ctx.out(); |
|||
} |
|||
} // namespace cf
|
|||
|
|||
struct basic_compiled_format {}; |
|||
|
|||
template <typename S, typename = void> |
|||
struct compiled_format_base : basic_compiled_format { |
|||
using char_type = char_t<S>; |
|||
using parts_container = std::vector<detail::format_part<char_type>>; |
|||
|
|||
parts_container compiled_parts; |
|||
|
|||
explicit compiled_format_base(basic_string_view<char_type> format_str) { |
|||
compile_format_string<false>(format_str, |
|||
[this](const format_part<char_type>& part) { |
|||
compiled_parts.push_back(part); |
|||
}); |
|||
} |
|||
|
|||
const parts_container& parts() const { return compiled_parts; } |
|||
}; |
|||
|
|||
template <typename Char, unsigned N> struct format_part_array { |
|||
format_part<Char> data[N] = {}; |
|||
FMT_CONSTEXPR format_part_array() = default; |
|||
}; |
|||
|
|||
template <typename Char, unsigned N> |
|||
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts( |
|||
basic_string_view<Char> format_str) { |
|||
format_part_array<Char, N> parts; |
|||
unsigned counter = 0; |
|||
// This is not a lambda for compatibility with older compilers.
|
|||
struct { |
|||
format_part<Char>* parts; |
|||
unsigned* counter; |
|||
FMT_CONSTEXPR void operator()(const format_part<Char>& part) { |
|||
parts[(*counter)++] = part; |
|||
} |
|||
} collector{parts.data, &counter}; |
|||
compile_format_string<true>(format_str, collector); |
|||
if (counter < N) { |
|||
parts.data[counter] = |
|||
format_part<Char>::make_text(basic_string_view<Char>()); |
|||
} |
|||
return parts; |
|||
} |
|||
|
|||
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) { |
|||
return (a < b) ? b : a; |
|||
} |
|||
|
|||
template <typename S> |
|||
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>> |
|||
: basic_compiled_format { |
|||
using char_type = char_t<S>; |
|||
|
|||
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {} |
|||
|
|||
// Workaround for old compilers. Format string compilation will not be
|
|||
// performed there anyway.
|
|||
#if FMT_USE_CONSTEXPR |
|||
static FMT_CONSTEXPR_DECL const unsigned num_format_parts = |
|||
constexpr_max(count_parts(to_string_view(S())), 1u); |
|||
#else |
|||
static const unsigned num_format_parts = 1; |
|||
#endif |
|||
|
|||
using parts_container = format_part<char_type>[num_format_parts]; |
|||
|
|||
const parts_container& parts() const { |
|||
static FMT_CONSTEXPR_DECL const auto compiled_parts = |
|||
compile_to_parts<char_type, num_format_parts>( |
|||
detail::to_string_view(S())); |
|||
return compiled_parts.data; |
|||
} |
|||
}; |
|||
|
|||
template <typename S, typename... Args> |
|||
class compiled_format : private compiled_format_base<S> { |
|||
public: |
|||
using typename compiled_format_base<S>::char_type; |
|||
|
|||
private: |
|||
basic_string_view<char_type> format_str_; |
|||
|
|||
template <typename Context, typename OutputIt, typename CompiledFormat> |
|||
friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf, |
|||
basic_format_args<Context> args) -> |
|||
typename Context::iterator; |
|||
|
|||
public: |
|||
compiled_format() = delete; |
|||
explicit constexpr compiled_format(basic_string_view<char_type> format_str) |
|||
: compiled_format_base<S>(format_str), format_str_(format_str) {} |
|||
}; |
|||
|
|||
#ifdef __cpp_if_constexpr |
|||
template <typename... Args> 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) { |
|||
static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); |
|||
if constexpr (N == 0) |
|||
return first; |
|||
else |
|||
return get<N - 1>(rest...); |
|||
} |
|||
|
|||
template <int N, typename> struct get_type_impl; |
|||
|
|||
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> { |
|||
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>; |
|||
}; |
|||
|
|||
template <int N, typename T> |
|||
using get_type = typename get_type_impl<N, T>::type; |
|||
|
|||
template <typename T> struct is_compiled_format : std::false_type {}; |
|||
|
|||
template <typename Char> struct text { |
|||
basic_string_view<Char> data; |
|||
using char_type = Char; |
|||
|
|||
template <typename OutputIt, typename... Args> |
|||
OutputIt format(OutputIt out, const Args&...) const { |
|||
return write<Char>(out, data); |
|||
} |
|||
}; |
|||
|
|||
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) { |
|||
return {{&s[pos], size}}; |
|||
} |
|||
|
|||
template <typename Char> struct code_unit { |
|||
Char value; |
|||
using char_type = Char; |
|||
|
|||
template <typename OutputIt, typename... Args> |
|||
OutputIt format(OutputIt out, const Args&...) const { |
|||
return write<Char>(out, value); |
|||
} |
|||
}; |
|||
|
|||
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 { |
|||
using char_type = Char; |
|||
|
|||
template <typename OutputIt, typename... Args> |
|||
OutputIt format(OutputIt out, const Args&... args) const { |
|||
// This ensures that the argument type is convertile to `const T&`.
|
|||
const T& arg = get<N>(args...); |
|||
return write<Char>(out, arg); |
|||
} |
|||
}; |
|||
|
|||
template <typename Char, typename T, int N> |
|||
struct is_compiled_format<field<Char, T, N>> : 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 { |
|||
using char_type = Char; |
|||
mutable formatter<T, Char> fmt; |
|||
|
|||
template <typename OutputIt, typename... Args> |
|||
OutputIt format(OutputIt out, const Args&... args) const { |
|||
// This ensures that the argument type is convertile to `const T&`.
|
|||
const T& arg = get<N>(args...); |
|||
const auto& vargs = |
|||
make_format_args<basic_format_context<OutputIt, Char>>(args...); |
|||
basic_format_context<OutputIt, Char> ctx(out, vargs); |
|||
return fmt.format(arg, ctx); |
|||
} |
|||
}; |
|||
|
|||
template <typename Char, typename T, int N> |
|||
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {}; |
|||
|
|||
template <typename L, typename R> struct concat { |
|||
L lhs; |
|||
R rhs; |
|||
using char_type = typename L::char_type; |
|||
|
|||
template <typename OutputIt, typename... Args> |
|||
OutputIt format(OutputIt out, const Args&... args) const { |
|||
out = lhs.format(out, args...); |
|||
return rhs.format(out, args...); |
|||
} |
|||
}; |
|||
|
|||
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) { |
|||
return {lhs, rhs}; |
|||
} |
|||
|
|||
struct unknown_format {}; |
|||
|
|||
template <typename Char> |
|||
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) { |
|||
for (size_t size = str.size(); pos != size; ++pos) { |
|||
if (str[pos] == '{' || str[pos] == '}') break; |
|||
} |
|||
return pos; |
|||
} |
|||
|
|||
template <typename Args, size_t POS, int ID, typename S> |
|||
constexpr auto compile_format_string(S format_str); |
|||
|
|||
template <typename Args, size_t POS, int ID, typename T, typename S> |
|||
constexpr auto parse_tail(T head, S format_str) { |
|||
if constexpr (POS != |
|||
basic_string_view<typename S::char_type>(format_str).size()) { |
|||
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str); |
|||
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>, |
|||
unknown_format>()) |
|||
return tail; |
|||
else |
|||
return make_concat(head, tail); |
|||
} else { |
|||
return head; |
|||
} |
|||
} |
|||
|
|||
template <typename T, typename Char> struct parse_specs_result { |
|||
formatter<T, Char> fmt; |
|||
size_t end; |
|||
int next_arg_id; |
|||
}; |
|||
|
|||
template <typename T, typename Char> |
|||
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, |
|||
size_t pos, int arg_id) { |
|||
str.remove_prefix(pos); |
|||
auto ctx = basic_format_parse_context<Char>(str, {}, arg_id + 1); |
|||
auto f = formatter<T, Char>(); |
|||
auto end = f.parse(ctx); |
|||
return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()}; |
|||
} |
|||
|
|||
// Compiles a non-empty format string and returns the compiled representation
|
|||
// or unknown_format() on unrecognized input.
|
|||
template <typename Args, size_t POS, int ID, typename S> |
|||
constexpr auto compile_format_string(S format_str) { |
|||
using char_type = typename S::char_type; |
|||
constexpr basic_string_view<char_type> str = format_str; |
|||
if constexpr (str[POS] == '{') { |
|||
if (POS + 1 == str.size()) |
|||
throw format_error("unmatched '{' in format string"); |
|||
if constexpr (str[POS + 1] == '{') { |
|||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); |
|||
} else if constexpr (str[POS + 1] == '}') { |
|||
using type = get_type<ID, Args>; |
|||
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(), |
|||
format_str); |
|||
} else if constexpr (str[POS + 1] == ':') { |
|||
using type = get_type<ID, Args>; |
|||
constexpr auto result = parse_specs<type>(str, POS + 2, ID); |
|||
return parse_tail<Args, result.end, result.next_arg_id>( |
|||
spec_field<char_type, type, ID>{result.fmt}, format_str); |
|||
} else { |
|||
return unknown_format(); |
|||
} |
|||
} else if constexpr (str[POS] == '}') { |
|||
if (POS + 1 == str.size()) |
|||
throw format_error("unmatched '}' in format string"); |
|||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); |
|||
} else { |
|||
constexpr auto end = parse_text(str, POS + 1); |
|||
if constexpr (end - POS > 1) { |
|||
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), |
|||
format_str); |
|||
} else { |
|||
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, |
|||
format_str); |
|||
} |
|||
} |
|||
} |
|||
|
|||
template <typename... Args, typename S, |
|||
FMT_ENABLE_IF(is_compile_string<S>::value || |
|||
detail::is_compiled_string<S>::value)> |
|||
constexpr auto compile(S format_str) { |
|||
constexpr basic_string_view<typename S::char_type> str = format_str; |
|||
if constexpr (str.size() == 0) { |
|||
return detail::make_text(str, 0, 0); |
|||
} else { |
|||
constexpr auto result = |
|||
detail::compile_format_string<detail::type_list<Args...>, 0, 0>( |
|||
format_str); |
|||
if constexpr (std::is_same<remove_cvref_t<decltype(result)>, |
|||
detail::unknown_format>()) { |
|||
return detail::compiled_format<S, Args...>(to_string_view(format_str)); |
|||
} else { |
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
#else |
|||
template <typename... Args, typename S, |
|||
FMT_ENABLE_IF(is_compile_string<S>::value)> |
|||
constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> { |
|||
return detail::compiled_format<S, Args...>(to_string_view(format_str)); |
|||
} |
|||
#endif // __cpp_if_constexpr
|
|||
|
|||
// Compiles the format string which must be a string literal.
|
|||
template <typename... Args, typename Char, size_t N> |
|||
auto compile(const Char (&format_str)[N]) |
|||
-> detail::compiled_format<const Char*, Args...> { |
|||
return detail::compiled_format<const Char*, Args...>( |
|||
basic_string_view<Char>(format_str, N - 1)); |
|||
} |
|||
} // namespace detail
|
|||
|
|||
// DEPRECATED! use FMT_COMPILE instead.
|
|||
template <typename... Args> |
|||
FMT_DEPRECATED auto compile(const Args&... args) |
|||
-> decltype(detail::compile(args...)) { |
|||
return detail::compile(args...); |
|||
} |
|||
|
|||
#if FMT_USE_CONSTEXPR |
|||
# ifdef __cpp_if_constexpr |
|||
|
|||
template <typename CompiledFormat, typename... Args, |
|||
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) { |
|||
basic_memory_buffer<Char> buffer; |
|||
cf.format(detail::buffer_appender<Char>(buffer), args...); |
|||
return to_string(buffer); |
|||
} |
|||
|
|||
template <typename OutputIt, typename CompiledFormat, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> |
|||
OutputIt format_to(OutputIt out, const CompiledFormat& cf, |
|||
const Args&... args) { |
|||
return cf.format(out, args...); |
|||
} |
|||
# endif // __cpp_if_constexpr
|
|||
#endif // FMT_USE_CONSTEXPR
|
|||
|
|||
template <typename CompiledFormat, typename... Args, |
|||
typename Char = typename CompiledFormat::char_type, |
|||
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format, |
|||
CompiledFormat>::value)> |
|||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) { |
|||
basic_memory_buffer<Char> buffer; |
|||
using context = buffer_context<Char>; |
|||
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf, |
|||
make_format_args<context>(args...)); |
|||
return to_string(buffer); |
|||
} |
|||
|
|||
template <typename S, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> |
|||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&, |
|||
Args&&... args) { |
|||
#ifdef __cpp_if_constexpr |
|||
if constexpr (std::is_same<typename S::char_type, char>::value) { |
|||
constexpr basic_string_view<typename S::char_type> str = S(); |
|||
if (str.size() == 2 && str[0] == '{' && str[1] == '}') |
|||
return fmt::to_string(detail::first(args...)); |
|||
} |
|||
#endif |
|||
constexpr auto compiled = detail::compile<Args...>(S()); |
|||
return format(compiled, std::forward<Args>(args)...); |
|||
} |
|||
|
|||
template <typename OutputIt, typename CompiledFormat, typename... Args, |
|||
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format, |
|||
CompiledFormat>::value)> |
|||
OutputIt format_to(OutputIt out, const CompiledFormat& cf, |
|||
const Args&... args) { |
|||
using char_type = typename CompiledFormat::char_type; |
|||
using context = format_context_t<OutputIt, char_type>; |
|||
return detail::cf::vformat_to<context>(out, cf, |
|||
make_format_args<context>(args...)); |
|||
} |
|||
|
|||
template <typename OutputIt, typename S, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> |
|||
OutputIt format_to(OutputIt out, const S&, const Args&... args) { |
|||
constexpr auto compiled = detail::compile<Args...>(S()); |
|||
return format_to(out, compiled, args...); |
|||
} |
|||
|
|||
template < |
|||
typename OutputIt, typename CompiledFormat, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&& std::is_base_of< |
|||
detail::basic_compiled_format, CompiledFormat>::value)> |
|||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, |
|||
const CompiledFormat& cf, |
|||
const Args&... args) { |
|||
auto it = |
|||
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...); |
|||
return {it.base(), it.count()}; |
|||
} |
|||
|
|||
template <typename OutputIt, typename S, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> |
|||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&, |
|||
const Args&... args) { |
|||
constexpr auto compiled = detail::compile<Args...>(S()); |
|||
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), compiled, |
|||
args...); |
|||
return {it.base(), it.count()}; |
|||
} |
|||
|
|||
template <typename CompiledFormat, typename... Args> |
|||
size_t formatted_size(const CompiledFormat& cf, const Args&... args) { |
|||
return format_to(detail::counting_iterator(), cf, args...).count(); |
|||
} |
|||
|
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_COMPILE_H_
|
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -1,69 +0,0 @@ |
|||
// Formatting library for C++
|
|||
//
|
|||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
|||
// All rights reserved.
|
|||
//
|
|||
// For the license information refer to format.h.
|
|||
|
|||
#include "3rdparty/fmt/format-inl.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
namespace detail { |
|||
|
|||
template <typename T> |
|||
int format_float(char* buf, std::size_t size, const char* format, int precision, |
|||
T value) { |
|||
#ifdef FMT_FUZZ |
|||
if (precision > 100000) |
|||
throw std::runtime_error( |
|||
"fuzz mode - avoid large allocation inside snprintf"); |
|||
#endif |
|||
// Suppress the warning about nonliteral format string.
|
|||
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; |
|||
return precision < 0 ? snprintf_ptr(buf, size, format, value) |
|||
: snprintf_ptr(buf, size, format, precision, value); |
|||
} |
|||
} // namespace detail
|
|||
|
|||
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>; |
|||
|
|||
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
|||
int (*instantiate_format_float)(double, int, detail::float_specs, |
|||
detail::buffer<char>&) = detail::format_float; |
|||
|
|||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR |
|||
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc); |
|||
template FMT_API std::locale detail::locale_ref::get<std::locale>() const; |
|||
#endif |
|||
|
|||
// Explicit instantiations for char.
|
|||
|
|||
template FMT_API std::string detail::grouping_impl<char>(locale_ref); |
|||
template FMT_API char detail::thousands_sep_impl(locale_ref); |
|||
template FMT_API char detail::decimal_point_impl(locale_ref); |
|||
|
|||
template FMT_API void detail::buffer<char>::append(const char*, const char*); |
|||
|
|||
template FMT_API FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to( |
|||
detail::buffer<char>&, string_view, |
|||
basic_format_args<FMT_BUFFER_CONTEXT(char)>); |
|||
|
|||
template FMT_API int detail::snprintf_float(double, int, detail::float_specs, |
|||
detail::buffer<char>&); |
|||
template FMT_API int detail::snprintf_float(long double, int, |
|||
detail::float_specs, |
|||
detail::buffer<char>&); |
|||
template FMT_API int detail::format_float(double, int, detail::float_specs, |
|||
detail::buffer<char>&); |
|||
template FMT_API int detail::format_float(long double, int, detail::float_specs, |
|||
detail::buffer<char>&); |
|||
|
|||
// Explicit instantiations for wchar_t.
|
|||
|
|||
template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref); |
|||
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref); |
|||
template FMT_API wchar_t detail::decimal_point_impl(locale_ref); |
|||
|
|||
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*, |
|||
const wchar_t*); |
|||
FMT_END_NAMESPACE |
File diff suppressed because it is too large
@ -1,78 +0,0 @@ |
|||
// Formatting library for C++ - std::locale support
|
|||
//
|
|||
// Copyright (c) 2012 - present, Victor Zverovich
|
|||
// All rights reserved.
|
|||
//
|
|||
// For the license information refer to format.h.
|
|||
|
|||
#ifndef FMT_LOCALE_H_ |
|||
#define FMT_LOCALE_H_ |
|||
|
|||
#include <locale> |
|||
|
|||
#include "format.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
|
|||
namespace detail { |
|||
template <typename Char> |
|||
typename buffer_context<Char>::iterator vformat_to( |
|||
const std::locale& loc, buffer<Char>& buf, |
|||
basic_string_view<Char> format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { |
|||
using af = arg_formatter<typename buffer_context<Char>::iterator, Char>; |
|||
return vformat_to<af>(buffer_appender<Char>(buf), to_string_view(format_str), |
|||
args, detail::locale_ref(loc)); |
|||
} |
|||
|
|||
template <typename Char> |
|||
std::basic_string<Char> vformat( |
|||
const std::locale& loc, basic_string_view<Char> format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { |
|||
basic_memory_buffer<Char> buffer; |
|||
detail::vformat_to(loc, buffer, format_str, args); |
|||
return fmt::to_string(buffer); |
|||
} |
|||
} // namespace detail
|
|||
|
|||
template <typename S, typename Char = char_t<S>> |
|||
inline std::basic_string<Char> vformat( |
|||
const std::locale& loc, const S& format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { |
|||
return detail::vformat(loc, to_string_view(format_str), args); |
|||
} |
|||
|
|||
template <typename S, typename... Args, typename Char = char_t<S>> |
|||
inline std::basic_string<Char> format(const std::locale& loc, |
|||
const S& format_str, Args&&... args) { |
|||
return detail::vformat( |
|||
loc, to_string_view(format_str), |
|||
fmt::make_args_checked<Args...>(format_str, args...)); |
|||
} |
|||
|
|||
template <typename S, typename OutputIt, typename... Args, |
|||
typename Char = enable_if_t< |
|||
detail::is_output_iterator<OutputIt>::value, char_t<S>>> |
|||
inline OutputIt vformat_to( |
|||
OutputIt out, const std::locale& loc, const S& format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { |
|||
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out)); |
|||
using af = |
|||
detail::arg_formatter<typename buffer_context<Char>::iterator, Char>; |
|||
vformat_to<af>(detail::buffer_appender<Char>(buf), to_string_view(format_str), |
|||
args, detail::locale_ref(loc)); |
|||
return detail::get_iterator(buf); |
|||
} |
|||
|
|||
template <typename OutputIt, typename S, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&& |
|||
detail::is_string<S>::value)> |
|||
inline OutputIt format_to(OutputIt out, const std::locale& loc, |
|||
const S& format_str, Args&&... args) { |
|||
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); |
|||
return vformat_to(out, loc, to_string_view(format_str), vargs); |
|||
} |
|||
|
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_LOCALE_H_
|
@ -1,177 +0,0 @@ |
|||
// Formatting library for C++ - std::ostream support
|
|||
//
|
|||
// Copyright (c) 2012 - present, Victor Zverovich
|
|||
// All rights reserved.
|
|||
//
|
|||
// For the license information refer to format.h.
|
|||
|
|||
#ifndef FMT_OSTREAM_H_ |
|||
#define FMT_OSTREAM_H_ |
|||
|
|||
#include <ostream> |
|||
|
|||
#include "format.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
|
|||
template <typename Char> class basic_printf_parse_context; |
|||
template <typename OutputIt, typename Char> class basic_printf_context; |
|||
|
|||
namespace detail { |
|||
|
|||
template <class Char> class formatbuf : public std::basic_streambuf<Char> { |
|||
private: |
|||
using int_type = typename std::basic_streambuf<Char>::int_type; |
|||
using traits_type = typename std::basic_streambuf<Char>::traits_type; |
|||
|
|||
buffer<Char>& buffer_; |
|||
|
|||
public: |
|||
formatbuf(buffer<Char>& buf) : buffer_(buf) {} |
|||
|
|||
protected: |
|||
// The put-area is actually always empty. This makes the implementation
|
|||
// simpler and has the advantage that the streambuf and the buffer are always
|
|||
// in sync and sputc never writes into uninitialized memory. The obvious
|
|||
// disadvantage is that each call to sputc always results in a (virtual) call
|
|||
// to overflow. There is no disadvantage here for sputn since this always
|
|||
// results in a call to xsputn.
|
|||
|
|||
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { |
|||
if (!traits_type::eq_int_type(ch, traits_type::eof())) |
|||
buffer_.push_back(static_cast<Char>(ch)); |
|||
return ch; |
|||
} |
|||
|
|||
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE { |
|||
buffer_.append(s, s + count); |
|||
return count; |
|||
} |
|||
}; |
|||
|
|||
struct converter { |
|||
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T); |
|||
}; |
|||
|
|||
template <typename Char> struct test_stream : std::basic_ostream<Char> { |
|||
private: |
|||
void_t<> operator<<(converter); |
|||
}; |
|||
|
|||
// Hide insertion operators for built-in types.
|
|||
template <typename Char, typename Traits> |
|||
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char); |
|||
template <typename Char, typename Traits> |
|||
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char); |
|||
template <typename Traits> |
|||
void_t<> operator<<(std::basic_ostream<char, Traits>&, char); |
|||
template <typename Traits> |
|||
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char); |
|||
template <typename Traits> |
|||
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char); |
|||
|
|||
// Checks if T has a user-defined operator<< (e.g. not a member of
|
|||
// std::ostream).
|
|||
template <typename T, typename Char> class is_streamable { |
|||
private: |
|||
template <typename U> |
|||
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>() |
|||
<< std::declval<U>()), |
|||
void_t<>>::value> |
|||
test(int); |
|||
|
|||
template <typename> static std::false_type test(...); |
|||
|
|||
using result = decltype(test<T>(0)); |
|||
|
|||
public: |
|||
static const bool value = result::value; |
|||
}; |
|||
|
|||
// Write the content of buf to os.
|
|||
template <typename Char> |
|||
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { |
|||
const Char* buf_data = buf.data(); |
|||
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; |
|||
unsigned_streamsize size = buf.size(); |
|||
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>()); |
|||
do { |
|||
unsigned_streamsize n = size <= max_size ? size : max_size; |
|||
os.write(buf_data, static_cast<std::streamsize>(n)); |
|||
buf_data += n; |
|||
size -= n; |
|||
} while (size != 0); |
|||
} |
|||
|
|||
template <typename Char, typename T> |
|||
void format_value(buffer<Char>& buf, const T& value, |
|||
locale_ref loc = locale_ref()) { |
|||
formatbuf<Char> format_buf(buf); |
|||
std::basic_ostream<Char> output(&format_buf); |
|||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) |
|||
if (loc) output.imbue(loc.get<std::locale>()); |
|||
#endif |
|||
output << value; |
|||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); |
|||
buf.try_resize(buf.size()); |
|||
} |
|||
|
|||
// Formats an object of type T that has an overloaded ostream operator<<.
|
|||
template <typename T, typename Char> |
|||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> |
|||
: private formatter<basic_string_view<Char>, Char> { |
|||
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) |
|||
-> decltype(ctx.begin()) { |
|||
return formatter<basic_string_view<Char>, Char>::parse(ctx); |
|||
} |
|||
template <typename ParseCtx, |
|||
FMT_ENABLE_IF(std::is_same< |
|||
ParseCtx, basic_printf_parse_context<Char>>::value)> |
|||
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) { |
|||
return ctx.begin(); |
|||
} |
|||
|
|||
template <typename OutputIt> |
|||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) |
|||
-> OutputIt { |
|||
basic_memory_buffer<Char> buffer; |
|||
format_value(buffer, value, ctx.locale()); |
|||
basic_string_view<Char> str(buffer.data(), buffer.size()); |
|||
return formatter<basic_string_view<Char>, Char>::format(str, ctx); |
|||
} |
|||
template <typename OutputIt> |
|||
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx) |
|||
-> OutputIt { |
|||
basic_memory_buffer<Char> buffer; |
|||
format_value(buffer, value, ctx.locale()); |
|||
return std::copy(buffer.begin(), buffer.end(), ctx.out()); |
|||
} |
|||
}; |
|||
} // namespace detail
|
|||
|
|||
template <typename Char> |
|||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { |
|||
basic_memory_buffer<Char> buffer; |
|||
detail::vformat_to(buffer, format_str, args); |
|||
detail::write_buffer(os, buffer); |
|||
} |
|||
|
|||
/**
|
|||
\rst |
|||
Prints formatted data to the stream *os*. |
|||
|
|||
**Example**:: |
|||
|
|||
fmt::print(cerr, "Don't {}!", "panic"); |
|||
\endrst |
|||
*/ |
|||
template <typename S, typename... Args, |
|||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> |
|||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) { |
|||
vprint(os, to_string_view(format_str), |
|||
fmt::make_args_checked<Args...>(format_str, args...)); |
|||
} |
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_OSTREAM_H_
|
@ -1,2 +0,0 @@ |
|||
#include "os.h" |
|||
#warning "fmt/posix.h is deprecated; use fmt/os.h instead" |
@ -1,393 +0,0 @@ |
|||
// Formatting library for C++ - experimental range support
|
|||
//
|
|||
// Copyright (c) 2012 - present, Victor Zverovich
|
|||
// All rights reserved.
|
|||
//
|
|||
// For the license information refer to format.h.
|
|||
//
|
|||
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
|||
// All Rights Reserved
|
|||
// {fmt} support for ranges, containers and types tuple interface.
|
|||
|
|||
#ifndef FMT_RANGES_H_ |
|||
#define FMT_RANGES_H_ |
|||
|
|||
#include <initializer_list> |
|||
#include <type_traits> |
|||
|
|||
#include "format.h" |
|||
|
|||
// output only up to N items from the range.
|
|||
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT |
|||
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256 |
|||
#endif |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
|
|||
template <typename Char> struct formatting_base { |
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
return ctx.begin(); |
|||
} |
|||
}; |
|||
|
|||
template <typename Char, typename Enable = void> |
|||
struct formatting_range : formatting_base<Char> { |
|||
static FMT_CONSTEXPR_DECL const size_t range_length_limit = |
|||
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
|||
// range.
|
|||
Char prefix; |
|||
Char delimiter; |
|||
Char postfix; |
|||
formatting_range() : prefix('{'), delimiter(','), postfix('}') {} |
|||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; |
|||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; |
|||
}; |
|||
|
|||
template <typename Char, typename Enable = void> |
|||
struct formatting_tuple : formatting_base<Char> { |
|||
Char prefix; |
|||
Char delimiter; |
|||
Char postfix; |
|||
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {} |
|||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; |
|||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; |
|||
}; |
|||
|
|||
namespace detail { |
|||
|
|||
template <typename RangeT, typename OutputIterator> |
|||
OutputIterator copy(const RangeT& range, OutputIterator out) { |
|||
for (auto it = range.begin(), end = range.end(); it != end; ++it) |
|||
*out++ = *it; |
|||
return out; |
|||
} |
|||
|
|||
template <typename OutputIterator> |
|||
OutputIterator copy(const char* str, OutputIterator out) { |
|||
while (*str) *out++ = *str++; |
|||
return out; |
|||
} |
|||
|
|||
template <typename OutputIterator> |
|||
OutputIterator copy(char ch, OutputIterator out) { |
|||
*out++ = ch; |
|||
return out; |
|||
} |
|||
|
|||
/// Return true value if T has std::string interface, like std::string_view.
|
|||
template <typename T> class is_like_std_string { |
|||
template <typename U> |
|||
static auto check(U* p) |
|||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); |
|||
template <typename> static void check(...); |
|||
|
|||
public: |
|||
static FMT_CONSTEXPR_DECL const bool value = |
|||
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value; |
|||
}; |
|||
|
|||
template <typename Char> |
|||
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {}; |
|||
|
|||
template <typename... Ts> struct conditional_helper {}; |
|||
|
|||
template <typename T, typename _ = void> struct is_range_ : std::false_type {}; |
|||
|
|||
#if !FMT_MSC_VER || FMT_MSC_VER > 1800 |
|||
template <typename T> |
|||
struct is_range_< |
|||
T, conditional_t<false, |
|||
conditional_helper<decltype(std::declval<T>().begin()), |
|||
decltype(std::declval<T>().end())>, |
|||
void>> : std::true_type {}; |
|||
#endif |
|||
|
|||
/// tuple_size and tuple_element check.
|
|||
template <typename T> class is_tuple_like_ { |
|||
template <typename U> |
|||
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int()); |
|||
template <typename> static void check(...); |
|||
|
|||
public: |
|||
static FMT_CONSTEXPR_DECL const bool value = |
|||
!std::is_void<decltype(check<T>(nullptr))>::value; |
|||
}; |
|||
|
|||
// Check for integer_sequence
|
|||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 |
|||
template <typename T, T... N> |
|||
using integer_sequence = std::integer_sequence<T, N...>; |
|||
template <size_t... N> using index_sequence = std::index_sequence<N...>; |
|||
template <size_t N> using make_index_sequence = std::make_index_sequence<N>; |
|||
#else |
|||
template <typename T, T... N> struct integer_sequence { |
|||
using value_type = T; |
|||
|
|||
static FMT_CONSTEXPR size_t size() { return sizeof...(N); } |
|||
}; |
|||
|
|||
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>; |
|||
|
|||
template <typename T, size_t N, T... Ns> |
|||
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {}; |
|||
template <typename T, T... Ns> |
|||
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {}; |
|||
|
|||
template <size_t N> |
|||
using make_index_sequence = make_integer_sequence<size_t, N>; |
|||
#endif |
|||
|
|||
template <class Tuple, class F, size_t... Is> |
|||
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT { |
|||
using std::get; |
|||
// using free function get<I>(T) now.
|
|||
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; |
|||
(void)_; // blocks warnings
|
|||
} |
|||
|
|||
template <class T> |
|||
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes( |
|||
T const&) { |
|||
return {}; |
|||
} |
|||
|
|||
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) { |
|||
const auto indexes = get_indexes(tup); |
|||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); |
|||
} |
|||
|
|||
template <typename Range> |
|||
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>; |
|||
|
|||
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string< |
|||
typename std::decay<Arg>::type>::value)> |
|||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { |
|||
return add_space ? " {}" : "{}"; |
|||
} |
|||
|
|||
template <typename Arg, FMT_ENABLE_IF(is_like_std_string< |
|||
typename std::decay<Arg>::type>::value)> |
|||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { |
|||
return add_space ? " \"{}\"" : "\"{}\""; |
|||
} |
|||
|
|||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) { |
|||
return add_space ? " \"{}\"" : "\"{}\""; |
|||
} |
|||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) { |
|||
return add_space ? L" \"{}\"" : L"\"{}\""; |
|||
} |
|||
|
|||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) { |
|||
return add_space ? " '{}'" : "'{}'"; |
|||
} |
|||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) { |
|||
return add_space ? L" '{}'" : L"'{}'"; |
|||
} |
|||
} // namespace detail
|
|||
|
|||
template <typename T> struct is_tuple_like { |
|||
static FMT_CONSTEXPR_DECL const bool value = |
|||
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value; |
|||
}; |
|||
|
|||
template <typename TupleT, typename Char> |
|||
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> { |
|||
private: |
|||
// C++11 generic lambda for format()
|
|||
template <typename FormatContext> struct format_each { |
|||
template <typename T> void operator()(const T& v) { |
|||
if (i > 0) { |
|||
if (formatting.add_prepostfix_space) { |
|||
*out++ = ' '; |
|||
} |
|||
out = detail::copy(formatting.delimiter, out); |
|||
} |
|||
out = format_to(out, |
|||
detail::format_str_quoted( |
|||
(formatting.add_delimiter_spaces && i > 0), v), |
|||
v); |
|||
++i; |
|||
} |
|||
|
|||
formatting_tuple<Char>& formatting; |
|||
size_t& i; |
|||
typename std::add_lvalue_reference<decltype( |
|||
std::declval<FormatContext>().out())>::type out; |
|||
}; |
|||
|
|||
public: |
|||
formatting_tuple<Char> formatting; |
|||
|
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
return formatting.parse(ctx); |
|||
} |
|||
|
|||
template <typename FormatContext = format_context> |
|||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { |
|||
auto out = ctx.out(); |
|||
size_t i = 0; |
|||
detail::copy(formatting.prefix, out); |
|||
|
|||
detail::for_each(values, format_each<FormatContext>{formatting, i, out}); |
|||
if (formatting.add_prepostfix_space) { |
|||
*out++ = ' '; |
|||
} |
|||
detail::copy(formatting.postfix, out); |
|||
|
|||
return ctx.out(); |
|||
} |
|||
}; |
|||
|
|||
template <typename T, typename Char> struct is_range { |
|||
static FMT_CONSTEXPR_DECL const bool value = |
|||
detail::is_range_<T>::value && !detail::is_like_std_string<T>::value && |
|||
!std::is_convertible<T, std::basic_string<Char>>::value && |
|||
!std::is_constructible<detail::std_string_view<Char>, T>::value; |
|||
}; |
|||
|
|||
template <typename T, typename Char> |
|||
struct formatter< |
|||
T, Char, |
|||
enable_if_t<fmt::is_range<T, Char>::value |
|||
// Workaround a bug in MSVC 2017 and earlier.
|
|||
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927 |
|||
&& has_formatter<detail::value_type<T>, format_context>::value |
|||
#endif |
|||
>> { |
|||
formatting_range<Char> formatting; |
|||
|
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
return formatting.parse(ctx); |
|||
} |
|||
|
|||
template <typename FormatContext> |
|||
typename FormatContext::iterator format(const T& values, FormatContext& ctx) { |
|||
auto out = detail::copy(formatting.prefix, ctx.out()); |
|||
size_t i = 0; |
|||
auto it = values.begin(); |
|||
auto end = values.end(); |
|||
for (; it != end; ++it) { |
|||
if (i > 0) { |
|||
if (formatting.add_prepostfix_space) *out++ = ' '; |
|||
out = detail::copy(formatting.delimiter, out); |
|||
} |
|||
out = format_to(out, |
|||
detail::format_str_quoted( |
|||
(formatting.add_delimiter_spaces && i > 0), *it), |
|||
*it); |
|||
if (++i > formatting.range_length_limit) { |
|||
out = format_to(out, " ... <other elements>"); |
|||
break; |
|||
} |
|||
} |
|||
if (formatting.add_prepostfix_space) *out++ = ' '; |
|||
return detail::copy(formatting.postfix, out); |
|||
} |
|||
}; |
|||
|
|||
template <typename Char, typename... T> struct tuple_arg_join : detail::view { |
|||
const std::tuple<T...>& tuple; |
|||
basic_string_view<Char> sep; |
|||
|
|||
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s) |
|||
: tuple{t}, sep{s} {} |
|||
}; |
|||
|
|||
template <typename Char, typename... T> |
|||
struct formatter<tuple_arg_join<Char, T...>, Char> { |
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
return ctx.begin(); |
|||
} |
|||
|
|||
template <typename FormatContext> |
|||
typename FormatContext::iterator format( |
|||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) { |
|||
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{}); |
|||
} |
|||
|
|||
private: |
|||
template <typename FormatContext, size_t... N> |
|||
typename FormatContext::iterator format( |
|||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx, |
|||
detail::index_sequence<N...>) { |
|||
return format_args(value, ctx, std::get<N>(value.tuple)...); |
|||
} |
|||
|
|||
template <typename FormatContext> |
|||
typename FormatContext::iterator format_args( |
|||
const tuple_arg_join<Char, T...>&, FormatContext& ctx) { |
|||
// NOTE: for compilers that support C++17, this empty function instantiation
|
|||
// can be replaced with a constexpr branch in the variadic overload.
|
|||
return ctx.out(); |
|||
} |
|||
|
|||
template <typename FormatContext, typename Arg, typename... Args> |
|||
typename FormatContext::iterator format_args( |
|||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx, |
|||
const Arg& arg, const Args&... args) { |
|||
using base = formatter<typename std::decay<Arg>::type, Char>; |
|||
auto out = ctx.out(); |
|||
out = base{}.format(arg, ctx); |
|||
if (sizeof...(Args) > 0) { |
|||
out = std::copy(value.sep.begin(), value.sep.end(), out); |
|||
ctx.advance_to(out); |
|||
return format_args(value, ctx, args...); |
|||
} |
|||
return out; |
|||
} |
|||
}; |
|||
|
|||
/**
|
|||
\rst |
|||
Returns an object that formats `tuple` with elements separated by `sep`. |
|||
|
|||
**Example**:: |
|||
|
|||
std::tuple<int, char> t = {1, 'a'}; |
|||
fmt::print("{}", fmt::join(t, ", ")); |
|||
// Output: "1, a"
|
|||
\endrst |
|||
*/ |
|||
template <typename... T> |
|||
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple, |
|||
string_view sep) { |
|||
return {tuple, sep}; |
|||
} |
|||
|
|||
template <typename... T> |
|||
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple, |
|||
wstring_view sep) { |
|||
return {tuple, sep}; |
|||
} |
|||
|
|||
/**
|
|||
\rst |
|||
Returns an object that formats `initializer_list` with elements separated by |
|||
`sep`. |
|||
|
|||
**Example**:: |
|||
|
|||
fmt::print("{}", fmt::join({1, 2, 3}, ", ")); |
|||
// Output: "1, 2, 3"
|
|||
\endrst |
|||
*/ |
|||
template <typename T> |
|||
arg_join<const T*, const T*, char> join(std::initializer_list<T> list, |
|||
string_view sep) { |
|||
return join(std::begin(list), std::end(list), sep); |
|||
} |
|||
|
|||
template <typename T> |
|||
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list, |
|||
wstring_view sep) { |
|||
return join(std::begin(list), std::end(list), sep); |
|||
} |
|||
|
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_RANGES_H_
|
@ -0,0 +1,41 @@ |
|||
Copyright (c) 2014-2021, The Monero Project |
|||
|
|||
All rights reserved. |
|||
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions are met: |
|||
|
|||
1. Redistributions of source code must retain the above copyright notice, this |
|||
list of conditions and the following disclaimer. |
|||
|
|||
2. Redistributions in binary form must reproduce the above copyright notice, |
|||
this list of conditions and the following disclaimer in the documentation |
|||
and/or other materials provided with the distribution. |
|||
|
|||
3. Neither the name of the copyright holder nor the names of its contributors |
|||
may be used to endorse or promote products derived from this software without |
|||
specific prior written permission. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
|||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
|
|||
Parts of the project are originally copyright (c) 2012-2013 The Cryptonote |
|||
developers |
|||
|
|||
Parts of the project are originally copyright (c) 2014 The Boolberry |
|||
developers, distributed under the MIT licence: |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|||
|
@ -0,0 +1,20 @@ |
|||
Contributing to {fmt} |
|||
===================== |
|||
|
|||
By submitting a pull request or a patch, you represent that you have the right |
|||
to license your contribution to the {fmt} project owners and the community, |
|||
agree that your contributions are licensed under the {fmt} license, and agree |
|||
to future changes to the licensing. |
|||
|
|||
All C++ code must adhere to [Google C++ Style Guide]( |
|||
https://google.github.io/styleguide/cppguide.html) with the following |
|||
exceptions: |
|||
|
|||
* Exceptions are permitted |
|||
* snake_case should be used instead of UpperCamelCase for function and type |
|||
names |
|||
|
|||
All documentation must adhere to the [Google Developer Documentation Style |
|||
Guide](https://developers.google.com/style). |
|||
|
|||
Thanks for contributing! |
File diff suppressed because it is too large
@ -0,0 +1,234 @@ |
|||
// Formatting library for C++ - dynamic format arguments
|
|||
//
|
|||
// Copyright (c) 2012 - present, Victor Zverovich
|
|||
// All rights reserved.
|
|||
//
|
|||
// For the license information refer to format.h.
|
|||
|
|||
#ifndef FMT_ARGS_H_ |
|||
#define FMT_ARGS_H_ |
|||
|
|||
#include <functional> // std::reference_wrapper |
|||
#include <memory> // std::unique_ptr |
|||
#include <vector> |
|||
|
|||
#include "core.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
|
|||
namespace detail { |
|||
|
|||
template <typename T> struct is_reference_wrapper : std::false_type {}; |
|||
template <typename T> |
|||
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {}; |
|||
|
|||
template <typename T> const T& unwrap(const T& v) { return v; } |
|||
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) { |
|||
return static_cast<const T&>(v); |
|||
} |
|||
|
|||
class dynamic_arg_list { |
|||
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
|
|||
// templates it doesn't complain about inability to deduce single translation
|
|||
// unit for placing vtable. So storage_node_base is made a fake template.
|
|||
template <typename = void> struct node { |
|||
virtual ~node() = default; |
|||
std::unique_ptr<node<>> next; |
|||
}; |
|||
|
|||
template <typename T> struct typed_node : node<> { |
|||
T value; |
|||
|
|||
template <typename Arg> |
|||
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} |
|||
|
|||
template <typename Char> |
|||
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg) |
|||
: value(arg.data(), arg.size()) {} |
|||
}; |
|||
|
|||
std::unique_ptr<node<>> head_; |
|||
|
|||
public: |
|||
template <typename T, typename Arg> const T& push(const Arg& arg) { |
|||
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg)); |
|||
auto& value = new_node->value; |
|||
new_node->next = std::move(head_); |
|||
head_ = std::move(new_node); |
|||
return value; |
|||
} |
|||
}; |
|||
} // namespace detail
|
|||
|
|||
/**
|
|||
\rst |
|||
A dynamic version of `fmt::format_arg_store`. |
|||
It's equipped with a storage to potentially temporary objects which lifetimes |
|||
could be shorter than the format arguments object. |
|||
|
|||
It can be implicitly converted into `~fmt::basic_format_args` for passing |
|||
into type-erased formatting functions such as `~fmt::vformat`. |
|||
\endrst |
|||
*/ |
|||
template <typename Context> |
|||
class dynamic_format_arg_store |
|||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 |
|||
// Workaround a GCC template argument substitution bug.
|
|||
: public basic_format_args<Context> |
|||
#endif |
|||
{ |
|||
private: |
|||
using char_type = typename Context::char_type; |
|||
|
|||
template <typename T> struct need_copy { |
|||
static constexpr detail::type mapped_type = |
|||
detail::mapped_type_constant<T, Context>::value; |
|||
|
|||
enum { |
|||
value = !(detail::is_reference_wrapper<T>::value || |
|||
std::is_same<T, basic_string_view<char_type>>::value || |
|||
std::is_same<T, detail::std_string_view<char_type>>::value || |
|||
(mapped_type != detail::type::cstring_type && |
|||
mapped_type != detail::type::string_type && |
|||
mapped_type != detail::type::custom_type)) |
|||
}; |
|||
}; |
|||
|
|||
template <typename T> |
|||
using stored_type = conditional_t< |
|||
std::is_convertible<T, std::basic_string<char_type>>::value && |
|||
!detail::is_reference_wrapper<T>::value, |
|||
std::basic_string<char_type>, T>; |
|||
|
|||
// Storage of basic_format_arg must be contiguous.
|
|||
std::vector<basic_format_arg<Context>> data_; |
|||
std::vector<detail::named_arg_info<char_type>> named_info_; |
|||
|
|||
// Storage of arguments not fitting into basic_format_arg must grow
|
|||
// without relocation because items in data_ refer to it.
|
|||
detail::dynamic_arg_list dynamic_args_; |
|||
|
|||
friend class basic_format_args<Context>; |
|||
|
|||
unsigned long long get_types() const { |
|||
return detail::is_unpacked_bit | data_.size() | |
|||
(named_info_.empty() |
|||
? 0ULL |
|||
: static_cast<unsigned long long>(detail::has_named_args_bit)); |
|||
} |
|||
|
|||
const basic_format_arg<Context>* data() const { |
|||
return named_info_.empty() ? data_.data() : data_.data() + 1; |
|||
} |
|||
|
|||
template <typename T> void emplace_arg(const T& arg) { |
|||
data_.emplace_back(detail::make_arg<Context>(arg)); |
|||
} |
|||
|
|||
template <typename T> |
|||
void emplace_arg(const detail::named_arg<char_type, T>& arg) { |
|||
if (named_info_.empty()) { |
|||
constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr}; |
|||
data_.insert(data_.begin(), {zero_ptr, 0}); |
|||
} |
|||
data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value))); |
|||
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) { |
|||
data->pop_back(); |
|||
}; |
|||
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)> |
|||
guard{&data_, pop_one}; |
|||
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)}); |
|||
data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; |
|||
guard.release(); |
|||
} |
|||
|
|||
public: |
|||
constexpr dynamic_format_arg_store() = default; |
|||
|
|||
/**
|
|||
\rst |
|||
Adds an argument into the dynamic store for later passing to a formatting |
|||
function. |
|||
|
|||
Note that custom types and string types (but not string views) are copied |
|||
into the store dynamically allocating memory if necessary. |
|||
|
|||
**Example**:: |
|||
|
|||
fmt::dynamic_format_arg_store<fmt::format_context> store; |
|||
store.push_back(42); |
|||
store.push_back("abc"); |
|||
store.push_back(1.5f); |
|||
std::string result = fmt::vformat("{} and {} and {}", store); |
|||
\endrst |
|||
*/ |
|||
template <typename T> void push_back(const T& arg) { |
|||
if (detail::const_check(need_copy<T>::value)) |
|||
emplace_arg(dynamic_args_.push<stored_type<T>>(arg)); |
|||
else |
|||
emplace_arg(detail::unwrap(arg)); |
|||
} |
|||
|
|||
/**
|
|||
\rst |
|||
Adds a reference to the argument into the dynamic store for later passing to |
|||
a formatting function. |
|||
|
|||
**Example**:: |
|||
|
|||
fmt::dynamic_format_arg_store<fmt::format_context> store; |
|||
char band[] = "Rolling Stones"; |
|||
store.push_back(std::cref(band)); |
|||
band[9] = 'c'; // Changing str affects the output.
|
|||
std::string result = fmt::vformat("{}", store); |
|||
// result == "Rolling Scones"
|
|||
\endrst |
|||
*/ |
|||
template <typename T> void push_back(std::reference_wrapper<T> arg) { |
|||
static_assert( |
|||
need_copy<T>::value, |
|||
"objects of built-in types and string views are always copied"); |
|||
emplace_arg(arg.get()); |
|||
} |
|||
|
|||
/**
|
|||
Adds named argument into the dynamic store for later passing to a formatting |
|||
function. ``std::reference_wrapper`` is supported to avoid copying of the |
|||
argument. The name is always copied into the store. |
|||
*/ |
|||
template <typename T> |
|||
void push_back(const detail::named_arg<char_type, T>& arg) { |
|||
const char_type* arg_name = |
|||
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str(); |
|||
if (detail::const_check(need_copy<T>::value)) { |
|||
emplace_arg( |
|||
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value))); |
|||
} else { |
|||
emplace_arg(fmt::arg(arg_name, arg.value)); |
|||
} |
|||
} |
|||
|
|||
/** Erase all elements from the store */ |
|||
void clear() { |
|||
data_.clear(); |
|||
named_info_.clear(); |
|||
dynamic_args_ = detail::dynamic_arg_list(); |
|||
} |
|||
|
|||
/**
|
|||
\rst |
|||
Reserves space to store at least *new_cap* arguments including |
|||
*new_cap_named* named arguments. |
|||
\endrst |
|||
*/ |
|||
void reserve(size_t new_cap, size_t new_cap_named) { |
|||
FMT_ASSERT(new_cap >= new_cap_named, |
|||
"Set of arguments includes set of named arguments"); |
|||
data_.reserve(new_cap); |
|||
named_info_.reserve(new_cap_named); |
|||
} |
|||
}; |
|||
|
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_ARGS_H_
|
File diff suppressed because it is too large
@ -0,0 +1,603 @@ |
|||
// Formatting library for C++ - experimental format string compilation
|
|||
//
|
|||
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
|
|||
// All rights reserved.
|
|||
//
|
|||
// For the license information refer to format.h.
|
|||
|
|||
#ifndef FMT_COMPILE_H_ |
|||
#define FMT_COMPILE_H_ |
|||
|
|||
#include "format.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
namespace detail { |
|||
|
|||
template <typename Char, typename InputIt> |
|||
inline counting_iterator copy_str(InputIt begin, InputIt end, |
|||
counting_iterator it) { |
|||
return it + (end - begin); |
|||
} |
|||
|
|||
template <typename OutputIt> class truncating_iterator_base { |
|||
protected: |
|||
OutputIt out_; |
|||
size_t limit_; |
|||
size_t count_ = 0; |
|||
|
|||
truncating_iterator_base() : out_(), limit_(0) {} |
|||
|
|||
truncating_iterator_base(OutputIt out, size_t limit) |
|||
: out_(out), limit_(limit) {} |
|||
|
|||
public: |
|||
using iterator_category = std::output_iterator_tag; |
|||
using value_type = typename std::iterator_traits<OutputIt>::value_type; |
|||
using difference_type = std::ptrdiff_t; |
|||
using pointer = void; |
|||
using reference = void; |
|||
FMT_UNCHECKED_ITERATOR(truncating_iterator_base); |
|||
|
|||
OutputIt base() const { return out_; } |
|||
size_t count() const { return count_; } |
|||
}; |
|||
|
|||
// An output iterator that truncates the output and counts the number of objects
|
|||
// written to it.
|
|||
template <typename OutputIt, |
|||
typename Enable = typename std::is_void< |
|||
typename std::iterator_traits<OutputIt>::value_type>::type> |
|||
class truncating_iterator; |
|||
|
|||
template <typename OutputIt> |
|||
class truncating_iterator<OutputIt, std::false_type> |
|||
: public truncating_iterator_base<OutputIt> { |
|||
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_; |
|||
|
|||
public: |
|||
using value_type = typename truncating_iterator_base<OutputIt>::value_type; |
|||
|
|||
truncating_iterator() = default; |
|||
|
|||
truncating_iterator(OutputIt out, size_t limit) |
|||
: truncating_iterator_base<OutputIt>(out, limit) {} |
|||
|
|||
truncating_iterator& operator++() { |
|||
if (this->count_++ < this->limit_) ++this->out_; |
|||
return *this; |
|||
} |
|||
|
|||
truncating_iterator operator++(int) { |
|||
auto it = *this; |
|||
++*this; |
|||
return it; |
|||
} |
|||
|
|||
value_type& operator*() const { |
|||
return this->count_ < this->limit_ ? *this->out_ : blackhole_; |
|||
} |
|||
}; |
|||
|
|||
template <typename OutputIt> |
|||
class truncating_iterator<OutputIt, std::true_type> |
|||
: public truncating_iterator_base<OutputIt> { |
|||
public: |
|||
truncating_iterator() = default; |
|||
|
|||
truncating_iterator(OutputIt out, size_t limit) |
|||
: truncating_iterator_base<OutputIt>(out, limit) {} |
|||
|
|||
template <typename T> truncating_iterator& operator=(T val) { |
|||
if (this->count_++ < this->limit_) *this->out_++ = val; |
|||
return *this; |
|||
} |
|||
|
|||
truncating_iterator& operator++() { return *this; } |
|||
truncating_iterator& operator++(int) { return *this; } |
|||
truncating_iterator& operator*() { return *this; } |
|||
}; |
|||
|
|||
// A compile-time string which is compiled into fast formatting code.
|
|||
class compiled_string {}; |
|||
|
|||
template <typename S> |
|||
struct is_compiled_string : std::is_base_of<compiled_string, S> {}; |
|||
|
|||
/**
|
|||
\rst |
|||
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 |
|||
``constexpr if`` compiler support. |
|||
|
|||
**Example**:: |
|||
|
|||
// Converts 42 into std::string using the most efficient method and no
|
|||
// runtime format string processing.
|
|||
std::string s = fmt::format(FMT_COMPILE("{}"), 42); |
|||
\endrst |
|||
*/ |
|||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) |
|||
# define FMT_COMPILE(s) \ |
|||
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) |
|||
#else |
|||
# define FMT_COMPILE(s) FMT_STRING(s) |
|||
#endif |
|||
|
|||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS |
|||
template <typename Char, size_t N, |
|||
fmt::detail_exported::fixed_string<Char, N> Str> |
|||
struct udl_compiled_string : compiled_string { |
|||
using char_type = Char; |
|||
explicit constexpr operator basic_string_view<char_type>() const { |
|||
return {Str.data, N - 1}; |
|||
} |
|||
}; |
|||
#endif |
|||
|
|||
template <typename T, typename... Tail> |
|||
const T& first(const T& value, const Tail&...) { |
|||
return value; |
|||
} |
|||
|
|||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) |
|||
template <typename... Args> 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) { |
|||
static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); |
|||
if constexpr (N == 0) |
|||
return first; |
|||
else |
|||
return detail::get<N - 1>(rest...); |
|||
} |
|||
|
|||
template <typename Char, typename... Args> |
|||
constexpr int get_arg_index_by_name(basic_string_view<Char> name, |
|||
type_list<Args...>) { |
|||
return get_arg_index_by_name<Args...>(name); |
|||
} |
|||
|
|||
template <int N, typename> struct get_type_impl; |
|||
|
|||
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> { |
|||
using type = |
|||
remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>; |
|||
}; |
|||
|
|||
template <int N, typename T> |
|||
using get_type = typename get_type_impl<N, T>::type; |
|||
|
|||
template <typename T> struct is_compiled_format : std::false_type {}; |
|||
|
|||
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 { |
|||
return write<Char>(out, data); |
|||
} |
|||
}; |
|||
|
|||
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) { |
|||
return {{&s[pos], size}}; |
|||
} |
|||
|
|||
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 { |
|||
return write<Char>(out, value); |
|||
} |
|||
}; |
|||
|
|||
// 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) { |
|||
const auto& arg = detail::get<N>(args...); |
|||
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) { |
|||
return arg.value; |
|||
} else { |
|||
return arg; |
|||
} |
|||
} |
|||
|
|||
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 { |
|||
using char_type = Char; |
|||
|
|||
template <typename OutputIt, typename... Args> |
|||
constexpr OutputIt format(OutputIt out, const Args&... args) const { |
|||
return write<Char>(out, get_arg_checked<T, N>(args...)); |
|||
} |
|||
}; |
|||
|
|||
template <typename Char, typename T, int N> |
|||
struct is_compiled_format<field<Char, T, N>> : std::true_type {}; |
|||
|
|||
// A replacement field that refers to argument with name.
|
|||
template <typename Char> struct runtime_named_field { |
|||
using char_type = Char; |
|||
basic_string_view<Char> name; |
|||
|
|||
template <typename OutputIt, typename T> |
|||
constexpr static bool 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) { |
|||
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) { |
|||
if (arg_name == arg.name) { |
|||
out = write<Char>(out, arg.value); |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
template <typename OutputIt, typename... Args> |
|||
constexpr OutputIt format(OutputIt out, const Args&... args) const { |
|||
bool found = (try_format_argument(out, name, args) || ...); |
|||
if (!found) { |
|||
FMT_THROW(format_error("argument with specified name is not found")); |
|||
} |
|||
return out; |
|||
} |
|||
}; |
|||
|
|||
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 { |
|||
using char_type = Char; |
|||
formatter<T, Char> fmt; |
|||
|
|||
template <typename OutputIt, typename... Args> |
|||
constexpr FMT_INLINE OutputIt format(OutputIt out, |
|||
const Args&... args) const { |
|||
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); |
|||
} |
|||
}; |
|||
|
|||
template <typename Char, typename T, int N> |
|||
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {}; |
|||
|
|||
template <typename L, typename R> struct concat { |
|||
L lhs; |
|||
R rhs; |
|||
using char_type = typename L::char_type; |
|||
|
|||
template <typename OutputIt, typename... Args> |
|||
constexpr OutputIt format(OutputIt out, const Args&... args) const { |
|||
out = lhs.format(out, args...); |
|||
return rhs.format(out, args...); |
|||
} |
|||
}; |
|||
|
|||
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) { |
|||
return {lhs, rhs}; |
|||
} |
|||
|
|||
struct unknown_format {}; |
|||
|
|||
template <typename Char> |
|||
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) { |
|||
for (size_t size = str.size(); pos != size; ++pos) { |
|||
if (str[pos] == '{' || str[pos] == '}') break; |
|||
} |
|||
return pos; |
|||
} |
|||
|
|||
template <typename Args, size_t POS, int ID, typename S> |
|||
constexpr auto compile_format_string(S format_str); |
|||
|
|||
template <typename Args, size_t POS, int ID, typename T, typename S> |
|||
constexpr auto parse_tail(T head, S format_str) { |
|||
if constexpr (POS != |
|||
basic_string_view<typename S::char_type>(format_str).size()) { |
|||
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str); |
|||
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>, |
|||
unknown_format>()) |
|||
return tail; |
|||
else |
|||
return make_concat(head, tail); |
|||
} else { |
|||
return head; |
|||
} |
|||
} |
|||
|
|||
template <typename T, typename Char> struct parse_specs_result { |
|||
formatter<T, Char> fmt; |
|||
size_t end; |
|||
int next_arg_id; |
|||
}; |
|||
|
|||
constexpr int 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) { |
|||
str.remove_prefix(pos); |
|||
auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, {}, |
|||
next_arg_id); |
|||
auto f = formatter<T, Char>(); |
|||
auto end = f.parse(ctx); |
|||
return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1, |
|||
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; |
|||
} |
|||
|
|||
template <typename Char> struct arg_id_handler { |
|||
arg_ref<Char> arg_id; |
|||
|
|||
constexpr int operator()() { |
|||
FMT_ASSERT(false, "handler cannot be used with automatic indexing"); |
|||
return 0; |
|||
} |
|||
constexpr int operator()(int id) { |
|||
arg_id = arg_ref<Char>(id); |
|||
return 0; |
|||
} |
|||
constexpr int operator()(basic_string_view<Char> id) { |
|||
arg_id = arg_ref<Char>(id); |
|||
return 0; |
|||
} |
|||
|
|||
constexpr void on_error(const char* message) { |
|||
FMT_THROW(format_error(message)); |
|||
} |
|||
}; |
|||
|
|||
template <typename Char> struct parse_arg_id_result { |
|||
arg_ref<Char> arg_id; |
|||
const Char* arg_id_end; |
|||
}; |
|||
|
|||
template <int ID, typename Char> |
|||
constexpr auto parse_arg_id(const Char* begin, const Char* end) { |
|||
auto handler = arg_id_handler<Char>{arg_ref<Char>{}}; |
|||
auto arg_id_end = parse_arg_id(begin, end, handler); |
|||
return parse_arg_id_result<Char>{handler.arg_id, arg_id_end}; |
|||
} |
|||
|
|||
template <typename T, typename Enable = void> struct field_type { |
|||
using type = remove_cvref_t<T>; |
|||
}; |
|||
|
|||
template <typename T> |
|||
struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> { |
|||
using type = remove_cvref_t<decltype(T::value)>; |
|||
}; |
|||
|
|||
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID, |
|||
typename S> |
|||
constexpr auto parse_replacement_field_then_tail(S format_str) { |
|||
using char_type = typename S::char_type; |
|||
constexpr auto str = basic_string_view<char_type>(format_str); |
|||
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); |
|||
if constexpr (c == '}') { |
|||
return parse_tail<Args, END_POS + 1, NEXT_ID>( |
|||
field<char_type, typename field_type<T>::type, ARG_INDEX>(), |
|||
format_str); |
|||
} else if constexpr (c == ':') { |
|||
constexpr auto result = parse_specs<typename field_type<T>::type>( |
|||
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); |
|||
return parse_tail<Args, result.end, result.next_arg_id>( |
|||
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{ |
|||
result.fmt}, |
|||
format_str); |
|||
} |
|||
} |
|||
|
|||
// Compiles a non-empty format string and returns the compiled representation
|
|||
// or unknown_format() on unrecognized input.
|
|||
template <typename Args, size_t POS, int ID, typename S> |
|||
constexpr auto compile_format_string(S format_str) { |
|||
using char_type = typename S::char_type; |
|||
constexpr auto str = basic_string_view<char_type>(format_str); |
|||
if constexpr (str[POS] == '{') { |
|||
if constexpr (POS + 1 == str.size()) |
|||
FMT_THROW(format_error("unmatched '{' in format string")); |
|||
if constexpr (str[POS + 1] == '{') { |
|||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); |
|||
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { |
|||
static_assert(ID != manual_indexing_id, |
|||
"cannot switch from manual to automatic argument indexing"); |
|||
constexpr auto next_id = |
|||
ID != manual_indexing_id ? ID + 1 : manual_indexing_id; |
|||
return parse_replacement_field_then_tail<get_type<ID, Args>, Args, |
|||
POS + 1, ID, next_id>( |
|||
format_str); |
|||
} else { |
|||
constexpr auto arg_id_result = |
|||
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size()); |
|||
constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data(); |
|||
constexpr char_type c = |
|||
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); |
|||
static_assert(c == '}' || c == ':', "missing '}' in format string"); |
|||
if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) { |
|||
static_assert( |
|||
ID == manual_indexing_id || ID == 0, |
|||
"cannot switch from automatic to manual argument indexing"); |
|||
constexpr auto arg_index = arg_id_result.arg_id.val.index; |
|||
return parse_replacement_field_then_tail<get_type<arg_index, Args>, |
|||
Args, arg_id_end_pos, |
|||
arg_index, manual_indexing_id>( |
|||
format_str); |
|||
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { |
|||
constexpr auto arg_index = |
|||
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); |
|||
if constexpr (arg_index != invalid_arg_index) { |
|||
constexpr auto next_id = |
|||
ID != manual_indexing_id ? ID + 1 : manual_indexing_id; |
|||
return parse_replacement_field_then_tail< |
|||
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos, |
|||
arg_index, next_id>(format_str); |
|||
} else { |
|||
if constexpr (c == '}') { |
|||
return parse_tail<Args, arg_id_end_pos + 1, ID>( |
|||
runtime_named_field<char_type>{arg_id_result.arg_id.val.name}, |
|||
format_str); |
|||
} else if constexpr (c == ':') { |
|||
return unknown_format(); // no type info for specs parsing
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
} else if constexpr (str[POS] == '}') { |
|||
if constexpr (POS + 1 == str.size()) |
|||
FMT_THROW(format_error("unmatched '}' in format string")); |
|||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); |
|||
} else { |
|||
constexpr auto end = parse_text(str, POS + 1); |
|||
if constexpr (end - POS > 1) { |
|||
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), |
|||
format_str); |
|||
} else { |
|||
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, |
|||
format_str); |
|||
} |
|||
} |
|||
} |
|||
|
|||
template <typename... Args, typename S, |
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> |
|||
constexpr auto compile(S format_str) { |
|||
constexpr auto str = basic_string_view<typename S::char_type>(format_str); |
|||
if constexpr (str.size() == 0) { |
|||
return detail::make_text(str, 0, 0); |
|||
} else { |
|||
constexpr auto result = |
|||
detail::compile_format_string<detail::type_list<Args...>, 0, 0>( |
|||
format_str); |
|||
return result; |
|||
} |
|||
} |
|||
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
|||
} // namespace detail
|
|||
|
|||
FMT_MODULE_EXPORT_BEGIN |
|||
|
|||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) |
|||
|
|||
template <typename CompiledFormat, typename... Args, |
|||
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) { |
|||
auto s = std::basic_string<Char>(); |
|||
cf.format(std::back_inserter(s), args...); |
|||
return s; |
|||
} |
|||
|
|||
template <typename OutputIt, typename CompiledFormat, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> |
|||
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, |
|||
const Args&... args) { |
|||
return cf.format(out, args...); |
|||
} |
|||
|
|||
template <typename S, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> |
|||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&, |
|||
Args&&... args) { |
|||
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] == '}') { |
|||
const auto& first = detail::first(args...); |
|||
if constexpr (detail::is_named_arg< |
|||
remove_cvref_t<decltype(first)>>::value) { |
|||
return fmt::to_string(first.value); |
|||
} else { |
|||
return fmt::to_string(first); |
|||
} |
|||
} |
|||
} |
|||
constexpr auto compiled = detail::compile<Args...>(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)...); |
|||
} else { |
|||
return fmt::format(compiled, std::forward<Args>(args)...); |
|||
} |
|||
} |
|||
|
|||
template <typename OutputIt, typename S, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> |
|||
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { |
|||
constexpr auto compiled = detail::compile<Args...>(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)...); |
|||
} else { |
|||
return fmt::format_to(out, compiled, std::forward<Args>(args)...); |
|||
} |
|||
} |
|||
#endif |
|||
|
|||
template <typename OutputIt, typename S, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> |
|||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, |
|||
const S& format_str, Args&&... args) { |
|||
auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n), |
|||
format_str, std::forward<Args>(args)...); |
|||
return {it.base(), it.count()}; |
|||
} |
|||
|
|||
template <typename S, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> |
|||
size_t formatted_size(const S& format_str, const Args&... args) { |
|||
return fmt::format_to(detail::counting_iterator(), format_str, args...) |
|||
.count(); |
|||
} |
|||
|
|||
template <typename S, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> |
|||
void print(std::FILE* f, const S& format_str, const Args&... args) { |
|||
memory_buffer buffer; |
|||
fmt::format_to(std::back_inserter(buffer), format_str, args...); |
|||
detail::print(f, {buffer.data(), buffer.size()}); |
|||
} |
|||
|
|||
template <typename S, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> |
|||
void print(const S& format_str, const Args&... args) { |
|||
print(stdout, format_str, args...); |
|||
} |
|||
|
|||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS |
|||
inline namespace literals { |
|||
template <detail_exported::fixed_string Str> constexpr auto operator""_cf() { |
|||
using char_t = remove_cvref_t<decltype(Str.data[0])>; |
|||
return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t), |
|||
Str>(); |
|||
} |
|||
} // namespace literals
|
|||
#endif |
|||
|
|||
FMT_MODULE_EXPORT_END |
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_COMPILE_H_
|
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,47 @@ |
|||
// Formatting library for C++
|
|||
//
|
|||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
|||
// All rights reserved.
|
|||
//
|
|||
// For the license information refer to format.h.
|
|||
|
|||
#include "3rdparty/fmt/format-inl.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
namespace detail { |
|||
|
|||
template FMT_API auto dragonbox::to_decimal(float x) noexcept |
|||
-> dragonbox::decimal_fp<float>; |
|||
template FMT_API auto dragonbox::to_decimal(double x) noexcept |
|||
-> dragonbox::decimal_fp<double>; |
|||
|
|||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR |
|||
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) |
|||
-> thousands_sep_result<char>; |
|||
template FMT_API auto decimal_point_impl(locale_ref) -> char; |
|||
|
|||
template FMT_API void buffer<char>::append(const char*, const char*); |
|||
|
|||
// DEPRECATED!
|
|||
// There is no correspondent extern template in format.h because of
|
|||
// incompatibility between clang and gcc (#2377).
|
|||
template FMT_API void vformat_to(buffer<char>&, string_view, |
|||
basic_format_args<FMT_BUFFER_CONTEXT(char)>, |
|||
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; |
|||
|
|||
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*); |
|||
|
|||
} // namespace detail
|
|||
FMT_END_NAMESPACE |
File diff suppressed because it is too large
@ -0,0 +1,227 @@ |
|||
// Formatting library for C++ - std::ostream support
|
|||
//
|
|||
// Copyright (c) 2012 - present, Victor Zverovich
|
|||
// All rights reserved.
|
|||
//
|
|||
// For the license information refer to format.h.
|
|||
|
|||
#ifndef FMT_OSTREAM_H_ |
|||
#define FMT_OSTREAM_H_ |
|||
|
|||
#include <fstream> |
|||
#include <ostream> |
|||
#if defined(_WIN32) && defined(__GLIBCXX__) |
|||
# include <ext/stdio_filebuf.h> |
|||
# include <ext/stdio_sync_filebuf.h> |
|||
#elif defined(_WIN32) && defined(_LIBCPP_VERSION) |
|||
# include <__std_stream> |
|||
#endif |
|||
|
|||
#include "format.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
|
|||
template <typename OutputIt, typename Char> class basic_printf_context; |
|||
|
|||
namespace detail { |
|||
|
|||
// Checks if T has a user-defined operator<<.
|
|||
template <typename T, typename Char, typename Enable = void> |
|||
class is_streamable { |
|||
private: |
|||
template <typename U> |
|||
static auto test(int) |
|||
-> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>() |
|||
<< std::declval<U>()) != 0>; |
|||
|
|||
template <typename> static auto test(...) -> std::false_type; |
|||
|
|||
using result = decltype(test<T>(0)); |
|||
|
|||
public: |
|||
is_streamable() = default; |
|||
|
|||
static const bool value = result::value; |
|||
}; |
|||
|
|||
// Formatting of built-in types and arrays is intentionally disabled because
|
|||
// it's handled by standard (non-ostream) formatters.
|
|||
template <typename T, typename Char> |
|||
struct is_streamable< |
|||
T, Char, |
|||
enable_if_t< |
|||
std::is_arithmetic<T>::value || std::is_array<T>::value || |
|||
std::is_pointer<T>::value || std::is_same<T, char8_type>::value || |
|||
std::is_convertible<T, fmt::basic_string_view<Char>>::value || |
|||
std::is_same<T, std_string_view<Char>>::value || |
|||
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>> |
|||
: std::false_type {}; |
|||
|
|||
// Generate a unique explicit instantion in every translation unit using a tag
|
|||
// type in an anonymous namespace.
|
|||
namespace { |
|||
struct file_access_tag {}; |
|||
} // namespace
|
|||
template <class Tag, class BufType, FILE* BufType::*FileMemberPtr> |
|||
class file_access { |
|||
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } |
|||
}; |
|||
|
|||
#if FMT_MSC_VERSION |
|||
template class file_access<file_access_tag, std::filebuf, |
|||
&std::filebuf::_Myfile>; |
|||
auto get_file(std::filebuf&) -> FILE*; |
|||
#elif defined(_WIN32) && defined(_LIBCPP_VERSION) |
|||
template class file_access<file_access_tag, std::__stdoutbuf<char>, |
|||
&std::__stdoutbuf<char>::__file_>; |
|||
auto get_file(std::__stdoutbuf<char>&) -> FILE*; |
|||
#endif |
|||
|
|||
inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) { |
|||
#if FMT_MSC_VERSION |
|||
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf())) |
|||
if (FILE* f = get_file(*buf)) return write_console(f, data); |
|||
#elif defined(_WIN32) && defined(__GLIBCXX__) |
|||
auto* rdbuf = os.rdbuf(); |
|||
FILE* c_file; |
|||
if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf)) |
|||
c_file = fbuf->file(); |
|||
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf)) |
|||
c_file = fbuf->file(); |
|||
else |
|||
return false; |
|||
if (c_file) return write_console(c_file, data); |
|||
#elif defined(_WIN32) && defined(_LIBCPP_VERSION) |
|||
if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf())) |
|||
if (FILE* f = get_file(*buf)) return write_console(f, data); |
|||
#else |
|||
ignore_unused(os); |
|||
ignore_unused(data); |
|||
#endif |
|||
return false; |
|||
} |
|||
inline bool write_ostream_unicode(std::wostream&, |
|||
fmt::basic_string_view<wchar_t>) { |
|||
return false; |
|||
} |
|||
|
|||
// Write the content of buf to os.
|
|||
// It is a separate function rather than a part of vprint to simplify testing.
|
|||
template <typename Char> |
|||
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { |
|||
if (write_ostream_unicode(os, {buf.data(), buf.size()})) return; |
|||
const Char* buf_data = buf.data(); |
|||
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; |
|||
unsigned_streamsize size = buf.size(); |
|||
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>()); |
|||
do { |
|||
unsigned_streamsize n = size <= max_size ? size : max_size; |
|||
os.write(buf_data, static_cast<std::streamsize>(n)); |
|||
buf_data += n; |
|||
size -= n; |
|||
} while (size != 0); |
|||
} |
|||
|
|||
template <typename Char, typename T> |
|||
void format_value(buffer<Char>& buf, const T& value, |
|||
locale_ref loc = locale_ref()) { |
|||
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf); |
|||
auto&& output = std::basic_ostream<Char>(&format_buf); |
|||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) |
|||
if (loc) output.imbue(loc.get<std::locale>()); |
|||
#endif |
|||
output << value; |
|||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); |
|||
} |
|||
|
|||
template <typename T> struct streamed_view { const T& value; }; |
|||
|
|||
} // namespace detail
|
|||
|
|||
// Formats an object of type T that has an overloaded ostream operator<<.
|
|||
template <typename Char> |
|||
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> { |
|||
void set_debug_format() = delete; |
|||
|
|||
template <typename T, typename OutputIt> |
|||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const |
|||
-> OutputIt { |
|||
auto buffer = basic_memory_buffer<Char>(); |
|||
format_value(buffer, value, ctx.locale()); |
|||
return formatter<basic_string_view<Char>, Char>::format( |
|||
{buffer.data(), buffer.size()}, ctx); |
|||
} |
|||
}; |
|||
|
|||
using ostream_formatter = basic_ostream_formatter<char>; |
|||
|
|||
template <typename T, typename Char> |
|||
struct formatter<detail::streamed_view<T>, Char> |
|||
: basic_ostream_formatter<Char> { |
|||
template <typename OutputIt> |
|||
auto format(detail::streamed_view<T> view, |
|||
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt { |
|||
return basic_ostream_formatter<Char>::format(view.value, ctx); |
|||
} |
|||
}; |
|||
|
|||
/**
|
|||
\rst |
|||
Returns a view that formats `value` via an ostream ``operator<<``. |
|||
|
|||
**Example**:: |
|||
|
|||
fmt::print("Current thread id: {}\n", |
|||
fmt::streamed(std::this_thread::get_id())); |
|||
\endrst |
|||
*/ |
|||
template <typename T> |
|||
auto streamed(const T& value) -> detail::streamed_view<T> { |
|||
return {value}; |
|||
} |
|||
|
|||
namespace detail { |
|||
|
|||
// Formats an object of type T that has an overloaded ostream operator<<.
|
|||
template <typename T, typename Char> |
|||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> |
|||
: basic_ostream_formatter<Char> { |
|||
using basic_ostream_formatter<Char>::format; |
|||
}; |
|||
|
|||
} // namespace detail
|
|||
|
|||
FMT_MODULE_EXPORT template <typename Char> |
|||
void vprint(std::basic_ostream<Char>& os, |
|||
basic_string_view<type_identity_t<Char>> format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) { |
|||
auto buffer = basic_memory_buffer<Char>(); |
|||
detail::vformat_to(buffer, format_str, args); |
|||
detail::write_buffer(os, buffer); |
|||
} |
|||
|
|||
/**
|
|||
\rst |
|||
Prints formatted data to the stream *os*. |
|||
|
|||
**Example**:: |
|||
|
|||
fmt::print(cerr, "Don't {}!", "panic"); |
|||
\endrst |
|||
*/ |
|||
FMT_MODULE_EXPORT template <typename... T> |
|||
void print(std::ostream& os, format_string<T...> fmt, T&&... args) { |
|||
vprint(os, fmt, fmt::make_format_args(args...)); |
|||
} |
|||
|
|||
FMT_MODULE_EXPORT |
|||
template <typename... Args> |
|||
void print(std::wostream& os, |
|||
basic_format_string<wchar_t, type_identity_t<Args>...> fmt, |
|||
Args&&... args) { |
|||
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...)); |
|||
} |
|||
|
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_OSTREAM_H_
|
@ -0,0 +1,732 @@ |
|||
// Formatting library for C++ - experimental range support
|
|||
//
|
|||
// Copyright (c) 2012 - present, Victor Zverovich
|
|||
// All rights reserved.
|
|||
//
|
|||
// For the license information refer to format.h.
|
|||
//
|
|||
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
|||
// All Rights Reserved
|
|||
// {fmt} support for ranges, containers and types tuple interface.
|
|||
|
|||
#ifndef FMT_RANGES_H_ |
|||
#define FMT_RANGES_H_ |
|||
|
|||
#include <initializer_list> |
|||
#include <tuple> |
|||
#include <type_traits> |
|||
|
|||
#include "format.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
|
|||
namespace detail { |
|||
|
|||
template <typename RangeT, typename OutputIterator> |
|||
OutputIterator copy(const RangeT& range, OutputIterator out) { |
|||
for (auto it = range.begin(), end = range.end(); it != end; ++it) |
|||
*out++ = *it; |
|||
return out; |
|||
} |
|||
|
|||
template <typename OutputIterator> |
|||
OutputIterator copy(const char* str, OutputIterator out) { |
|||
while (*str) *out++ = *str++; |
|||
return out; |
|||
} |
|||
|
|||
template <typename OutputIterator> |
|||
OutputIterator copy(char ch, OutputIterator out) { |
|||
*out++ = ch; |
|||
return out; |
|||
} |
|||
|
|||
template <typename OutputIterator> |
|||
OutputIterator copy(wchar_t ch, OutputIterator out) { |
|||
*out++ = ch; |
|||
return out; |
|||
} |
|||
|
|||
// Returns true if T has a std::string-like interface, like std::string_view.
|
|||
template <typename T> class is_std_string_like { |
|||
template <typename U> |
|||
static auto check(U* p) |
|||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); |
|||
template <typename> static void check(...); |
|||
|
|||
public: |
|||
static constexpr const bool value = |
|||
is_string<T>::value || |
|||
std::is_convertible<T, std_string_view<char>>::value || |
|||
!std::is_void<decltype(check<T>(nullptr))>::value; |
|||
}; |
|||
|
|||
template <typename Char> |
|||
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {}; |
|||
|
|||
template <typename T> class is_map { |
|||
template <typename U> static auto check(U*) -> typename U::mapped_type; |
|||
template <typename> static void check(...); |
|||
|
|||
public: |
|||
#ifdef FMT_FORMAT_MAP_AS_LIST |
|||
static constexpr const bool value = false; |
|||
#else |
|||
static constexpr const bool value = |
|||
!std::is_void<decltype(check<T>(nullptr))>::value; |
|||
#endif |
|||
}; |
|||
|
|||
template <typename T> class is_set { |
|||
template <typename U> static auto check(U*) -> typename U::key_type; |
|||
template <typename> static void check(...); |
|||
|
|||
public: |
|||
#ifdef FMT_FORMAT_SET_AS_LIST |
|||
static constexpr const bool value = false; |
|||
#else |
|||
static constexpr const bool value = |
|||
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value; |
|||
#endif |
|||
}; |
|||
|
|||
template <typename... Ts> struct conditional_helper {}; |
|||
|
|||
template <typename T, typename _ = void> struct is_range_ : std::false_type {}; |
|||
|
|||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800 |
|||
|
|||
# define FMT_DECLTYPE_RETURN(val) \ |
|||
->decltype(val) { return val; } \ |
|||
static_assert( \ |
|||
true, "") // This makes it so that a semicolon is required after the
|
|||
// macro, which helps clang-format handle the formatting.
|
|||
|
|||
// C array overload
|
|||
template <typename T, std::size_t N> |
|||
auto range_begin(const T (&arr)[N]) -> const T* { |
|||
return arr; |
|||
} |
|||
template <typename T, std::size_t N> |
|||
auto range_end(const T (&arr)[N]) -> const T* { |
|||
return arr + N; |
|||
} |
|||
|
|||
template <typename T, typename Enable = void> |
|||
struct has_member_fn_begin_end_t : std::false_type {}; |
|||
|
|||
template <typename T> |
|||
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()), |
|||
decltype(std::declval<T>().end())>> |
|||
: std::true_type {}; |
|||
|
|||
// Member function overload
|
|||
template <typename T> |
|||
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin()); |
|||
template <typename T> |
|||
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end()); |
|||
|
|||
// ADL overload. Only participates in overload resolution if member functions
|
|||
// are not found.
|
|||
template <typename T> |
|||
auto range_begin(T&& rng) |
|||
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value, |
|||
decltype(begin(static_cast<T&&>(rng)))> { |
|||
return begin(static_cast<T&&>(rng)); |
|||
} |
|||
template <typename T> |
|||
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value, |
|||
decltype(end(static_cast<T&&>(rng)))> { |
|||
return end(static_cast<T&&>(rng)); |
|||
} |
|||
|
|||
template <typename T, typename Enable = void> |
|||
struct has_const_begin_end : std::false_type {}; |
|||
template <typename T, typename Enable = void> |
|||
struct has_mutable_begin_end : std::false_type {}; |
|||
|
|||
template <typename T> |
|||
struct has_const_begin_end< |
|||
T, |
|||
void_t< |
|||
decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())), |
|||
decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>> |
|||
: std::true_type {}; |
|||
|
|||
template <typename T> |
|||
struct has_mutable_begin_end< |
|||
T, void_t<decltype(detail::range_begin(std::declval<T>())), |
|||
decltype(detail::range_end(std::declval<T>())), |
|||
enable_if_t<std::is_copy_constructible<T>::value>>> |
|||
: std::true_type {}; |
|||
|
|||
template <typename T> |
|||
struct is_range_<T, void> |
|||
: std::integral_constant<bool, (has_const_begin_end<T>::value || |
|||
has_mutable_begin_end<T>::value)> {}; |
|||
# undef FMT_DECLTYPE_RETURN |
|||
#endif |
|||
|
|||
// tuple_size and tuple_element check.
|
|||
template <typename T> class is_tuple_like_ { |
|||
template <typename U> |
|||
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int()); |
|||
template <typename> static void check(...); |
|||
|
|||
public: |
|||
static constexpr const bool value = |
|||
!std::is_void<decltype(check<T>(nullptr))>::value; |
|||
}; |
|||
|
|||
// Check for integer_sequence
|
|||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900 |
|||
template <typename T, T... N> |
|||
using integer_sequence = std::integer_sequence<T, N...>; |
|||
template <size_t... N> using index_sequence = std::index_sequence<N...>; |
|||
template <size_t N> using make_index_sequence = std::make_index_sequence<N>; |
|||
#else |
|||
template <typename T, T... N> struct integer_sequence { |
|||
using value_type = T; |
|||
|
|||
static FMT_CONSTEXPR size_t size() { return sizeof...(N); } |
|||
}; |
|||
|
|||
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>; |
|||
|
|||
template <typename T, size_t N, T... Ns> |
|||
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {}; |
|||
template <typename T, T... Ns> |
|||
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {}; |
|||
|
|||
template <size_t N> |
|||
using make_index_sequence = make_integer_sequence<size_t, N>; |
|||
#endif |
|||
|
|||
template <typename T> |
|||
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>; |
|||
|
|||
template <typename T, typename C, bool = is_tuple_like_<T>::value> |
|||
class is_tuple_formattable_ { |
|||
public: |
|||
static constexpr const bool value = false; |
|||
}; |
|||
template <typename T, typename C> class is_tuple_formattable_<T, C, true> { |
|||
template <std::size_t... I> |
|||
static std::true_type check2(index_sequence<I...>, |
|||
integer_sequence<bool, (I == I)...>); |
|||
static std::false_type check2(...); |
|||
template <std::size_t... I> |
|||
static decltype(check2( |
|||
index_sequence<I...>{}, |
|||
integer_sequence< |
|||
bool, (is_formattable<typename std::tuple_element<I, T>::type, |
|||
C>::value)...>{})) check(index_sequence<I...>); |
|||
|
|||
public: |
|||
static constexpr const bool value = |
|||
decltype(check(tuple_index_sequence<T>{}))::value; |
|||
}; |
|||
|
|||
template <class Tuple, class F, size_t... Is> |
|||
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) noexcept { |
|||
using std::get; |
|||
// using free function get<I>(T) now.
|
|||
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; |
|||
(void)_; // blocks warnings
|
|||
} |
|||
|
|||
template <class T> |
|||
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes( |
|||
T const&) { |
|||
return {}; |
|||
} |
|||
|
|||
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) { |
|||
const auto indexes = get_indexes(tup); |
|||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); |
|||
} |
|||
|
|||
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920 |
|||
// Older MSVC doesn't get the reference type correctly for arrays.
|
|||
template <typename R> struct range_reference_type_impl { |
|||
using type = decltype(*detail::range_begin(std::declval<R&>())); |
|||
}; |
|||
|
|||
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> { |
|||
using type = T&; |
|||
}; |
|||
|
|||
template <typename T> |
|||
using range_reference_type = typename range_reference_type_impl<T>::type; |
|||
#else |
|||
template <typename Range> |
|||
using range_reference_type = |
|||
decltype(*detail::range_begin(std::declval<Range&>())); |
|||
#endif |
|||
|
|||
// We don't use the Range's value_type for anything, but we do need the Range's
|
|||
// reference type, with cv-ref stripped.
|
|||
template <typename Range> |
|||
using uncvref_type = remove_cvref_t<range_reference_type<Range>>; |
|||
|
|||
template <typename Range> |
|||
using uncvref_first_type = remove_cvref_t< |
|||
decltype(std::declval<range_reference_type<Range>>().first)>; |
|||
|
|||
template <typename Range> |
|||
using uncvref_second_type = remove_cvref_t< |
|||
decltype(std::declval<range_reference_type<Range>>().second)>; |
|||
|
|||
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) { |
|||
*out++ = ','; |
|||
*out++ = ' '; |
|||
return out; |
|||
} |
|||
|
|||
template <typename Char, typename OutputIt> |
|||
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt { |
|||
return write_escaped_string(out, str); |
|||
} |
|||
|
|||
template <typename Char, typename OutputIt, typename T, |
|||
FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)> |
|||
inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt { |
|||
auto sv = std_string_view<Char>(str); |
|||
return write_range_entry<Char>(out, basic_string_view<Char>(sv)); |
|||
} |
|||
|
|||
template <typename Char, typename OutputIt, typename Arg, |
|||
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)> |
|||
OutputIt write_range_entry(OutputIt out, const Arg v) { |
|||
return write_escaped_char(out, v); |
|||
} |
|||
|
|||
template < |
|||
typename Char, typename OutputIt, typename Arg, |
|||
FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value && |
|||
!std::is_same<Arg, Char>::value)> |
|||
OutputIt write_range_entry(OutputIt out, const Arg& v) { |
|||
return write<Char>(out, v); |
|||
} |
|||
|
|||
template <typename CharT, CharT... C> struct string_literal { |
|||
static constexpr CharT value[sizeof...(C)] = {C...}; |
|||
constexpr operator basic_string_view<CharT>() const { |
|||
return {value, sizeof...(C)}; |
|||
} |
|||
}; |
|||
|
|||
template <typename CharT, CharT... C> |
|||
constexpr CharT string_literal<CharT, C...>::value[sizeof...(C)]; |
|||
|
|||
} // namespace detail
|
|||
|
|||
template <typename T> struct is_tuple_like { |
|||
static constexpr const bool value = |
|||
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value; |
|||
}; |
|||
|
|||
template <typename T, typename C> struct is_tuple_formattable { |
|||
static constexpr const bool value = |
|||
detail::is_tuple_formattable_<T, C>::value; |
|||
}; |
|||
|
|||
template <typename TupleT, typename Char> |
|||
struct formatter<TupleT, Char, |
|||
enable_if_t<fmt::is_tuple_like<TupleT>::value && |
|||
fmt::is_tuple_formattable<TupleT, Char>::value>> { |
|||
private: |
|||
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; |
|||
basic_string_view<Char> opening_bracket_ = |
|||
detail::string_literal<Char, '('>{}; |
|||
basic_string_view<Char> closing_bracket_ = |
|||
detail::string_literal<Char, ')'>{}; |
|||
|
|||
// C++11 generic lambda for format().
|
|||
template <typename FormatContext> struct format_each { |
|||
template <typename T> void operator()(const T& v) { |
|||
if (i > 0) out = detail::copy_str<Char>(separator, out); |
|||
out = detail::write_range_entry<Char>(out, v); |
|||
++i; |
|||
} |
|||
int i; |
|||
typename FormatContext::iterator& out; |
|||
basic_string_view<Char> separator; |
|||
}; |
|||
|
|||
public: |
|||
FMT_CONSTEXPR formatter() {} |
|||
|
|||
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { |
|||
separator_ = sep; |
|||
} |
|||
|
|||
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, |
|||
basic_string_view<Char> close) { |
|||
opening_bracket_ = open; |
|||
closing_bracket_ = close; |
|||
} |
|||
|
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
return ctx.begin(); |
|||
} |
|||
|
|||
template <typename FormatContext = format_context> |
|||
auto format(const TupleT& values, FormatContext& ctx) const |
|||
-> decltype(ctx.out()) { |
|||
auto out = ctx.out(); |
|||
out = detail::copy_str<Char>(opening_bracket_, out); |
|||
detail::for_each(values, format_each<FormatContext>{0, out, separator_}); |
|||
out = detail::copy_str<Char>(closing_bracket_, out); |
|||
return out; |
|||
} |
|||
}; |
|||
|
|||
template <typename T, typename Char> struct is_range { |
|||
static constexpr const bool value = |
|||
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value && |
|||
!std::is_convertible<T, std::basic_string<Char>>::value && |
|||
!std::is_constructible<detail::std_string_view<Char>, T>::value; |
|||
}; |
|||
|
|||
namespace detail { |
|||
template <typename Context> struct range_mapper { |
|||
using mapper = arg_mapper<Context>; |
|||
|
|||
template <typename T, |
|||
FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)> |
|||
static auto map(T&& value) -> T&& { |
|||
return static_cast<T&&>(value); |
|||
} |
|||
template <typename T, |
|||
FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)> |
|||
static auto map(T&& value) |
|||
-> decltype(mapper().map(static_cast<T&&>(value))) { |
|||
return mapper().map(static_cast<T&&>(value)); |
|||
} |
|||
}; |
|||
|
|||
template <typename Char, typename Element> |
|||
using range_formatter_type = conditional_t< |
|||
is_formattable<Element, Char>::value, |
|||
formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map( |
|||
std::declval<Element>()))>, |
|||
Char>, |
|||
fallback_formatter<Element, Char>>; |
|||
|
|||
template <typename R> |
|||
using maybe_const_range = |
|||
conditional_t<has_const_begin_end<R>::value, const R, R>; |
|||
|
|||
// Workaround a bug in MSVC 2015 and earlier.
|
|||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 |
|||
template <typename R, typename Char> |
|||
struct is_formattable_delayed |
|||
: disjunction< |
|||
is_formattable<uncvref_type<maybe_const_range<R>>, Char>, |
|||
has_fallback_formatter<uncvref_type<maybe_const_range<R>>, Char>> {}; |
|||
#endif |
|||
|
|||
} // namespace detail
|
|||
|
|||
template <typename T, typename Char, typename Enable = void> |
|||
struct range_formatter; |
|||
|
|||
template <typename T, typename Char> |
|||
struct range_formatter< |
|||
T, Char, |
|||
enable_if_t<conjunction< |
|||
std::is_same<T, remove_cvref_t<T>>, |
|||
disjunction<is_formattable<T, Char>, |
|||
detail::has_fallback_formatter<T, Char>>>::value>> { |
|||
private: |
|||
detail::range_formatter_type<Char, T> underlying_; |
|||
bool custom_specs_ = false; |
|||
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; |
|||
basic_string_view<Char> opening_bracket_ = |
|||
detail::string_literal<Char, '['>{}; |
|||
basic_string_view<Char> closing_bracket_ = |
|||
detail::string_literal<Char, ']'>{}; |
|||
|
|||
template <class U> |
|||
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, int) |
|||
-> decltype(u.set_debug_format()) { |
|||
u.set_debug_format(); |
|||
} |
|||
|
|||
template <class U> |
|||
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {} |
|||
|
|||
FMT_CONSTEXPR void maybe_set_debug_format() { |
|||
maybe_set_debug_format(underlying_, 0); |
|||
} |
|||
|
|||
public: |
|||
FMT_CONSTEXPR range_formatter() {} |
|||
|
|||
FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& { |
|||
return underlying_; |
|||
} |
|||
|
|||
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { |
|||
separator_ = sep; |
|||
} |
|||
|
|||
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, |
|||
basic_string_view<Char> close) { |
|||
opening_bracket_ = open; |
|||
closing_bracket_ = close; |
|||
} |
|||
|
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
auto it = ctx.begin(); |
|||
auto end = ctx.end(); |
|||
if (it == end || *it == '}') { |
|||
maybe_set_debug_format(); |
|||
return it; |
|||
} |
|||
|
|||
if (*it == 'n') { |
|||
set_brackets({}, {}); |
|||
++it; |
|||
} |
|||
|
|||
if (*it == '}') { |
|||
maybe_set_debug_format(); |
|||
return it; |
|||
} |
|||
|
|||
if (*it != ':') |
|||
FMT_THROW(format_error("no other top-level range formatters supported")); |
|||
|
|||
custom_specs_ = true; |
|||
++it; |
|||
ctx.advance_to(it); |
|||
return underlying_.parse(ctx); |
|||
} |
|||
|
|||
template <typename R, class FormatContext> |
|||
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { |
|||
detail::range_mapper<buffer_context<Char>> mapper; |
|||
auto out = ctx.out(); |
|||
out = detail::copy_str<Char>(opening_bracket_, out); |
|||
int i = 0; |
|||
auto it = detail::range_begin(range); |
|||
auto end = detail::range_end(range); |
|||
for (; it != end; ++it) { |
|||
if (i > 0) out = detail::copy_str<Char>(separator_, out); |
|||
; |
|||
ctx.advance_to(out); |
|||
out = underlying_.format(mapper.map(*it), ctx); |
|||
++i; |
|||
} |
|||
out = detail::copy_str<Char>(closing_bracket_, out); |
|||
return out; |
|||
} |
|||
}; |
|||
|
|||
enum class range_format { disabled, map, set, sequence, string, debug_string }; |
|||
|
|||
namespace detail { |
|||
template <typename T> struct range_format_kind_ { |
|||
static constexpr auto value = std::is_same<range_reference_type<T>, T>::value |
|||
? range_format::disabled |
|||
: is_map<T>::value ? range_format::map |
|||
: is_set<T>::value ? range_format::set |
|||
: range_format::sequence; |
|||
}; |
|||
|
|||
template <range_format K, typename R, typename Char, typename Enable = void> |
|||
struct range_default_formatter; |
|||
|
|||
template <range_format K> |
|||
using range_format_constant = std::integral_constant<range_format, K>; |
|||
|
|||
template <range_format K, typename R, typename Char> |
|||
struct range_default_formatter< |
|||
K, R, Char, |
|||
enable_if_t<(K == range_format::sequence || K == range_format::map || |
|||
K == range_format::set)>> { |
|||
using range_type = detail::maybe_const_range<R>; |
|||
range_formatter<detail::uncvref_type<range_type>, Char> underlying_; |
|||
|
|||
FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); } |
|||
|
|||
FMT_CONSTEXPR void init(range_format_constant<range_format::set>) { |
|||
underlying_.set_brackets(detail::string_literal<Char, '{'>{}, |
|||
detail::string_literal<Char, '}'>{}); |
|||
} |
|||
|
|||
FMT_CONSTEXPR void init(range_format_constant<range_format::map>) { |
|||
underlying_.set_brackets(detail::string_literal<Char, '{'>{}, |
|||
detail::string_literal<Char, '}'>{}); |
|||
underlying_.underlying().set_brackets({}, {}); |
|||
underlying_.underlying().set_separator( |
|||
detail::string_literal<Char, ':', ' '>{}); |
|||
} |
|||
|
|||
FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {} |
|||
|
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
return underlying_.parse(ctx); |
|||
} |
|||
|
|||
template <typename FormatContext> |
|||
auto format(range_type& range, FormatContext& ctx) const |
|||
-> decltype(ctx.out()) { |
|||
return underlying_.format(range, ctx); |
|||
} |
|||
}; |
|||
} // namespace detail
|
|||
|
|||
template <typename T, typename Char, typename Enable = void> |
|||
struct range_format_kind |
|||
: conditional_t< |
|||
is_range<T, Char>::value, detail::range_format_kind_<T>, |
|||
std::integral_constant<range_format, range_format::disabled>> {}; |
|||
|
|||
template <typename R, typename Char> |
|||
struct formatter< |
|||
R, Char, |
|||
enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value != |
|||
range_format::disabled> |
|||
// Workaround a bug in MSVC 2015 and earlier.
|
|||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 |
|||
, |
|||
detail::is_formattable_delayed<R, Char> |
|||
#endif |
|||
>::value>> |
|||
: detail::range_default_formatter<range_format_kind<R, Char>::value, R, |
|||
Char> { |
|||
}; |
|||
|
|||
template <typename Char, typename... T> struct tuple_join_view : detail::view { |
|||
const std::tuple<T...>& tuple; |
|||
basic_string_view<Char> sep; |
|||
|
|||
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s) |
|||
: tuple(t), sep{s} {} |
|||
}; |
|||
|
|||
template <typename Char, typename... T> |
|||
using tuple_arg_join = tuple_join_view<Char, T...>; |
|||
|
|||
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
|
|||
// support in tuple_join. It is disabled by default because of issues with
|
|||
// the dynamic width and precision.
|
|||
#ifndef FMT_TUPLE_JOIN_SPECIFIERS |
|||
# define FMT_TUPLE_JOIN_SPECIFIERS 0 |
|||
#endif |
|||
|
|||
template <typename Char, typename... T> |
|||
struct formatter<tuple_join_view<Char, T...>, Char> { |
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>()); |
|||
} |
|||
|
|||
template <typename FormatContext> |
|||
auto format(const tuple_join_view<Char, T...>& value, |
|||
FormatContext& ctx) const -> typename FormatContext::iterator { |
|||
return do_format(value, ctx, |
|||
std::integral_constant<size_t, sizeof...(T)>()); |
|||
} |
|||
|
|||
private: |
|||
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_; |
|||
|
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto do_parse(ParseContext& ctx, |
|||
std::integral_constant<size_t, 0>) |
|||
-> decltype(ctx.begin()) { |
|||
return ctx.begin(); |
|||
} |
|||
|
|||
template <typename ParseContext, size_t N> |
|||
FMT_CONSTEXPR auto do_parse(ParseContext& ctx, |
|||
std::integral_constant<size_t, N>) |
|||
-> decltype(ctx.begin()) { |
|||
auto end = ctx.begin(); |
|||
#if FMT_TUPLE_JOIN_SPECIFIERS |
|||
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx); |
|||
if (N > 1) { |
|||
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>()); |
|||
if (end != end1) |
|||
FMT_THROW(format_error("incompatible format specs for tuple elements")); |
|||
} |
|||
#endif |
|||
return end; |
|||
} |
|||
|
|||
template <typename FormatContext> |
|||
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx, |
|||
std::integral_constant<size_t, 0>) const -> |
|||
typename FormatContext::iterator { |
|||
return ctx.out(); |
|||
} |
|||
|
|||
template <typename FormatContext, size_t N> |
|||
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx, |
|||
std::integral_constant<size_t, N>) const -> |
|||
typename FormatContext::iterator { |
|||
auto out = std::get<sizeof...(T) - N>(formatters_) |
|||
.format(std::get<sizeof...(T) - N>(value.tuple), ctx); |
|||
if (N > 1) { |
|||
out = std::copy(value.sep.begin(), value.sep.end(), out); |
|||
ctx.advance_to(out); |
|||
return do_format(value, ctx, std::integral_constant<size_t, N - 1>()); |
|||
} |
|||
return out; |
|||
} |
|||
}; |
|||
|
|||
FMT_MODULE_EXPORT_BEGIN |
|||
|
|||
/**
|
|||
\rst |
|||
Returns an object that formats `tuple` with elements separated by `sep`. |
|||
|
|||
**Example**:: |
|||
|
|||
std::tuple<int, char> t = {1, 'a'}; |
|||
fmt::print("{}", fmt::join(t, ", ")); |
|||
// Output: "1, a"
|
|||
\endrst |
|||
*/ |
|||
template <typename... T> |
|||
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep) |
|||
-> tuple_join_view<char, T...> { |
|||
return {tuple, sep}; |
|||
} |
|||
|
|||
template <typename... T> |
|||
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, |
|||
basic_string_view<wchar_t> sep) |
|||
-> tuple_join_view<wchar_t, T...> { |
|||
return {tuple, sep}; |
|||
} |
|||
|
|||
/**
|
|||
\rst |
|||
Returns an object that formats `initializer_list` with elements separated by |
|||
`sep`. |
|||
|
|||
**Example**:: |
|||
|
|||
fmt::print("{}", fmt::join({1, 2, 3}, ", ")); |
|||
// Output: "1, 2, 3"
|
|||
\endrst |
|||
*/ |
|||
template <typename T> |
|||
auto join(std::initializer_list<T> list, string_view sep) |
|||
-> join_view<const T*, const T*> { |
|||
return join(std::begin(list), std::end(list), sep); |
|||
} |
|||
|
|||
FMT_MODULE_EXPORT_END |
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_RANGES_H_
|
@ -0,0 +1,171 @@ |
|||
// Formatting library for C++ - formatters for standard library types
|
|||
//
|
|||
// Copyright (c) 2012 - present, Victor Zverovich
|
|||
// All rights reserved.
|
|||
//
|
|||
// For the license information refer to format.h.
|
|||
|
|||
#ifndef FMT_STD_H_ |
|||
#define FMT_STD_H_ |
|||
|
|||
#include <thread> |
|||
#include <type_traits> |
|||
#include <utility> |
|||
|
|||
#include "ostream.h" |
|||
|
|||
#if FMT_HAS_INCLUDE(<version>) |
|||
# include <version> |
|||
#endif |
|||
// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
|
|||
#if FMT_CPLUSPLUS >= 201703L |
|||
# if FMT_HAS_INCLUDE(<filesystem>) |
|||
# include <filesystem> |
|||
# endif |
|||
# if FMT_HAS_INCLUDE(<variant>) |
|||
# include <variant> |
|||
# endif |
|||
#endif |
|||
|
|||
#ifdef __cpp_lib_filesystem |
|||
FMT_BEGIN_NAMESPACE |
|||
|
|||
namespace detail { |
|||
|
|||
template <typename Char> |
|||
void write_escaped_path(basic_memory_buffer<Char>& quoted, |
|||
const std::filesystem::path& p) { |
|||
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>()); |
|||
} |
|||
# ifdef _WIN32 |
|||
template <> |
|||
inline void write_escaped_path<char>(basic_memory_buffer<char>& quoted, |
|||
const std::filesystem::path& p) { |
|||
auto s = p.u8string(); |
|||
write_escaped_string<char>( |
|||
std::back_inserter(quoted), |
|||
string_view(reinterpret_cast<const char*>(s.c_str()), s.size())); |
|||
} |
|||
# endif |
|||
template <> |
|||
inline void write_escaped_path<std::filesystem::path::value_type>( |
|||
basic_memory_buffer<std::filesystem::path::value_type>& quoted, |
|||
const std::filesystem::path& p) { |
|||
write_escaped_string<std::filesystem::path::value_type>( |
|||
std::back_inserter(quoted), p.native()); |
|||
} |
|||
|
|||
} // namespace detail
|
|||
|
|||
template <typename Char> |
|||
struct formatter<std::filesystem::path, Char> |
|||
: formatter<basic_string_view<Char>> { |
|||
template <typename FormatContext> |
|||
auto format(const std::filesystem::path& p, FormatContext& ctx) const -> |
|||
typename FormatContext::iterator { |
|||
basic_memory_buffer<Char> quoted; |
|||
detail::write_escaped_path(quoted, p); |
|||
return formatter<basic_string_view<Char>>::format( |
|||
basic_string_view<Char>(quoted.data(), quoted.size()), ctx); |
|||
} |
|||
}; |
|||
FMT_END_NAMESPACE |
|||
#endif |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
template <typename Char> |
|||
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {}; |
|||
FMT_END_NAMESPACE |
|||
|
|||
#ifdef __cpp_lib_variant |
|||
FMT_BEGIN_NAMESPACE |
|||
template <typename Char> struct formatter<std::monostate, Char> { |
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
return ctx.begin(); |
|||
} |
|||
|
|||
template <typename FormatContext> |
|||
auto format(const std::monostate&, FormatContext& ctx) const |
|||
-> decltype(ctx.out()) { |
|||
auto out = ctx.out(); |
|||
out = detail::write<Char>(out, "monostate"); |
|||
return out; |
|||
} |
|||
}; |
|||
|
|||
namespace detail { |
|||
|
|||
template <typename T> |
|||
using variant_index_sequence = |
|||
std::make_index_sequence<std::variant_size<T>::value>; |
|||
|
|||
// variant_size and variant_alternative check.
|
|||
template <typename T, typename U = void> |
|||
struct is_variant_like_ : std::false_type {}; |
|||
template <typename T> |
|||
struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>> |
|||
: std::true_type {}; |
|||
|
|||
// formattable element check
|
|||
template <typename T, typename C> class is_variant_formattable_ { |
|||
template <std::size_t... I> |
|||
static std::conjunction< |
|||
is_formattable<std::variant_alternative_t<I, T>, C>...> |
|||
check(std::index_sequence<I...>); |
|||
|
|||
public: |
|||
static constexpr const bool value = |
|||
decltype(check(variant_index_sequence<T>{}))::value; |
|||
}; |
|||
|
|||
template <typename Char, typename OutputIt, typename T> |
|||
auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt { |
|||
if constexpr (is_string<T>::value) |
|||
return write_escaped_string<Char>(out, detail::to_string_view(v)); |
|||
else if constexpr (std::is_same_v<T, Char>) |
|||
return write_escaped_char(out, v); |
|||
else |
|||
return write<Char>(out, v); |
|||
} |
|||
|
|||
} // namespace detail
|
|||
|
|||
template <typename T> struct is_variant_like { |
|||
static constexpr const bool value = detail::is_variant_like_<T>::value; |
|||
}; |
|||
|
|||
template <typename T, typename C> struct is_variant_formattable { |
|||
static constexpr const bool value = |
|||
detail::is_variant_formattable_<T, C>::value; |
|||
}; |
|||
|
|||
template <typename Variant, typename Char> |
|||
struct formatter< |
|||
Variant, Char, |
|||
std::enable_if_t<std::conjunction_v< |
|||
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> { |
|||
template <typename ParseContext> |
|||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
|||
return ctx.begin(); |
|||
} |
|||
|
|||
template <typename FormatContext> |
|||
auto format(const Variant& value, FormatContext& ctx) const |
|||
-> decltype(ctx.out()) { |
|||
auto out = ctx.out(); |
|||
|
|||
out = detail::write<Char>(out, "variant("); |
|||
std::visit( |
|||
[&](const auto& v) { |
|||
out = detail::write_variant_alternative<Char>(out, v); |
|||
}, |
|||
value); |
|||
*out++ = ')'; |
|||
return out; |
|||
} |
|||
}; |
|||
FMT_END_NAMESPACE |
|||
#endif |
|||
|
|||
#endif // FMT_STD_H_
|
@ -0,0 +1,231 @@ |
|||
// Formatting library for C++ - optional wchar_t and exotic character support
|
|||
//
|
|||
// Copyright (c) 2012 - present, Victor Zverovich
|
|||
// All rights reserved.
|
|||
//
|
|||
// For the license information refer to format.h.
|
|||
|
|||
#ifndef FMT_XCHAR_H_ |
|||
#define FMT_XCHAR_H_ |
|||
|
|||
#include <cwchar> |
|||
|
|||
#include "format.h" |
|||
|
|||
FMT_BEGIN_NAMESPACE |
|||
namespace detail { |
|||
template <typename T> |
|||
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>; |
|||
} |
|||
|
|||
FMT_MODULE_EXPORT_BEGIN |
|||
|
|||
using wstring_view = basic_string_view<wchar_t>; |
|||
using wformat_parse_context = basic_format_parse_context<wchar_t>; |
|||
using wformat_context = buffer_context<wchar_t>; |
|||
using wformat_args = basic_format_args<wformat_context>; |
|||
using wmemory_buffer = basic_memory_buffer<wchar_t>; |
|||
|
|||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 |
|||
// Workaround broken conversion on older gcc.
|
|||
template <typename... Args> using wformat_string = wstring_view; |
|||
#else |
|||
template <typename... Args> |
|||
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>; |
|||
#endif |
|||
|
|||
template <> struct is_char<wchar_t> : std::true_type {}; |
|||
template <> struct is_char<detail::char8_type> : std::true_type {}; |
|||
template <> struct is_char<char16_t> : std::true_type {}; |
|||
template <> struct is_char<char32_t> : std::true_type {}; |
|||
|
|||
template <typename... Args> |
|||
constexpr format_arg_store<wformat_context, Args...> make_wformat_args( |
|||
const Args&... args) { |
|||
return {args...}; |
|||
} |
|||
|
|||
inline namespace literals { |
|||
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS |
|||
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) { |
|||
return {s}; |
|||
} |
|||
#endif |
|||
} // namespace literals
|
|||
|
|||
template <typename It, typename Sentinel> |
|||
auto join(It begin, Sentinel end, wstring_view sep) |
|||
-> join_view<It, Sentinel, wchar_t> { |
|||
return {begin, end, sep}; |
|||
} |
|||
|
|||
template <typename Range> |
|||
auto join(Range&& range, wstring_view sep) |
|||
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>, |
|||
wchar_t> { |
|||
return join(std::begin(range), std::end(range), sep); |
|||
} |
|||
|
|||
template <typename T> |
|||
auto join(std::initializer_list<T> list, wstring_view sep) |
|||
-> join_view<const T*, const T*, wchar_t> { |
|||
return join(std::begin(list), std::end(list), sep); |
|||
} |
|||
|
|||
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)> |
|||
auto vformat(basic_string_view<Char> format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) |
|||
-> std::basic_string<Char> { |
|||
basic_memory_buffer<Char> buffer; |
|||
detail::vformat_to(buffer, format_str, args); |
|||
return to_string(buffer); |
|||
} |
|||
|
|||
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 409 |
|||
template <typename... Args> |
|||
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>; |
|||
#endif |
|||
|
|||
template <typename... T> |
|||
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring { |
|||
return vformat(fmt, fmt::make_wformat_args(args...)); |
|||
} |
|||
|
|||
// Pass char_t as a default template parameter instead of using
|
|||
// std::basic_string<char_t<S>> to reduce the symbol size.
|
|||
template <typename S, typename... Args, typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(!std::is_same<Char, char>::value)> |
|||
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> { |
|||
return vformat(detail::to_string_view(format_str), |
|||
fmt::make_format_args<buffer_context<Char>>(args...)); |
|||
} |
|||
|
|||
template <typename Locale, typename S, typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&& |
|||
detail::is_exotic_char<Char>::value)> |
|||
inline auto vformat( |
|||
const Locale& loc, const S& format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) |
|||
-> std::basic_string<Char> { |
|||
return detail::vformat(loc, detail::to_string_view(format_str), args); |
|||
} |
|||
|
|||
template <typename Locale, typename S, typename... Args, |
|||
typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&& |
|||
detail::is_exotic_char<Char>::value)> |
|||
inline auto format(const Locale& loc, const S& format_str, Args&&... args) |
|||
-> std::basic_string<Char> { |
|||
return detail::vformat(loc, detail::to_string_view(format_str), |
|||
fmt::make_format_args<buffer_context<Char>>(args...)); |
|||
} |
|||
|
|||
template <typename OutputIt, typename S, typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& |
|||
detail::is_exotic_char<Char>::value)> |
|||
auto vformat_to(OutputIt out, const S& format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) |
|||
-> OutputIt { |
|||
auto&& buf = detail::get_buffer<Char>(out); |
|||
detail::vformat_to(buf, detail::to_string_view(format_str), args); |
|||
return detail::get_iterator(buf); |
|||
} |
|||
|
|||
template <typename OutputIt, typename S, typename... Args, |
|||
typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& |
|||
detail::is_exotic_char<Char>::value)> |
|||
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { |
|||
return vformat_to(out, detail::to_string_view(fmt), |
|||
fmt::make_format_args<buffer_context<Char>>(args...)); |
|||
} |
|||
|
|||
template <typename Locale, typename S, typename OutputIt, typename... Args, |
|||
typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& |
|||
detail::is_locale<Locale>::value&& |
|||
detail::is_exotic_char<Char>::value)> |
|||
inline auto vformat_to( |
|||
OutputIt out, const Locale& loc, const S& format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt { |
|||
auto&& buf = detail::get_buffer<Char>(out); |
|||
vformat_to(buf, detail::to_string_view(format_str), args, |
|||
detail::locale_ref(loc)); |
|||
return detail::get_iterator(buf); |
|||
} |
|||
|
|||
template < |
|||
typename OutputIt, typename Locale, typename S, typename... Args, |
|||
typename Char = char_t<S>, |
|||
bool enable = detail::is_output_iterator<OutputIt, Char>::value&& |
|||
detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value> |
|||
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, |
|||
Args&&... args) -> |
|||
typename std::enable_if<enable, OutputIt>::type { |
|||
return vformat_to(out, loc, to_string_view(format_str), |
|||
fmt::make_format_args<buffer_context<Char>>(args...)); |
|||
} |
|||
|
|||
template <typename OutputIt, typename Char, typename... Args, |
|||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& |
|||
detail::is_exotic_char<Char>::value)> |
|||
inline auto vformat_to_n( |
|||
OutputIt out, size_t n, basic_string_view<Char> format_str, |
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args) |
|||
-> format_to_n_result<OutputIt> { |
|||
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out, |
|||
n); |
|||
detail::vformat_to(buf, format_str, args); |
|||
return {buf.out(), buf.count()}; |
|||
} |
|||
|
|||
template <typename OutputIt, typename S, typename... Args, |
|||
typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& |
|||
detail::is_exotic_char<Char>::value)> |
|||
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, |
|||
const Args&... args) -> format_to_n_result<OutputIt> { |
|||
return vformat_to_n(out, n, detail::to_string_view(fmt), |
|||
fmt::make_format_args<buffer_context<Char>>(args...)); |
|||
} |
|||
|
|||
template <typename S, typename... Args, typename Char = char_t<S>, |
|||
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> |
|||
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t { |
|||
detail::counting_buffer<Char> buf; |
|||
detail::vformat_to(buf, detail::to_string_view(fmt), |
|||
fmt::make_format_args<buffer_context<Char>>(args...)); |
|||
return buf.count(); |
|||
} |
|||
|
|||
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { |
|||
wmemory_buffer buffer; |
|||
detail::vformat_to(buffer, fmt, args); |
|||
buffer.push_back(L'\0'); |
|||
if (std::fputws(buffer.data(), f) == -1) |
|||
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); |
|||
} |
|||
|
|||
inline void vprint(wstring_view fmt, wformat_args args) { |
|||
vprint(stdout, fmt, args); |
|||
} |
|||
|
|||
template <typename... T> |
|||
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) { |
|||
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...)); |
|||
} |
|||
|
|||
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) { |
|||
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...)); |
|||
} |
|||
|
|||
/**
|
|||
Converts *value* to ``std::wstring`` using the default format for type *T*. |
|||
*/ |
|||
template <typename T> inline auto to_wstring(const T& value) -> std::wstring { |
|||
return format(FMT_STRING(L"{}"), value); |
|||
} |
|||
FMT_MODULE_EXPORT_END |
|||
FMT_END_NAMESPACE |
|||
|
|||
#endif // FMT_XCHAR_H_
|
File diff suppressed because it is too large
Loading…
Reference in new issue