mirror of https://github.com/trapexit/mergerfs.git
Browse Source
Merge pull request #1128 from trapexit/checkbranches
Merge pull request #1128 from trapexit/checkbranches
Forbid adding mountpoint to branch listpull/1129/head
trapexit
2 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 15375 additions and 38 deletions
-
72.cirrus.yml
-
234src/fmt/args.h
-
2069src/fmt/chrono.h
-
651src/fmt/color.h
-
611src/fmt/compile.h
-
3323src/fmt/core.h
-
1723src/fmt/format-inl.h
-
4217src/fmt/format.h
-
478src/fmt/os.h
-
237src/fmt/ostream.h
-
640src/fmt/printf.h
-
722src/fmt/ranges.h
-
171src/fmt/std.h
-
229src/fmt/xchar.h
-
4src/fs_path.hpp
-
4src/fs_pathvector.hpp
-
28src/option_parser.cpp
@ -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_ |
2069
src/fmt/chrono.h
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,651 @@ |
|||||
|
// Formatting library for C++ - color support |
||||
|
// |
||||
|
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors |
||||
|
// All rights reserved. |
||||
|
// |
||||
|
// For the license information refer to format.h. |
||||
|
|
||||
|
#ifndef FMT_COLOR_H_ |
||||
|
#define FMT_COLOR_H_ |
||||
|
|
||||
|
#include "format.h" |
||||
|
|
||||
|
FMT_BEGIN_NAMESPACE |
||||
|
FMT_MODULE_EXPORT_BEGIN |
||||
|
|
||||
|
enum class color : uint32_t { |
||||
|
alice_blue = 0xF0F8FF, // rgb(240,248,255) |
||||
|
antique_white = 0xFAEBD7, // rgb(250,235,215) |
||||
|
aqua = 0x00FFFF, // rgb(0,255,255) |
||||
|
aquamarine = 0x7FFFD4, // rgb(127,255,212) |
||||
|
azure = 0xF0FFFF, // rgb(240,255,255) |
||||
|
beige = 0xF5F5DC, // rgb(245,245,220) |
||||
|
bisque = 0xFFE4C4, // rgb(255,228,196) |
||||
|
black = 0x000000, // rgb(0,0,0) |
||||
|
blanched_almond = 0xFFEBCD, // rgb(255,235,205) |
||||
|
blue = 0x0000FF, // rgb(0,0,255) |
||||
|
blue_violet = 0x8A2BE2, // rgb(138,43,226) |
||||
|
brown = 0xA52A2A, // rgb(165,42,42) |
||||
|
burly_wood = 0xDEB887, // rgb(222,184,135) |
||||
|
cadet_blue = 0x5F9EA0, // rgb(95,158,160) |
||||
|
chartreuse = 0x7FFF00, // rgb(127,255,0) |
||||
|
chocolate = 0xD2691E, // rgb(210,105,30) |
||||
|
coral = 0xFF7F50, // rgb(255,127,80) |
||||
|
cornflower_blue = 0x6495ED, // rgb(100,149,237) |
||||
|
cornsilk = 0xFFF8DC, // rgb(255,248,220) |
||||
|
crimson = 0xDC143C, // rgb(220,20,60) |
||||
|
cyan = 0x00FFFF, // rgb(0,255,255) |
||||
|
dark_blue = 0x00008B, // rgb(0,0,139) |
||||
|
dark_cyan = 0x008B8B, // rgb(0,139,139) |
||||
|
dark_golden_rod = 0xB8860B, // rgb(184,134,11) |
||||
|
dark_gray = 0xA9A9A9, // rgb(169,169,169) |
||||
|
dark_green = 0x006400, // rgb(0,100,0) |
||||
|
dark_khaki = 0xBDB76B, // rgb(189,183,107) |
||||
|
dark_magenta = 0x8B008B, // rgb(139,0,139) |
||||
|
dark_olive_green = 0x556B2F, // rgb(85,107,47) |
||||
|
dark_orange = 0xFF8C00, // rgb(255,140,0) |
||||
|
dark_orchid = 0x9932CC, // rgb(153,50,204) |
||||
|
dark_red = 0x8B0000, // rgb(139,0,0) |
||||
|
dark_salmon = 0xE9967A, // rgb(233,150,122) |
||||
|
dark_sea_green = 0x8FBC8F, // rgb(143,188,143) |
||||
|
dark_slate_blue = 0x483D8B, // rgb(72,61,139) |
||||
|
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) |
||||
|
dark_turquoise = 0x00CED1, // rgb(0,206,209) |
||||
|
dark_violet = 0x9400D3, // rgb(148,0,211) |
||||
|
deep_pink = 0xFF1493, // rgb(255,20,147) |
||||
|
deep_sky_blue = 0x00BFFF, // rgb(0,191,255) |
||||
|
dim_gray = 0x696969, // rgb(105,105,105) |
||||
|
dodger_blue = 0x1E90FF, // rgb(30,144,255) |
||||
|
fire_brick = 0xB22222, // rgb(178,34,34) |
||||
|
floral_white = 0xFFFAF0, // rgb(255,250,240) |
||||
|
forest_green = 0x228B22, // rgb(34,139,34) |
||||
|
fuchsia = 0xFF00FF, // rgb(255,0,255) |
||||
|
gainsboro = 0xDCDCDC, // rgb(220,220,220) |
||||
|
ghost_white = 0xF8F8FF, // rgb(248,248,255) |
||||
|
gold = 0xFFD700, // rgb(255,215,0) |
||||
|
golden_rod = 0xDAA520, // rgb(218,165,32) |
||||
|
gray = 0x808080, // rgb(128,128,128) |
||||
|
green = 0x008000, // rgb(0,128,0) |
||||
|
green_yellow = 0xADFF2F, // rgb(173,255,47) |
||||
|
honey_dew = 0xF0FFF0, // rgb(240,255,240) |
||||
|
hot_pink = 0xFF69B4, // rgb(255,105,180) |
||||
|
indian_red = 0xCD5C5C, // rgb(205,92,92) |
||||
|
indigo = 0x4B0082, // rgb(75,0,130) |
||||
|
ivory = 0xFFFFF0, // rgb(255,255,240) |
||||
|
khaki = 0xF0E68C, // rgb(240,230,140) |
||||
|
lavender = 0xE6E6FA, // rgb(230,230,250) |
||||
|
lavender_blush = 0xFFF0F5, // rgb(255,240,245) |
||||
|
lawn_green = 0x7CFC00, // rgb(124,252,0) |
||||
|
lemon_chiffon = 0xFFFACD, // rgb(255,250,205) |
||||
|
light_blue = 0xADD8E6, // rgb(173,216,230) |
||||
|
light_coral = 0xF08080, // rgb(240,128,128) |
||||
|
light_cyan = 0xE0FFFF, // rgb(224,255,255) |
||||
|
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) |
||||
|
light_gray = 0xD3D3D3, // rgb(211,211,211) |
||||
|
light_green = 0x90EE90, // rgb(144,238,144) |
||||
|
light_pink = 0xFFB6C1, // rgb(255,182,193) |
||||
|
light_salmon = 0xFFA07A, // rgb(255,160,122) |
||||
|
light_sea_green = 0x20B2AA, // rgb(32,178,170) |
||||
|
light_sky_blue = 0x87CEFA, // rgb(135,206,250) |
||||
|
light_slate_gray = 0x778899, // rgb(119,136,153) |
||||
|
light_steel_blue = 0xB0C4DE, // rgb(176,196,222) |
||||
|
light_yellow = 0xFFFFE0, // rgb(255,255,224) |
||||
|
lime = 0x00FF00, // rgb(0,255,0) |
||||
|
lime_green = 0x32CD32, // rgb(50,205,50) |
||||
|
linen = 0xFAF0E6, // rgb(250,240,230) |
||||
|
magenta = 0xFF00FF, // rgb(255,0,255) |
||||
|
maroon = 0x800000, // rgb(128,0,0) |
||||
|
medium_aquamarine = 0x66CDAA, // rgb(102,205,170) |
||||
|
medium_blue = 0x0000CD, // rgb(0,0,205) |
||||
|
medium_orchid = 0xBA55D3, // rgb(186,85,211) |
||||
|
medium_purple = 0x9370DB, // rgb(147,112,219) |
||||
|
medium_sea_green = 0x3CB371, // rgb(60,179,113) |
||||
|
medium_slate_blue = 0x7B68EE, // rgb(123,104,238) |
||||
|
medium_spring_green = 0x00FA9A, // rgb(0,250,154) |
||||
|
medium_turquoise = 0x48D1CC, // rgb(72,209,204) |
||||
|
medium_violet_red = 0xC71585, // rgb(199,21,133) |
||||
|
midnight_blue = 0x191970, // rgb(25,25,112) |
||||
|
mint_cream = 0xF5FFFA, // rgb(245,255,250) |
||||
|
misty_rose = 0xFFE4E1, // rgb(255,228,225) |
||||
|
moccasin = 0xFFE4B5, // rgb(255,228,181) |
||||
|
navajo_white = 0xFFDEAD, // rgb(255,222,173) |
||||
|
navy = 0x000080, // rgb(0,0,128) |
||||
|
old_lace = 0xFDF5E6, // rgb(253,245,230) |
||||
|
olive = 0x808000, // rgb(128,128,0) |
||||
|
olive_drab = 0x6B8E23, // rgb(107,142,35) |
||||
|
orange = 0xFFA500, // rgb(255,165,0) |
||||
|
orange_red = 0xFF4500, // rgb(255,69,0) |
||||
|
orchid = 0xDA70D6, // rgb(218,112,214) |
||||
|
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) |
||||
|
pale_green = 0x98FB98, // rgb(152,251,152) |
||||
|
pale_turquoise = 0xAFEEEE, // rgb(175,238,238) |
||||
|
pale_violet_red = 0xDB7093, // rgb(219,112,147) |
||||
|
papaya_whip = 0xFFEFD5, // rgb(255,239,213) |
||||
|
peach_puff = 0xFFDAB9, // rgb(255,218,185) |
||||
|
peru = 0xCD853F, // rgb(205,133,63) |
||||
|
pink = 0xFFC0CB, // rgb(255,192,203) |
||||
|
plum = 0xDDA0DD, // rgb(221,160,221) |
||||
|
powder_blue = 0xB0E0E6, // rgb(176,224,230) |
||||
|
purple = 0x800080, // rgb(128,0,128) |
||||
|
rebecca_purple = 0x663399, // rgb(102,51,153) |
||||
|
red = 0xFF0000, // rgb(255,0,0) |
||||
|
rosy_brown = 0xBC8F8F, // rgb(188,143,143) |
||||
|
royal_blue = 0x4169E1, // rgb(65,105,225) |
||||
|
saddle_brown = 0x8B4513, // rgb(139,69,19) |
||||
|
salmon = 0xFA8072, // rgb(250,128,114) |
||||
|
sandy_brown = 0xF4A460, // rgb(244,164,96) |
||||
|
sea_green = 0x2E8B57, // rgb(46,139,87) |
||||
|
sea_shell = 0xFFF5EE, // rgb(255,245,238) |
||||
|
sienna = 0xA0522D, // rgb(160,82,45) |
||||
|
silver = 0xC0C0C0, // rgb(192,192,192) |
||||
|
sky_blue = 0x87CEEB, // rgb(135,206,235) |
||||
|
slate_blue = 0x6A5ACD, // rgb(106,90,205) |
||||
|
slate_gray = 0x708090, // rgb(112,128,144) |
||||
|
snow = 0xFFFAFA, // rgb(255,250,250) |
||||
|
spring_green = 0x00FF7F, // rgb(0,255,127) |
||||
|
steel_blue = 0x4682B4, // rgb(70,130,180) |
||||
|
tan = 0xD2B48C, // rgb(210,180,140) |
||||
|
teal = 0x008080, // rgb(0,128,128) |
||||
|
thistle = 0xD8BFD8, // rgb(216,191,216) |
||||
|
tomato = 0xFF6347, // rgb(255,99,71) |
||||
|
turquoise = 0x40E0D0, // rgb(64,224,208) |
||||
|
violet = 0xEE82EE, // rgb(238,130,238) |
||||
|
wheat = 0xF5DEB3, // rgb(245,222,179) |
||||
|
white = 0xFFFFFF, // rgb(255,255,255) |
||||
|
white_smoke = 0xF5F5F5, // rgb(245,245,245) |
||||
|
yellow = 0xFFFF00, // rgb(255,255,0) |
||||
|
yellow_green = 0x9ACD32 // rgb(154,205,50) |
||||
|
}; // enum class color |
||||
|
|
||||
|
enum class terminal_color : uint8_t { |
||||
|
black = 30, |
||||
|
red, |
||||
|
green, |
||||
|
yellow, |
||||
|
blue, |
||||
|
magenta, |
||||
|
cyan, |
||||
|
white, |
||||
|
bright_black = 90, |
||||
|
bright_red, |
||||
|
bright_green, |
||||
|
bright_yellow, |
||||
|
bright_blue, |
||||
|
bright_magenta, |
||||
|
bright_cyan, |
||||
|
bright_white |
||||
|
}; |
||||
|
|
||||
|
enum class emphasis : uint8_t { |
||||
|
bold = 1, |
||||
|
faint = 1 << 1, |
||||
|
italic = 1 << 2, |
||||
|
underline = 1 << 3, |
||||
|
blink = 1 << 4, |
||||
|
reverse = 1 << 5, |
||||
|
conceal = 1 << 6, |
||||
|
strikethrough = 1 << 7, |
||||
|
}; |
||||
|
|
||||
|
// rgb is a struct for red, green and blue colors. |
||||
|
// Using the name "rgb" makes some editors show the color in a tooltip. |
||||
|
struct rgb { |
||||
|
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} |
||||
|
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} |
||||
|
FMT_CONSTEXPR rgb(uint32_t hex) |
||||
|
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} |
||||
|
FMT_CONSTEXPR rgb(color hex) |
||||
|
: r((uint32_t(hex) >> 16) & 0xFF), |
||||
|
g((uint32_t(hex) >> 8) & 0xFF), |
||||
|
b(uint32_t(hex) & 0xFF) {} |
||||
|
uint8_t r; |
||||
|
uint8_t g; |
||||
|
uint8_t b; |
||||
|
}; |
||||
|
|
||||
|
FMT_BEGIN_DETAIL_NAMESPACE |
||||
|
|
||||
|
// color is a struct of either a rgb color or a terminal color. |
||||
|
struct color_type { |
||||
|
FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {} |
||||
|
FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} { |
||||
|
value.rgb_color = static_cast<uint32_t>(rgb_color); |
||||
|
} |
||||
|
FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} { |
||||
|
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) | |
||||
|
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b; |
||||
|
} |
||||
|
FMT_CONSTEXPR color_type(terminal_color term_color) noexcept |
||||
|
: is_rgb(), value{} { |
||||
|
value.term_color = static_cast<uint8_t>(term_color); |
||||
|
} |
||||
|
bool is_rgb; |
||||
|
union color_union { |
||||
|
uint8_t term_color; |
||||
|
uint32_t rgb_color; |
||||
|
} value; |
||||
|
}; |
||||
|
|
||||
|
FMT_END_DETAIL_NAMESPACE |
||||
|
|
||||
|
/** A text style consisting of foreground and background colors and emphasis. */ |
||||
|
class text_style { |
||||
|
public: |
||||
|
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept |
||||
|
: set_foreground_color(), set_background_color(), ems(em) {} |
||||
|
|
||||
|
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { |
||||
|
if (!set_foreground_color) { |
||||
|
set_foreground_color = rhs.set_foreground_color; |
||||
|
foreground_color = rhs.foreground_color; |
||||
|
} else if (rhs.set_foreground_color) { |
||||
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) |
||||
|
FMT_THROW(format_error("can't OR a terminal color")); |
||||
|
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; |
||||
|
} |
||||
|
|
||||
|
if (!set_background_color) { |
||||
|
set_background_color = rhs.set_background_color; |
||||
|
background_color = rhs.background_color; |
||||
|
} else if (rhs.set_background_color) { |
||||
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb) |
||||
|
FMT_THROW(format_error("can't OR a terminal color")); |
||||
|
background_color.value.rgb_color |= rhs.background_color.value.rgb_color; |
||||
|
} |
||||
|
|
||||
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) | |
||||
|
static_cast<uint8_t>(rhs.ems)); |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
friend FMT_CONSTEXPR text_style operator|(text_style lhs, |
||||
|
const text_style& rhs) { |
||||
|
return lhs |= rhs; |
||||
|
} |
||||
|
|
||||
|
FMT_CONSTEXPR bool has_foreground() const noexcept { |
||||
|
return set_foreground_color; |
||||
|
} |
||||
|
FMT_CONSTEXPR bool has_background() const noexcept { |
||||
|
return set_background_color; |
||||
|
} |
||||
|
FMT_CONSTEXPR bool has_emphasis() const noexcept { |
||||
|
return static_cast<uint8_t>(ems) != 0; |
||||
|
} |
||||
|
FMT_CONSTEXPR detail::color_type get_foreground() const noexcept { |
||||
|
FMT_ASSERT(has_foreground(), "no foreground specified for this style"); |
||||
|
return foreground_color; |
||||
|
} |
||||
|
FMT_CONSTEXPR detail::color_type get_background() const noexcept { |
||||
|
FMT_ASSERT(has_background(), "no background specified for this style"); |
||||
|
return background_color; |
||||
|
} |
||||
|
FMT_CONSTEXPR emphasis get_emphasis() const noexcept { |
||||
|
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); |
||||
|
return ems; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
FMT_CONSTEXPR text_style(bool is_foreground, |
||||
|
detail::color_type text_color) noexcept |
||||
|
: set_foreground_color(), set_background_color(), ems() { |
||||
|
if (is_foreground) { |
||||
|
foreground_color = text_color; |
||||
|
set_foreground_color = true; |
||||
|
} else { |
||||
|
background_color = text_color; |
||||
|
set_background_color = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept; |
||||
|
|
||||
|
friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept; |
||||
|
|
||||
|
detail::color_type foreground_color; |
||||
|
detail::color_type background_color; |
||||
|
bool set_foreground_color; |
||||
|
bool set_background_color; |
||||
|
emphasis ems; |
||||
|
}; |
||||
|
|
||||
|
/** Creates a text style from the foreground (text) color. */ |
||||
|
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept { |
||||
|
return text_style(true, foreground); |
||||
|
} |
||||
|
|
||||
|
/** Creates a text style from the background color. */ |
||||
|
FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept { |
||||
|
return text_style(false, background); |
||||
|
} |
||||
|
|
||||
|
FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept { |
||||
|
return text_style(lhs) | rhs; |
||||
|
} |
||||
|
|
||||
|
FMT_BEGIN_DETAIL_NAMESPACE |
||||
|
|
||||
|
template <typename Char> struct ansi_color_escape { |
||||
|
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, |
||||
|
const char* esc) noexcept { |
||||
|
// If we have a terminal color, we need to output another escape code |
||||
|
// sequence. |
||||
|
if (!text_color.is_rgb) { |
||||
|
bool is_background = esc == string_view("\x1b[48;2;"); |
||||
|
uint32_t value = text_color.value.term_color; |
||||
|
// Background ASCII codes are the same as the foreground ones but with |
||||
|
// 10 more. |
||||
|
if (is_background) value += 10u; |
||||
|
|
||||
|
size_t index = 0; |
||||
|
buffer[index++] = static_cast<Char>('\x1b'); |
||||
|
buffer[index++] = static_cast<Char>('['); |
||||
|
|
||||
|
if (value >= 100u) { |
||||
|
buffer[index++] = static_cast<Char>('1'); |
||||
|
value %= 100u; |
||||
|
} |
||||
|
buffer[index++] = static_cast<Char>('0' + value / 10u); |
||||
|
buffer[index++] = static_cast<Char>('0' + value % 10u); |
||||
|
|
||||
|
buffer[index++] = static_cast<Char>('m'); |
||||
|
buffer[index++] = static_cast<Char>('\0'); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < 7; i++) { |
||||
|
buffer[i] = static_cast<Char>(esc[i]); |
||||
|
} |
||||
|
rgb color(text_color.value.rgb_color); |
||||
|
to_esc(color.r, buffer + 7, ';'); |
||||
|
to_esc(color.g, buffer + 11, ';'); |
||||
|
to_esc(color.b, buffer + 15, 'm'); |
||||
|
buffer[19] = static_cast<Char>(0); |
||||
|
} |
||||
|
FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept { |
||||
|
uint8_t em_codes[num_emphases] = {}; |
||||
|
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; |
||||
|
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2; |
||||
|
if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3; |
||||
|
if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4; |
||||
|
if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5; |
||||
|
if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7; |
||||
|
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; |
||||
|
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; |
||||
|
|
||||
|
size_t index = 0; |
||||
|
for (size_t i = 0; i < num_emphases; ++i) { |
||||
|
if (!em_codes[i]) continue; |
||||
|
buffer[index++] = static_cast<Char>('\x1b'); |
||||
|
buffer[index++] = static_cast<Char>('['); |
||||
|
buffer[index++] = static_cast<Char>('0' + em_codes[i]); |
||||
|
buffer[index++] = static_cast<Char>('m'); |
||||
|
} |
||||
|
buffer[index++] = static_cast<Char>(0); |
||||
|
} |
||||
|
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } |
||||
|
|
||||
|
FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; } |
||||
|
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept { |
||||
|
return buffer + std::char_traits<Char>::length(buffer); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
static constexpr size_t num_emphases = 8; |
||||
|
Char buffer[7u + 3u * num_emphases + 1u]; |
||||
|
|
||||
|
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, |
||||
|
char delimiter) noexcept { |
||||
|
out[0] = static_cast<Char>('0' + c / 100); |
||||
|
out[1] = static_cast<Char>('0' + c / 10 % 10); |
||||
|
out[2] = static_cast<Char>('0' + c % 10); |
||||
|
out[3] = static_cast<Char>(delimiter); |
||||
|
} |
||||
|
static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept { |
||||
|
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
template <typename Char> |
||||
|
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color( |
||||
|
detail::color_type foreground) noexcept { |
||||
|
return ansi_color_escape<Char>(foreground, "\x1b[38;2;"); |
||||
|
} |
||||
|
|
||||
|
template <typename Char> |
||||
|
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color( |
||||
|
detail::color_type background) noexcept { |
||||
|
return ansi_color_escape<Char>(background, "\x1b[48;2;"); |
||||
|
} |
||||
|
|
||||
|
template <typename Char> |
||||
|
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept { |
||||
|
return ansi_color_escape<Char>(em); |
||||
|
} |
||||
|
|
||||
|
template <typename Char> inline void fputs(const Char* chars, FILE* stream) { |
||||
|
int result = std::fputs(chars, stream); |
||||
|
if (result < 0) |
||||
|
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); |
||||
|
} |
||||
|
|
||||
|
template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) { |
||||
|
int result = std::fputws(chars, stream); |
||||
|
if (result < 0) |
||||
|
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); |
||||
|
} |
||||
|
|
||||
|
template <typename Char> inline void reset_color(FILE* stream) { |
||||
|
fputs("\x1b[0m", stream); |
||||
|
} |
||||
|
|
||||
|
template <> inline void reset_color<wchar_t>(FILE* stream) { |
||||
|
fputs(L"\x1b[0m", stream); |
||||
|
} |
||||
|
|
||||
|
template <typename Char> inline void reset_color(buffer<Char>& buffer) { |
||||
|
auto reset_color = string_view("\x1b[0m"); |
||||
|
buffer.append(reset_color.begin(), reset_color.end()); |
||||
|
} |
||||
|
|
||||
|
template <typename T> struct styled_arg { |
||||
|
const T& value; |
||||
|
text_style style; |
||||
|
}; |
||||
|
|
||||
|
template <typename Char> |
||||
|
void vformat_to(buffer<Char>& buf, const text_style& ts, |
||||
|
basic_string_view<Char> format_str, |
||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) { |
||||
|
bool has_style = false; |
||||
|
if (ts.has_emphasis()) { |
||||
|
has_style = true; |
||||
|
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis()); |
||||
|
buf.append(emphasis.begin(), emphasis.end()); |
||||
|
} |
||||
|
if (ts.has_foreground()) { |
||||
|
has_style = true; |
||||
|
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground()); |
||||
|
buf.append(foreground.begin(), foreground.end()); |
||||
|
} |
||||
|
if (ts.has_background()) { |
||||
|
has_style = true; |
||||
|
auto background = detail::make_background_color<Char>(ts.get_background()); |
||||
|
buf.append(background.begin(), background.end()); |
||||
|
} |
||||
|
detail::vformat_to(buf, format_str, args, {}); |
||||
|
if (has_style) detail::reset_color<Char>(buf); |
||||
|
} |
||||
|
|
||||
|
FMT_END_DETAIL_NAMESPACE |
||||
|
|
||||
|
template <typename S, typename Char = char_t<S>> |
||||
|
void vprint(std::FILE* f, const text_style& ts, const S& format, |
||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) { |
||||
|
basic_memory_buffer<Char> buf; |
||||
|
detail::vformat_to(buf, ts, detail::to_string_view(format), args); |
||||
|
if (detail::is_utf8()) { |
||||
|
detail::print(f, basic_string_view<Char>(buf.begin(), buf.size())); |
||||
|
} else { |
||||
|
buf.push_back(Char(0)); |
||||
|
detail::fputs(buf.data(), f); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
\rst |
||||
|
Formats a string and prints it to the specified file stream using ANSI |
||||
|
escape sequences to specify text formatting. |
||||
|
|
||||
|
**Example**:: |
||||
|
|
||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), |
||||
|
"Elapsed time: {0:.2f} seconds", 1.23); |
||||
|
\endrst |
||||
|
*/ |
||||
|
template <typename S, typename... Args, |
||||
|
FMT_ENABLE_IF(detail::is_string<S>::value)> |
||||
|
void print(std::FILE* f, const text_style& ts, const S& format_str, |
||||
|
const Args&... args) { |
||||
|
vprint(f, ts, format_str, |
||||
|
fmt::make_format_args<buffer_context<char_t<S>>>(args...)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
\rst |
||||
|
Formats a string and prints it to stdout using ANSI escape sequences to |
||||
|
specify text formatting. |
||||
|
|
||||
|
**Example**:: |
||||
|
|
||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), |
||||
|
"Elapsed time: {0:.2f} seconds", 1.23); |
||||
|
\endrst |
||||
|
*/ |
||||
|
template <typename S, typename... Args, |
||||
|
FMT_ENABLE_IF(detail::is_string<S>::value)> |
||||
|
void print(const text_style& ts, const S& format_str, const Args&... args) { |
||||
|
return print(stdout, ts, format_str, args...); |
||||
|
} |
||||
|
|
||||
|
template <typename S, typename Char = char_t<S>> |
||||
|
inline std::basic_string<Char> vformat( |
||||
|
const text_style& ts, const S& format_str, |
||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) { |
||||
|
basic_memory_buffer<Char> buf; |
||||
|
detail::vformat_to(buf, ts, detail::to_string_view(format_str), args); |
||||
|
return fmt::to_string(buf); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
\rst |
||||
|
Formats arguments and returns the result as a string using ANSI |
||||
|
escape sequences to specify text formatting. |
||||
|
|
||||
|
**Example**:: |
||||
|
|
||||
|
#include <fmt/color.h> |
||||
|
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), |
||||
|
"The answer is {}", 42); |
||||
|
\endrst |
||||
|
*/ |
||||
|
template <typename S, typename... Args, typename Char = char_t<S>> |
||||
|
inline std::basic_string<Char> format(const text_style& ts, const S& format_str, |
||||
|
const Args&... args) { |
||||
|
return fmt::vformat(ts, detail::to_string_view(format_str), |
||||
|
fmt::make_format_args<buffer_context<Char>>(args...)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
Formats a string with the given text_style and writes the output to ``out``. |
||||
|
*/ |
||||
|
template <typename OutputIt, typename Char, |
||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)> |
||||
|
OutputIt vformat_to( |
||||
|
OutputIt out, const text_style& ts, basic_string_view<Char> format_str, |
||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) { |
||||
|
auto&& buf = detail::get_buffer<Char>(out); |
||||
|
detail::vformat_to(buf, ts, format_str, args); |
||||
|
return detail::get_iterator(buf); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
\rst |
||||
|
Formats arguments with the given text_style, writes the result to the output |
||||
|
iterator ``out`` and returns the iterator past the end of the output range. |
||||
|
|
||||
|
**Example**:: |
||||
|
|
||||
|
std::vector<char> out; |
||||
|
fmt::format_to(std::back_inserter(out), |
||||
|
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); |
||||
|
\endrst |
||||
|
*/ |
||||
|
template <typename OutputIt, typename S, typename... Args, |
||||
|
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&& |
||||
|
detail::is_string<S>::value> |
||||
|
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, |
||||
|
Args&&... args) -> |
||||
|
typename std::enable_if<enable, OutputIt>::type { |
||||
|
return vformat_to(out, ts, detail::to_string_view(format_str), |
||||
|
fmt::make_format_args<buffer_context<char_t<S>>>(args...)); |
||||
|
} |
||||
|
|
||||
|
template <typename T, typename Char> |
||||
|
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> { |
||||
|
template <typename FormatContext> |
||||
|
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const |
||||
|
-> decltype(ctx.out()) { |
||||
|
const auto& ts = arg.style; |
||||
|
const auto& value = arg.value; |
||||
|
auto out = ctx.out(); |
||||
|
|
||||
|
bool has_style = false; |
||||
|
if (ts.has_emphasis()) { |
||||
|
has_style = true; |
||||
|
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis()); |
||||
|
out = std::copy(emphasis.begin(), emphasis.end(), out); |
||||
|
} |
||||
|
if (ts.has_foreground()) { |
||||
|
has_style = true; |
||||
|
auto foreground = |
||||
|
detail::make_foreground_color<Char>(ts.get_foreground()); |
||||
|
out = std::copy(foreground.begin(), foreground.end(), out); |
||||
|
} |
||||
|
if (ts.has_background()) { |
||||
|
has_style = true; |
||||
|
auto background = |
||||
|
detail::make_background_color<Char>(ts.get_background()); |
||||
|
out = std::copy(background.begin(), background.end(), out); |
||||
|
} |
||||
|
out = formatter<T, Char>::format(value, ctx); |
||||
|
if (has_style) { |
||||
|
auto reset_color = string_view("\x1b[0m"); |
||||
|
out = std::copy(reset_color.begin(), reset_color.end(), out); |
||||
|
} |
||||
|
return out; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
\rst |
||||
|
Returns an argument that will be formatted using ANSI escape sequences, |
||||
|
to be used in a formatting function. |
||||
|
|
||||
|
**Example**:: |
||||
|
|
||||
|
fmt::print("Elapsed time: {0:.2f} seconds", |
||||
|
fmt::styled(1.23, fmt::fg(fmt::color::green) | |
||||
|
fmt::bg(fmt::color::blue))); |
||||
|
\endrst |
||||
|
*/ |
||||
|
template <typename T> |
||||
|
FMT_CONSTEXPR auto styled(const T& value, text_style ts) |
||||
|
-> detail::styled_arg<remove_cvref_t<T>> { |
||||
|
return detail::styled_arg<remove_cvref_t<T>>{value, ts}; |
||||
|
} |
||||
|
|
||||
|
FMT_MODULE_EXPORT_END |
||||
|
FMT_END_NAMESPACE |
||||
|
|
||||
|
#endif // FMT_COLOR_H_ |
@ -0,0 +1,611 @@ |
|||||
|
// 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> |
||||
|
FMT_CONSTEXPR 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()), |
||||
|
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 != ':') { |
||||
|
FMT_THROW(format_error("expected ':'")); |
||||
|
} else { |
||||
|
constexpr auto result = parse_specs<typename field_type<T>::type>( |
||||
|
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); |
||||
|
if constexpr (result.end >= str.size() || str[result.end] != '}') { |
||||
|
FMT_THROW(format_error("expected '}'")); |
||||
|
return 0; |
||||
|
} else { |
||||
|
return parse_tail<Args, result.end + 1, 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)> |
||||
|
FMT_CONSTEXPR20 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_ |
3323
src/fmt/core.h
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1723
src/fmt/format-inl.h
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
4217
src/fmt/format.h
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,478 @@ |
|||||
|
// Formatting library for C++ - optional OS-specific functionality |
||||
|
// |
||||
|
// Copyright (c) 2012 - present, Victor Zverovich |
||||
|
// All rights reserved. |
||||
|
// |
||||
|
// For the license information refer to format.h. |
||||
|
|
||||
|
#ifndef FMT_OS_H_ |
||||
|
#define FMT_OS_H_ |
||||
|
|
||||
|
#include <cerrno> |
||||
|
#include <cstddef> |
||||
|
#include <cstdio> |
||||
|
#include <system_error> // std::system_error |
||||
|
|
||||
|
#if defined __APPLE__ || defined(__FreeBSD__) |
||||
|
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X |
||||
|
#endif |
||||
|
|
||||
|
#include "format.h" |
||||
|
|
||||
|
#ifndef FMT_USE_FCNTL |
||||
|
// UWP doesn't provide _pipe. |
||||
|
# if FMT_HAS_INCLUDE("winapifamily.h") |
||||
|
# include <winapifamily.h> |
||||
|
# endif |
||||
|
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \ |
||||
|
defined(__linux__)) && \ |
||||
|
(!defined(WINAPI_FAMILY) || \ |
||||
|
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) |
||||
|
# include <fcntl.h> // for O_RDONLY |
||||
|
# define FMT_USE_FCNTL 1 |
||||
|
# else |
||||
|
# define FMT_USE_FCNTL 0 |
||||
|
# endif |
||||
|
#endif |
||||
|
|
||||
|
#ifndef FMT_POSIX |
||||
|
# if defined(_WIN32) && !defined(__MINGW32__) |
||||
|
// Fix warnings about deprecated symbols. |
||||
|
# define FMT_POSIX(call) _##call |
||||
|
# else |
||||
|
# define FMT_POSIX(call) call |
||||
|
# endif |
||||
|
#endif |
||||
|
|
||||
|
// Calls to system functions are wrapped in FMT_SYSTEM for testability. |
||||
|
#ifdef FMT_SYSTEM |
||||
|
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call) |
||||
|
#else |
||||
|
# define FMT_SYSTEM(call) ::call |
||||
|
# ifdef _WIN32 |
||||
|
// Fix warnings about deprecated symbols. |
||||
|
# define FMT_POSIX_CALL(call) ::_##call |
||||
|
# else |
||||
|
# define FMT_POSIX_CALL(call) ::call |
||||
|
# endif |
||||
|
#endif |
||||
|
|
||||
|
// Retries the expression while it evaluates to error_result and errno |
||||
|
// equals to EINTR. |
||||
|
#ifndef _WIN32 |
||||
|
# define FMT_RETRY_VAL(result, expression, error_result) \ |
||||
|
do { \ |
||||
|
(result) = (expression); \ |
||||
|
} while ((result) == (error_result) && errno == EINTR) |
||||
|
#else |
||||
|
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression) |
||||
|
#endif |
||||
|
|
||||
|
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) |
||||
|
|
||||
|
FMT_BEGIN_NAMESPACE |
||||
|
FMT_MODULE_EXPORT_BEGIN |
||||
|
|
||||
|
/** |
||||
|
\rst |
||||
|
A reference to a null-terminated string. It can be constructed from a C |
||||
|
string or ``std::string``. |
||||
|
|
||||
|
You can use one of the following type aliases for common character types: |
||||
|
|
||||
|
+---------------+-----------------------------+ |
||||
|
| Type | Definition | |
||||
|
+===============+=============================+ |
||||
|
| cstring_view | basic_cstring_view<char> | |
||||
|
+---------------+-----------------------------+ |
||||
|
| wcstring_view | basic_cstring_view<wchar_t> | |
||||
|
+---------------+-----------------------------+ |
||||
|
|
||||
|
This class is most useful as a parameter type to allow passing |
||||
|
different types of strings to a function, for example:: |
||||
|
|
||||
|
template <typename... Args> |
||||
|
std::string format(cstring_view format_str, const Args & ... args); |
||||
|
|
||||
|
format("{}", 42); |
||||
|
format(std::string("{}"), 42); |
||||
|
\endrst |
||||
|
*/ |
||||
|
template <typename Char> class basic_cstring_view { |
||||
|
private: |
||||
|
const Char* data_; |
||||
|
|
||||
|
public: |
||||
|
/** Constructs a string reference object from a C string. */ |
||||
|
basic_cstring_view(const Char* s) : data_(s) {} |
||||
|
|
||||
|
/** |
||||
|
\rst |
||||
|
Constructs a string reference from an ``std::string`` object. |
||||
|
\endrst |
||||
|
*/ |
||||
|
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {} |
||||
|
|
||||
|
/** Returns the pointer to a C string. */ |
||||
|
const Char* c_str() const { return data_; } |
||||
|
}; |
||||
|
|
||||
|
using cstring_view = basic_cstring_view<char>; |
||||
|
using wcstring_view = basic_cstring_view<wchar_t>; |
||||
|
|
||||
|
template <typename Char> struct formatter<std::error_code, Char> { |
||||
|
template <typename ParseContext> |
||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { |
||||
|
return ctx.begin(); |
||||
|
} |
||||
|
|
||||
|
template <typename FormatContext> |
||||
|
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const |
||||
|
-> decltype(ctx.out()) { |
||||
|
auto out = ctx.out(); |
||||
|
out = detail::write_bytes(out, ec.category().name(), |
||||
|
basic_format_specs<Char>()); |
||||
|
out = detail::write<Char>(out, Char(':')); |
||||
|
out = detail::write<Char>(out, ec.value()); |
||||
|
return out; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
#ifdef _WIN32 |
||||
|
FMT_API const std::error_category& system_category() noexcept; |
||||
|
|
||||
|
FMT_BEGIN_DETAIL_NAMESPACE |
||||
|
// A converter from UTF-16 to UTF-8. |
||||
|
// It is only provided for Windows since other systems support UTF-8 natively. |
||||
|
class utf16_to_utf8 { |
||||
|
private: |
||||
|
memory_buffer buffer_; |
||||
|
|
||||
|
public: |
||||
|
utf16_to_utf8() {} |
||||
|
FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s); |
||||
|
operator string_view() const { return string_view(&buffer_[0], size()); } |
||||
|
size_t size() const { return buffer_.size() - 1; } |
||||
|
const char* c_str() const { return &buffer_[0]; } |
||||
|
std::string str() const { return std::string(&buffer_[0], size()); } |
||||
|
|
||||
|
// Performs conversion returning a system error code instead of |
||||
|
// throwing exception on conversion error. This method may still throw |
||||
|
// in case of memory allocation error. |
||||
|
FMT_API int convert(basic_string_view<wchar_t> s); |
||||
|
}; |
||||
|
|
||||
|
FMT_API void format_windows_error(buffer<char>& out, int error_code, |
||||
|
const char* message) noexcept; |
||||
|
FMT_END_DETAIL_NAMESPACE |
||||
|
|
||||
|
FMT_API std::system_error vwindows_error(int error_code, string_view format_str, |
||||
|
format_args args); |
||||
|
|
||||
|
/** |
||||
|
\rst |
||||
|
Constructs a :class:`std::system_error` object with the description |
||||
|
of the form |
||||
|
|
||||
|
.. parsed-literal:: |
||||
|
*<message>*: *<system-message>* |
||||
|
|
||||
|
where *<message>* is the formatted message and *<system-message>* is the |
||||
|
system message corresponding to the error code. |
||||
|
*error_code* is a Windows error code as given by ``GetLastError``. |
||||
|
If *error_code* is not a valid error code such as -1, the system message |
||||
|
will look like "error -1". |
||||
|
|
||||
|
**Example**:: |
||||
|
|
||||
|
// This throws a system_error with the description |
||||
|
// cannot open file 'madeup': The system cannot find the file specified. |
||||
|
// or similar (system message may vary). |
||||
|
const char *filename = "madeup"; |
||||
|
LPOFSTRUCT of = LPOFSTRUCT(); |
||||
|
HFILE file = OpenFile(filename, &of, OF_READ); |
||||
|
if (file == HFILE_ERROR) { |
||||
|
throw fmt::windows_error(GetLastError(), |
||||
|
"cannot open file '{}'", filename); |
||||
|
} |
||||
|
\endrst |
||||
|
*/ |
||||
|
template <typename... Args> |
||||
|
std::system_error windows_error(int error_code, string_view message, |
||||
|
const Args&... args) { |
||||
|
return vwindows_error(error_code, message, fmt::make_format_args(args...)); |
||||
|
} |
||||
|
|
||||
|
// Reports a Windows error without throwing an exception. |
||||
|
// Can be used to report errors from destructors. |
||||
|
FMT_API void report_windows_error(int error_code, const char* message) noexcept; |
||||
|
#else |
||||
|
inline const std::error_category& system_category() noexcept { |
||||
|
return std::system_category(); |
||||
|
} |
||||
|
#endif // _WIN32 |
||||
|
|
||||
|
// std::system is not available on some platforms such as iOS (#2248). |
||||
|
#ifdef __OSX__ |
||||
|
template <typename S, typename... Args, typename Char = char_t<S>> |
||||
|
void say(const S& format_str, Args&&... args) { |
||||
|
std::system(format("say \"{}\"", format(format_str, args...)).c_str()); |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
// A buffered file. |
||||
|
class buffered_file { |
||||
|
private: |
||||
|
FILE* file_; |
||||
|
|
||||
|
friend class file; |
||||
|
|
||||
|
explicit buffered_file(FILE* f) : file_(f) {} |
||||
|
|
||||
|
public: |
||||
|
buffered_file(const buffered_file&) = delete; |
||||
|
void operator=(const buffered_file&) = delete; |
||||
|
|
||||
|
// Constructs a buffered_file object which doesn't represent any file. |
||||
|
buffered_file() noexcept : file_(nullptr) {} |
||||
|
|
||||
|
// Destroys the object closing the file it represents if any. |
||||
|
FMT_API ~buffered_file() noexcept; |
||||
|
|
||||
|
public: |
||||
|
buffered_file(buffered_file&& other) noexcept : file_(other.file_) { |
||||
|
other.file_ = nullptr; |
||||
|
} |
||||
|
|
||||
|
buffered_file& operator=(buffered_file&& other) { |
||||
|
close(); |
||||
|
file_ = other.file_; |
||||
|
other.file_ = nullptr; |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
// Opens a file. |
||||
|
FMT_API buffered_file(cstring_view filename, cstring_view mode); |
||||
|
|
||||
|
// Closes the file. |
||||
|
FMT_API void close(); |
||||
|
|
||||
|
// Returns the pointer to a FILE object representing this file. |
||||
|
FILE* get() const noexcept { return file_; } |
||||
|
|
||||
|
FMT_API int descriptor() const; |
||||
|
|
||||
|
void vprint(string_view format_str, format_args args) { |
||||
|
fmt::vprint(file_, format_str, args); |
||||
|
} |
||||
|
|
||||
|
template <typename... Args> |
||||
|
inline void print(string_view format_str, const Args&... args) { |
||||
|
vprint(format_str, fmt::make_format_args(args...)); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
#if FMT_USE_FCNTL |
||||
|
// A file. Closed file is represented by a file object with descriptor -1. |
||||
|
// Methods that are not declared with noexcept may throw |
||||
|
// fmt::system_error in case of failure. Note that some errors such as |
||||
|
// closing the file multiple times will cause a crash on Windows rather |
||||
|
// than an exception. You can get standard behavior by overriding the |
||||
|
// invalid parameter handler with _set_invalid_parameter_handler. |
||||
|
class FMT_API file { |
||||
|
private: |
||||
|
int fd_; // File descriptor. |
||||
|
|
||||
|
// Constructs a file object with a given descriptor. |
||||
|
explicit file(int fd) : fd_(fd) {} |
||||
|
|
||||
|
public: |
||||
|
// Possible values for the oflag argument to the constructor. |
||||
|
enum { |
||||
|
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. |
||||
|
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. |
||||
|
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing. |
||||
|
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist. |
||||
|
APPEND = FMT_POSIX(O_APPEND), // Open in append mode. |
||||
|
TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file. |
||||
|
}; |
||||
|
|
||||
|
// Constructs a file object which doesn't represent any file. |
||||
|
file() noexcept : fd_(-1) {} |
||||
|
|
||||
|
// Opens a file and constructs a file object representing this file. |
||||
|
file(cstring_view path, int oflag); |
||||
|
|
||||
|
public: |
||||
|
file(const file&) = delete; |
||||
|
void operator=(const file&) = delete; |
||||
|
|
||||
|
file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } |
||||
|
|
||||
|
// Move assignment is not noexcept because close may throw. |
||||
|
file& operator=(file&& other) { |
||||
|
close(); |
||||
|
fd_ = other.fd_; |
||||
|
other.fd_ = -1; |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
// Destroys the object closing the file it represents if any. |
||||
|
~file() noexcept; |
||||
|
|
||||
|
// Returns the file descriptor. |
||||
|
int descriptor() const noexcept { return fd_; } |
||||
|
|
||||
|
// Closes the file. |
||||
|
void close(); |
||||
|
|
||||
|
// Returns the file size. The size has signed type for consistency with |
||||
|
// stat::st_size. |
||||
|
long long size() const; |
||||
|
|
||||
|
// Attempts to read count bytes from the file into the specified buffer. |
||||
|
size_t read(void* buffer, size_t count); |
||||
|
|
||||
|
// Attempts to write count bytes from the specified buffer to the file. |
||||
|
size_t write(const void* buffer, size_t count); |
||||
|
|
||||
|
// Duplicates a file descriptor with the dup function and returns |
||||
|
// the duplicate as a file object. |
||||
|
static file dup(int fd); |
||||
|
|
||||
|
// Makes fd be the copy of this file descriptor, closing fd first if |
||||
|
// necessary. |
||||
|
void dup2(int fd); |
||||
|
|
||||
|
// Makes fd be the copy of this file descriptor, closing fd first if |
||||
|
// necessary. |
||||
|
void dup2(int fd, std::error_code& ec) noexcept; |
||||
|
|
||||
|
// Creates a pipe setting up read_end and write_end file objects for reading |
||||
|
// and writing respectively. |
||||
|
static void pipe(file& read_end, file& write_end); |
||||
|
|
||||
|
// Creates a buffered_file object associated with this file and detaches |
||||
|
// this file object from the file. |
||||
|
buffered_file fdopen(const char* mode); |
||||
|
}; |
||||
|
|
||||
|
// Returns the memory page size. |
||||
|
long getpagesize(); |
||||
|
|
||||
|
FMT_BEGIN_DETAIL_NAMESPACE |
||||
|
|
||||
|
struct buffer_size { |
||||
|
buffer_size() = default; |
||||
|
size_t value = 0; |
||||
|
buffer_size operator=(size_t val) const { |
||||
|
auto bs = buffer_size(); |
||||
|
bs.value = val; |
||||
|
return bs; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
struct ostream_params { |
||||
|
int oflag = file::WRONLY | file::CREATE | file::TRUNC; |
||||
|
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; |
||||
|
|
||||
|
ostream_params() {} |
||||
|
|
||||
|
template <typename... T> |
||||
|
ostream_params(T... params, int new_oflag) : ostream_params(params...) { |
||||
|
oflag = new_oflag; |
||||
|
} |
||||
|
|
||||
|
template <typename... T> |
||||
|
ostream_params(T... params, detail::buffer_size bs) |
||||
|
: ostream_params(params...) { |
||||
|
this->buffer_size = bs.value; |
||||
|
} |
||||
|
|
||||
|
// Intel has a bug that results in failure to deduce a constructor |
||||
|
// for empty parameter packs. |
||||
|
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000 |
||||
|
ostream_params(int new_oflag) : oflag(new_oflag) {} |
||||
|
ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {} |
||||
|
# endif |
||||
|
}; |
||||
|
|
||||
|
FMT_END_DETAIL_NAMESPACE |
||||
|
|
||||
|
// Added {} below to work around default constructor error known to |
||||
|
// occur in Xcode versions 7.2.1 and 8.2.1. |
||||
|
constexpr detail::buffer_size buffer_size{}; |
||||
|
|
||||
|
/** A fast output stream which is not thread-safe. */ |
||||
|
class FMT_API ostream final : private detail::buffer<char> { |
||||
|
private: |
||||
|
file file_; |
||||
|
|
||||
|
void grow(size_t) override; |
||||
|
|
||||
|
ostream(cstring_view path, const detail::ostream_params& params) |
||||
|
: file_(path, params.oflag) { |
||||
|
set(new char[params.buffer_size], params.buffer_size); |
||||
|
} |
||||
|
|
||||
|
public: |
||||
|
ostream(ostream&& other) |
||||
|
: detail::buffer<char>(other.data(), other.size(), other.capacity()), |
||||
|
file_(std::move(other.file_)) { |
||||
|
other.clear(); |
||||
|
other.set(nullptr, 0); |
||||
|
} |
||||
|
~ostream() { |
||||
|
flush(); |
||||
|
delete[] data(); |
||||
|
} |
||||
|
|
||||
|
void flush() { |
||||
|
if (size() == 0) return; |
||||
|
file_.write(data(), size()); |
||||
|
clear(); |
||||
|
} |
||||
|
|
||||
|
template <typename... T> |
||||
|
friend ostream output_file(cstring_view path, T... params); |
||||
|
|
||||
|
void close() { |
||||
|
flush(); |
||||
|
file_.close(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
Formats ``args`` according to specifications in ``fmt`` and writes the |
||||
|
output to the file. |
||||
|
*/ |
||||
|
template <typename... T> void print(format_string<T...> fmt, T&&... args) { |
||||
|
vformat_to(detail::buffer_appender<char>(*this), fmt, |
||||
|
fmt::make_format_args(args...)); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
\rst |
||||
|
Opens a file for writing. Supported parameters passed in *params*: |
||||
|
|
||||
|
* ``<integer>``: Flags passed to `open |
||||
|
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_ |
||||
|
(``file::WRONLY | file::CREATE | file::TRUNC`` by default) |
||||
|
* ``buffer_size=<integer>``: Output buffer size |
||||
|
|
||||
|
**Example**:: |
||||
|
|
||||
|
auto out = fmt::output_file("guide.txt"); |
||||
|
out.print("Don't {}", "Panic"); |
||||
|
\endrst |
||||
|
*/ |
||||
|
template <typename... T> |
||||
|
inline ostream output_file(cstring_view path, T... params) { |
||||
|
return {path, detail::ostream_params(params...)}; |
||||
|
} |
||||
|
#endif // FMT_USE_FCNTL |
||||
|
|
||||
|
FMT_MODULE_EXPORT_END |
||||
|
FMT_END_NAMESPACE |
||||
|
|
||||
|
#endif // FMT_OS_H_ |
@ -0,0 +1,237 @@ |
|||||
|
// 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, 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) { |
||||
|
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; |
||||
|
}; |
||||
|
|
||||
|
inline void vprint_directly(std::ostream& os, string_view format_str, |
||||
|
format_args args) { |
||||
|
auto buffer = memory_buffer(); |
||||
|
detail::vformat_to(buffer, format_str, args); |
||||
|
detail::write_buffer(os, buffer); |
||||
|
} |
||||
|
|
||||
|
} // 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); |
||||
|
if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return; |
||||
|
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) { |
||||
|
const auto& vargs = fmt::make_format_args(args...); |
||||
|
if (detail::is_utf8()) |
||||
|
vprint(os, fmt, vargs); |
||||
|
else |
||||
|
detail::vprint_directly(os, fmt, vargs); |
||||
|
} |
||||
|
|
||||
|
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,640 @@ |
|||||
|
// Formatting library for C++ - legacy printf implementation |
||||
|
// |
||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich |
||||
|
// All rights reserved. |
||||
|
// |
||||
|
// For the license information refer to format.h. |
||||
|
|
||||
|
#ifndef FMT_PRINTF_H_ |
||||
|
#define FMT_PRINTF_H_ |
||||
|
|
||||
|
#include <algorithm> // std::max |
||||
|
#include <limits> // std::numeric_limits |
||||
|
|
||||
|
#include "format.h" |
||||
|
|
||||
|
FMT_BEGIN_NAMESPACE |
||||
|
FMT_MODULE_EXPORT_BEGIN |
||||
|
|
||||
|
template <typename T> struct printf_formatter { printf_formatter() = delete; }; |
||||
|
|
||||
|
template <typename Char> |
||||
|
class basic_printf_parse_context : public basic_format_parse_context<Char> { |
||||
|
using basic_format_parse_context<Char>::basic_format_parse_context; |
||||
|
}; |
||||
|
|
||||
|
template <typename OutputIt, typename Char> class basic_printf_context { |
||||
|
private: |
||||
|
OutputIt out_; |
||||
|
basic_format_args<basic_printf_context> args_; |
||||
|
|
||||
|
public: |
||||
|
using char_type = Char; |
||||
|
using format_arg = basic_format_arg<basic_printf_context>; |
||||
|
using parse_context_type = basic_printf_parse_context<Char>; |
||||
|
template <typename T> using formatter_type = printf_formatter<T>; |
||||
|
|
||||
|
/** |
||||
|
\rst |
||||
|
Constructs a ``printf_context`` object. References to the arguments are |
||||
|
stored in the context object so make sure they have appropriate lifetimes. |
||||
|
\endrst |
||||
|
*/ |
||||
|
basic_printf_context(OutputIt out, |
||||
|
basic_format_args<basic_printf_context> args) |
||||
|
: out_(out), args_(args) {} |
||||
|
|
||||
|
OutputIt out() { return out_; } |
||||
|
void advance_to(OutputIt it) { out_ = it; } |
||||
|
|
||||
|
detail::locale_ref locale() { return {}; } |
||||
|
|
||||
|
format_arg arg(int id) const { return args_.get(id); } |
||||
|
|
||||
|
FMT_CONSTEXPR void on_error(const char* message) { |
||||
|
detail::error_handler().on_error(message); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
FMT_BEGIN_DETAIL_NAMESPACE |
||||
|
|
||||
|
// Checks if a value fits in int - used to avoid warnings about comparing |
||||
|
// signed and unsigned integers. |
||||
|
template <bool IsSigned> struct int_checker { |
||||
|
template <typename T> static bool fits_in_int(T value) { |
||||
|
unsigned max = max_value<int>(); |
||||
|
return value <= max; |
||||
|
} |
||||
|
static bool fits_in_int(bool) { return true; } |
||||
|
}; |
||||
|
|
||||
|
template <> struct int_checker<true> { |
||||
|
template <typename T> static bool fits_in_int(T value) { |
||||
|
return value >= (std::numeric_limits<int>::min)() && |
||||
|
value <= max_value<int>(); |
||||
|
} |
||||
|
static bool fits_in_int(int) { return true; } |
||||
|
}; |
||||
|
|
||||
|
class printf_precision_handler { |
||||
|
public: |
||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> |
||||
|
int operator()(T value) { |
||||
|
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) |
||||
|
FMT_THROW(format_error("number is too big")); |
||||
|
return (std::max)(static_cast<int>(value), 0); |
||||
|
} |
||||
|
|
||||
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> |
||||
|
int operator()(T) { |
||||
|
FMT_THROW(format_error("precision is not integer")); |
||||
|
return 0; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// An argument visitor that returns true iff arg is a zero integer. |
||||
|
class is_zero_int { |
||||
|
public: |
||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> |
||||
|
bool operator()(T value) { |
||||
|
return value == 0; |
||||
|
} |
||||
|
|
||||
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> |
||||
|
bool operator()(T) { |
||||
|
return false; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {}; |
||||
|
|
||||
|
template <> struct make_unsigned_or_bool<bool> { using type = bool; }; |
||||
|
|
||||
|
template <typename T, typename Context> class arg_converter { |
||||
|
private: |
||||
|
using char_type = typename Context::char_type; |
||||
|
|
||||
|
basic_format_arg<Context>& arg_; |
||||
|
char_type type_; |
||||
|
|
||||
|
public: |
||||
|
arg_converter(basic_format_arg<Context>& arg, char_type type) |
||||
|
: arg_(arg), type_(type) {} |
||||
|
|
||||
|
void operator()(bool value) { |
||||
|
if (type_ != 's') operator()<bool>(value); |
||||
|
} |
||||
|
|
||||
|
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)> |
||||
|
void operator()(U value) { |
||||
|
bool is_signed = type_ == 'd' || type_ == 'i'; |
||||
|
using target_type = conditional_t<std::is_same<T, void>::value, U, T>; |
||||
|
if (const_check(sizeof(target_type) <= sizeof(int))) { |
||||
|
// Extra casts are used to silence warnings. |
||||
|
if (is_signed) { |
||||
|
arg_ = detail::make_arg<Context>( |
||||
|
static_cast<int>(static_cast<target_type>(value))); |
||||
|
} else { |
||||
|
using unsigned_type = typename make_unsigned_or_bool<target_type>::type; |
||||
|
arg_ = detail::make_arg<Context>( |
||||
|
static_cast<unsigned>(static_cast<unsigned_type>(value))); |
||||
|
} |
||||
|
} else { |
||||
|
if (is_signed) { |
||||
|
// glibc's printf doesn't sign extend arguments of smaller types: |
||||
|
// std::printf("%lld", -42); // prints "4294967254" |
||||
|
// but we don't have to do the same because it's a UB. |
||||
|
arg_ = detail::make_arg<Context>(static_cast<long long>(value)); |
||||
|
} else { |
||||
|
arg_ = detail::make_arg<Context>( |
||||
|
static_cast<typename make_unsigned_or_bool<U>::type>(value)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)> |
||||
|
void operator()(U) {} // No conversion needed for non-integral types. |
||||
|
}; |
||||
|
|
||||
|
// Converts an integer argument to T for printf, if T is an integral type. |
||||
|
// If T is void, the argument is converted to corresponding signed or unsigned |
||||
|
// type depending on the type specifier: 'd' and 'i' - signed, other - |
||||
|
// unsigned). |
||||
|
template <typename T, typename Context, typename Char> |
||||
|
void convert_arg(basic_format_arg<Context>& arg, Char type) { |
||||
|
visit_format_arg(arg_converter<T, Context>(arg, type), arg); |
||||
|
} |
||||
|
|
||||
|
// Converts an integer argument to char for printf. |
||||
|
template <typename Context> class char_converter { |
||||
|
private: |
||||
|
basic_format_arg<Context>& arg_; |
||||
|
|
||||
|
public: |
||||
|
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {} |
||||
|
|
||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> |
||||
|
void operator()(T value) { |
||||
|
arg_ = detail::make_arg<Context>( |
||||
|
static_cast<typename Context::char_type>(value)); |
||||
|
} |
||||
|
|
||||
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> |
||||
|
void operator()(T) {} // No conversion needed for non-integral types. |
||||
|
}; |
||||
|
|
||||
|
// An argument visitor that return a pointer to a C string if argument is a |
||||
|
// string or null otherwise. |
||||
|
template <typename Char> struct get_cstring { |
||||
|
template <typename T> const Char* operator()(T) { return nullptr; } |
||||
|
const Char* operator()(const Char* s) { return s; } |
||||
|
}; |
||||
|
|
||||
|
// Checks if an argument is a valid printf width specifier and sets |
||||
|
// left alignment if it is negative. |
||||
|
template <typename Char> class printf_width_handler { |
||||
|
private: |
||||
|
using format_specs = basic_format_specs<Char>; |
||||
|
|
||||
|
format_specs& specs_; |
||||
|
|
||||
|
public: |
||||
|
explicit printf_width_handler(format_specs& specs) : specs_(specs) {} |
||||
|
|
||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> |
||||
|
unsigned operator()(T value) { |
||||
|
auto width = static_cast<uint32_or_64_or_128_t<T>>(value); |
||||
|
if (detail::is_negative(value)) { |
||||
|
specs_.align = align::left; |
||||
|
width = 0 - width; |
||||
|
} |
||||
|
unsigned int_max = max_value<int>(); |
||||
|
if (width > int_max) FMT_THROW(format_error("number is too big")); |
||||
|
return static_cast<unsigned>(width); |
||||
|
} |
||||
|
|
||||
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> |
||||
|
unsigned operator()(T) { |
||||
|
FMT_THROW(format_error("width is not integer")); |
||||
|
return 0; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// The ``printf`` argument formatter. |
||||
|
template <typename OutputIt, typename Char> |
||||
|
class printf_arg_formatter : public arg_formatter<Char> { |
||||
|
private: |
||||
|
using base = arg_formatter<Char>; |
||||
|
using context_type = basic_printf_context<OutputIt, Char>; |
||||
|
using format_specs = basic_format_specs<Char>; |
||||
|
|
||||
|
context_type& context_; |
||||
|
|
||||
|
OutputIt write_null_pointer(bool is_string = false) { |
||||
|
auto s = this->specs; |
||||
|
s.type = presentation_type::none; |
||||
|
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s); |
||||
|
} |
||||
|
|
||||
|
public: |
||||
|
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx) |
||||
|
: base{iter, s, locale_ref()}, context_(ctx) {} |
||||
|
|
||||
|
OutputIt operator()(monostate value) { return base::operator()(value); } |
||||
|
|
||||
|
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)> |
||||
|
OutputIt operator()(T value) { |
||||
|
// MSVC2013 fails to compile separate overloads for bool and Char so use |
||||
|
// std::is_same instead. |
||||
|
if (std::is_same<T, Char>::value) { |
||||
|
format_specs fmt_specs = this->specs; |
||||
|
if (fmt_specs.type != presentation_type::none && |
||||
|
fmt_specs.type != presentation_type::chr) { |
||||
|
return (*this)(static_cast<int>(value)); |
||||
|
} |
||||
|
fmt_specs.sign = sign::none; |
||||
|
fmt_specs.alt = false; |
||||
|
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types. |
||||
|
// align::numeric needs to be overwritten here since the '0' flag is |
||||
|
// ignored for non-numeric types |
||||
|
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) |
||||
|
fmt_specs.align = align::right; |
||||
|
return write<Char>(this->out, static_cast<Char>(value), fmt_specs); |
||||
|
} |
||||
|
return base::operator()(value); |
||||
|
} |
||||
|
|
||||
|
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> |
||||
|
OutputIt operator()(T value) { |
||||
|
return base::operator()(value); |
||||
|
} |
||||
|
|
||||
|
/** Formats a null-terminated C string. */ |
||||
|
OutputIt operator()(const char* value) { |
||||
|
if (value) return base::operator()(value); |
||||
|
return write_null_pointer(this->specs.type != presentation_type::pointer); |
||||
|
} |
||||
|
|
||||
|
/** Formats a null-terminated wide C string. */ |
||||
|
OutputIt operator()(const wchar_t* value) { |
||||
|
if (value) return base::operator()(value); |
||||
|
return write_null_pointer(this->specs.type != presentation_type::pointer); |
||||
|
} |
||||
|
|
||||
|
OutputIt operator()(basic_string_view<Char> value) { |
||||
|
return base::operator()(value); |
||||
|
} |
||||
|
|
||||
|
/** Formats a pointer. */ |
||||
|
OutputIt operator()(const void* value) { |
||||
|
return value ? base::operator()(value) : write_null_pointer(); |
||||
|
} |
||||
|
|
||||
|
/** Formats an argument of a custom (user-defined) type. */ |
||||
|
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) { |
||||
|
auto parse_ctx = |
||||
|
basic_printf_parse_context<Char>(basic_string_view<Char>()); |
||||
|
handle.format(parse_ctx, context_); |
||||
|
return this->out; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
template <typename Char> |
||||
|
void parse_flags(basic_format_specs<Char>& specs, const Char*& it, |
||||
|
const Char* end) { |
||||
|
for (; it != end; ++it) { |
||||
|
switch (*it) { |
||||
|
case '-': |
||||
|
specs.align = align::left; |
||||
|
break; |
||||
|
case '+': |
||||
|
specs.sign = sign::plus; |
||||
|
break; |
||||
|
case '0': |
||||
|
specs.fill[0] = '0'; |
||||
|
break; |
||||
|
case ' ': |
||||
|
if (specs.sign != sign::plus) { |
||||
|
specs.sign = sign::space; |
||||
|
} |
||||
|
break; |
||||
|
case '#': |
||||
|
specs.alt = true; |
||||
|
break; |
||||
|
default: |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template <typename Char, typename GetArg> |
||||
|
int parse_header(const Char*& it, const Char* end, |
||||
|
basic_format_specs<Char>& specs, GetArg get_arg) { |
||||
|
int arg_index = -1; |
||||
|
Char c = *it; |
||||
|
if (c >= '0' && c <= '9') { |
||||
|
// Parse an argument index (if followed by '$') or a width possibly |
||||
|
// preceded with '0' flag(s). |
||||
|
int value = parse_nonnegative_int(it, end, -1); |
||||
|
if (it != end && *it == '$') { // value is an argument index |
||||
|
++it; |
||||
|
arg_index = value != -1 ? value : max_value<int>(); |
||||
|
} else { |
||||
|
if (c == '0') specs.fill[0] = '0'; |
||||
|
if (value != 0) { |
||||
|
// Nonzero value means that we parsed width and don't need to |
||||
|
// parse it or flags again, so return now. |
||||
|
if (value == -1) FMT_THROW(format_error("number is too big")); |
||||
|
specs.width = value; |
||||
|
return arg_index; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
parse_flags(specs, it, end); |
||||
|
// Parse width. |
||||
|
if (it != end) { |
||||
|
if (*it >= '0' && *it <= '9') { |
||||
|
specs.width = parse_nonnegative_int(it, end, -1); |
||||
|
if (specs.width == -1) FMT_THROW(format_error("number is too big")); |
||||
|
} else if (*it == '*') { |
||||
|
++it; |
||||
|
specs.width = static_cast<int>(visit_format_arg( |
||||
|
detail::printf_width_handler<Char>(specs), get_arg(-1))); |
||||
|
} |
||||
|
} |
||||
|
return arg_index; |
||||
|
} |
||||
|
|
||||
|
template <typename Char, typename Context> |
||||
|
void vprintf(buffer<Char>& buf, basic_string_view<Char> format, |
||||
|
basic_format_args<Context> args) { |
||||
|
using OutputIt = buffer_appender<Char>; |
||||
|
auto out = OutputIt(buf); |
||||
|
auto context = basic_printf_context<OutputIt, Char>(out, args); |
||||
|
auto parse_ctx = basic_printf_parse_context<Char>(format); |
||||
|
|
||||
|
// Returns the argument with specified index or, if arg_index is -1, the next |
||||
|
// argument. |
||||
|
auto get_arg = [&](int arg_index) { |
||||
|
if (arg_index < 0) |
||||
|
arg_index = parse_ctx.next_arg_id(); |
||||
|
else |
||||
|
parse_ctx.check_arg_id(--arg_index); |
||||
|
return detail::get_arg(context, arg_index); |
||||
|
}; |
||||
|
|
||||
|
const Char* start = parse_ctx.begin(); |
||||
|
const Char* end = parse_ctx.end(); |
||||
|
auto it = start; |
||||
|
while (it != end) { |
||||
|
if (!detail::find<false, Char>(it, end, '%', it)) { |
||||
|
it = end; // detail::find leaves it == nullptr if it doesn't find '%' |
||||
|
break; |
||||
|
} |
||||
|
Char c = *it++; |
||||
|
if (it != end && *it == c) { |
||||
|
out = detail::write( |
||||
|
out, basic_string_view<Char>(start, detail::to_unsigned(it - start))); |
||||
|
start = ++it; |
||||
|
continue; |
||||
|
} |
||||
|
out = detail::write(out, basic_string_view<Char>( |
||||
|
start, detail::to_unsigned(it - 1 - start))); |
||||
|
|
||||
|
basic_format_specs<Char> specs; |
||||
|
specs.align = align::right; |
||||
|
|
||||
|
// Parse argument index, flags and width. |
||||
|
int arg_index = parse_header(it, end, specs, get_arg); |
||||
|
if (arg_index == 0) parse_ctx.on_error("argument not found"); |
||||
|
|
||||
|
// Parse precision. |
||||
|
if (it != end && *it == '.') { |
||||
|
++it; |
||||
|
c = it != end ? *it : 0; |
||||
|
if ('0' <= c && c <= '9') { |
||||
|
specs.precision = parse_nonnegative_int(it, end, 0); |
||||
|
} else if (c == '*') { |
||||
|
++it; |
||||
|
specs.precision = static_cast<int>( |
||||
|
visit_format_arg(detail::printf_precision_handler(), get_arg(-1))); |
||||
|
} else { |
||||
|
specs.precision = 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
auto arg = get_arg(arg_index); |
||||
|
// For d, i, o, u, x, and X conversion specifiers, if a precision is |
||||
|
// specified, the '0' flag is ignored |
||||
|
if (specs.precision >= 0 && arg.is_integral()) |
||||
|
specs.fill[0] = |
||||
|
' '; // Ignore '0' flag for non-numeric types or if '-' present. |
||||
|
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) { |
||||
|
auto str = visit_format_arg(detail::get_cstring<Char>(), arg); |
||||
|
auto str_end = str + specs.precision; |
||||
|
auto nul = std::find(str, str_end, Char()); |
||||
|
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>( |
||||
|
basic_string_view<Char>( |
||||
|
str, detail::to_unsigned(nul != str_end ? nul - str |
||||
|
: specs.precision))); |
||||
|
} |
||||
|
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg)) |
||||
|
specs.alt = false; |
||||
|
if (specs.fill[0] == '0') { |
||||
|
if (arg.is_arithmetic() && specs.align != align::left) |
||||
|
specs.align = align::numeric; |
||||
|
else |
||||
|
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-' |
||||
|
// flag is also present. |
||||
|
} |
||||
|
|
||||
|
// Parse length and convert the argument to the required type. |
||||
|
c = it != end ? *it++ : 0; |
||||
|
Char t = it != end ? *it : 0; |
||||
|
using detail::convert_arg; |
||||
|
switch (c) { |
||||
|
case 'h': |
||||
|
if (t == 'h') { |
||||
|
++it; |
||||
|
t = it != end ? *it : 0; |
||||
|
convert_arg<signed char>(arg, t); |
||||
|
} else { |
||||
|
convert_arg<short>(arg, t); |
||||
|
} |
||||
|
break; |
||||
|
case 'l': |
||||
|
if (t == 'l') { |
||||
|
++it; |
||||
|
t = it != end ? *it : 0; |
||||
|
convert_arg<long long>(arg, t); |
||||
|
} else { |
||||
|
convert_arg<long>(arg, t); |
||||
|
} |
||||
|
break; |
||||
|
case 'j': |
||||
|
convert_arg<intmax_t>(arg, t); |
||||
|
break; |
||||
|
case 'z': |
||||
|
convert_arg<size_t>(arg, t); |
||||
|
break; |
||||
|
case 't': |
||||
|
convert_arg<std::ptrdiff_t>(arg, t); |
||||
|
break; |
||||
|
case 'L': |
||||
|
// printf produces garbage when 'L' is omitted for long double, no |
||||
|
// need to do the same. |
||||
|
break; |
||||
|
default: |
||||
|
--it; |
||||
|
convert_arg<void>(arg, c); |
||||
|
} |
||||
|
|
||||
|
// Parse type. |
||||
|
if (it == end) FMT_THROW(format_error("invalid format string")); |
||||
|
char type = static_cast<char>(*it++); |
||||
|
if (arg.is_integral()) { |
||||
|
// Normalize type. |
||||
|
switch (type) { |
||||
|
case 'i': |
||||
|
case 'u': |
||||
|
type = 'd'; |
||||
|
break; |
||||
|
case 'c': |
||||
|
visit_format_arg( |
||||
|
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg), |
||||
|
arg); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
specs.type = parse_presentation_type(type); |
||||
|
if (specs.type == presentation_type::none) |
||||
|
parse_ctx.on_error("invalid type specifier"); |
||||
|
|
||||
|
start = it; |
||||
|
|
||||
|
// Format argument. |
||||
|
out = visit_format_arg( |
||||
|
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg); |
||||
|
} |
||||
|
detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start))); |
||||
|
} |
||||
|
FMT_END_DETAIL_NAMESPACE |
||||
|
|
||||
|
template <typename Char> |
||||
|
using basic_printf_context_t = |
||||
|
basic_printf_context<detail::buffer_appender<Char>, Char>; |
||||
|
|
||||
|
using printf_context = basic_printf_context_t<char>; |
||||
|
using wprintf_context = basic_printf_context_t<wchar_t>; |
||||
|
|
||||
|
using printf_args = basic_format_args<printf_context>; |
||||
|
using wprintf_args = basic_format_args<wprintf_context>; |
||||
|
|
||||
|
/** |
||||
|
\rst |
||||
|
Constructs an `~fmt::format_arg_store` object that contains references to |
||||
|
arguments and can be implicitly converted to `~fmt::printf_args`. |
||||
|
\endrst |
||||
|
*/ |
||||
|
template <typename... T> |
||||
|
inline auto make_printf_args(const T&... args) |
||||
|
-> format_arg_store<printf_context, T...> { |
||||
|
return {args...}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
\rst |
||||
|
Constructs an `~fmt::format_arg_store` object that contains references to |
||||
|
arguments and can be implicitly converted to `~fmt::wprintf_args`. |
||||
|
\endrst |
||||
|
*/ |
||||
|
template <typename... T> |
||||
|
inline auto make_wprintf_args(const T&... args) |
||||
|
-> format_arg_store<wprintf_context, T...> { |
||||
|
return {args...}; |
||||
|
} |
||||
|
|
||||
|
template <typename S, typename Char = char_t<S>> |
||||
|
inline auto vsprintf( |
||||
|
const S& fmt, |
||||
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) |
||||
|
-> std::basic_string<Char> { |
||||
|
basic_memory_buffer<Char> buffer; |
||||
|
vprintf(buffer, detail::to_string_view(fmt), args); |
||||
|
return to_string(buffer); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
\rst |
||||
|
Formats arguments and returns the result as a string. |
||||
|
|
||||
|
**Example**:: |
||||
|
|
||||
|
std::string message = fmt::sprintf("The answer is %d", 42); |
||||
|
\endrst |
||||
|
*/ |
||||
|
template <typename S, typename... T, |
||||
|
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> |
||||
|
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> { |
||||
|
using context = basic_printf_context_t<Char>; |
||||
|
return vsprintf(detail::to_string_view(fmt), |
||||
|
fmt::make_format_args<context>(args...)); |
||||
|
} |
||||
|
|
||||
|
template <typename S, typename Char = char_t<S>> |
||||
|
inline auto vfprintf( |
||||
|
std::FILE* f, const S& fmt, |
||||
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) |
||||
|
-> int { |
||||
|
basic_memory_buffer<Char> buffer; |
||||
|
vprintf(buffer, detail::to_string_view(fmt), args); |
||||
|
size_t size = buffer.size(); |
||||
|
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size |
||||
|
? -1 |
||||
|
: static_cast<int>(size); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
\rst |
||||
|
Prints formatted data to the file *f*. |
||||
|
|
||||
|
**Example**:: |
||||
|
|
||||
|
fmt::fprintf(stderr, "Don't %s!", "panic"); |
||||
|
\endrst |
||||
|
*/ |
||||
|
template <typename S, typename... T, typename Char = char_t<S>> |
||||
|
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { |
||||
|
using context = basic_printf_context_t<Char>; |
||||
|
return vfprintf(f, detail::to_string_view(fmt), |
||||
|
fmt::make_format_args<context>(args...)); |
||||
|
} |
||||
|
|
||||
|
template <typename S, typename Char = char_t<S>> |
||||
|
inline auto vprintf( |
||||
|
const S& fmt, |
||||
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) |
||||
|
-> int { |
||||
|
return vfprintf(stdout, detail::to_string_view(fmt), args); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
\rst |
||||
|
Prints formatted data to ``stdout``. |
||||
|
|
||||
|
**Example**:: |
||||
|
|
||||
|
fmt::printf("Elapsed time: %.2f seconds", 1.23); |
||||
|
\endrst |
||||
|
*/ |
||||
|
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)> |
||||
|
inline auto printf(const S& fmt, const T&... args) -> int { |
||||
|
return vprintf( |
||||
|
detail::to_string_view(fmt), |
||||
|
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...)); |
||||
|
} |
||||
|
|
||||
|
FMT_MODULE_EXPORT_END |
||||
|
FMT_END_NAMESPACE |
||||
|
|
||||
|
#endif // FMT_PRINTF_H_ |
@ -0,0 +1,722 @@ |
|||||
|
// 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); |
||||
|
} |
||||
|
|
||||
|
} // 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_convertible<T, detail::std_string_view<Char>>::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,229 @@ |
|||||
|
// 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; |
||||
|
inline auto runtime(wstring_view s) -> wstring_view { return s; } |
||||
|
#else |
||||
|
template <typename... Args> |
||||
|
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>; |
||||
|
inline auto runtime(wstring_view s) -> basic_runtime<wchar_t> { return {{s}}; } |
||||
|
#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); |
||||
|
} |
||||
|
|
||||
|
template <typename... T> |
||||
|
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring { |
||||
|
return vformat(fmt::wstring_view(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 && |
||||
|
!std::is_same<Char, wchar_t>::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_ |
@ -1,10 +1,10 @@ |
|||||
#pragma once
|
#pragma once
|
||||
|
|
||||
#include "ghc/filesystem.hpp"
|
|
||||
|
#include "fs_path.hpp"
|
||||
|
|
||||
#include <vector>
|
#include <vector>
|
||||
|
|
||||
namespace fs |
namespace fs |
||||
{ |
{ |
||||
typedef std::vector<ghc::filesystem::path> PathVector; |
|
||||
|
typedef std::vector<fs::Path> PathVector; |
||||
} |
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue