Merge pull request #10652 from shuffle2/fmt

update fmt and fix warnings that popped up with vs 17.2
This commit is contained in:
Tilka 2022-05-13 22:09:01 +01:00 committed by GitHub
commit fcb3f9e35b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 2257 additions and 2130 deletions

View File

@ -125,7 +125,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake") "${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
include(cxx14) include(cxx14)
include(CheckCXXCompilerFlag)
include(JoinPaths) include(JoinPaths)
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index) list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index)
@ -209,18 +208,6 @@ if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*") ${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
endif () endif ()
set(strtod_l_headers stdlib.h)
if (APPLE)
set(strtod_l_headers ${strtod_l_headers} xlocale.h)
endif ()
include(CheckSymbolExists)
if (WIN32)
check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
else ()
check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
endif ()
function(add_headers VAR) function(add_headers VAR)
set(headers ${${VAR}}) set(headers ${${VAR}})
foreach (header ${ARGN}) foreach (header ${ARGN})
@ -244,17 +231,6 @@ endif ()
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst) add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt) add_library(fmt::fmt ALIAS fmt)
if (HAVE_STRTOD_L)
target_compile_definitions(fmt PUBLIC FMT_LOCALE)
endif ()
if (MINGW)
check_cxx_compiler_flag("-Wa,-mbig-obj" FMT_HAS_MBIG_OBJ)
if (${FMT_HAS_MBIG_OBJ})
target_compile_options(fmt PUBLIC "-Wa,-mbig-obj")
endif()
endif ()
if (FMT_WERROR) if (FMT_WERROR)
target_compile_options(fmt PRIVATE ${WERROR_FLAG}) target_compile_options(fmt PRIVATE ${WERROR_FLAG})
endif () endif ()
@ -275,6 +251,7 @@ set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
set_target_properties(fmt PROPERTIES set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR} VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
PUBLIC_HEADER "${FMT_HEADERS}"
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}") DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target # Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target
@ -352,6 +329,8 @@ if (FMT_INSTALL)
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
LIBRARY DESTINATION ${FMT_LIB_DIR} LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR} ARCHIVE DESTINATION ${FMT_LIB_DIR}
PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt"
FRAMEWORK DESTINATION "."
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
# Use a namespace because CMake provides better diagnostics for namespaced # Use a namespace because CMake provides better diagnostics for namespaced
@ -368,7 +347,6 @@ if (FMT_INSTALL)
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}> install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
DESTINATION ${FMT_LIB_DIR} OPTIONAL) DESTINATION ${FMT_LIB_DIR} OPTIONAL)
install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt")
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif () endif ()

View File

@ -6,7 +6,7 @@
`#2696 <https://github.com/fmtlib/fmt/pull/2696>`_). `#2696 <https://github.com/fmtlib/fmt/pull/2696>`_).
Thanks `@saraedum (Julian Rüth) <https://github.com/saraedum>`_. Thanks `@saraedum (Julian Rüth) <https://github.com/saraedum>`_.
* Fixed chorno formatting on big endian systems * Fixed chrono formatting on big endian systems
(`#2698 <https://github.com/fmtlib/fmt/issues/2698>`_, (`#2698 <https://github.com/fmtlib/fmt/issues/2698>`_,
`#2699 <https://github.com/fmtlib/fmt/pull/2699>`_). `#2699 <https://github.com/fmtlib/fmt/pull/2699>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_ and Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_ and

View File

@ -1,5 +1,7 @@
{fmt} .. image:: https://user-images.githubusercontent.com/
===== 576385/156254208-f5b743a9-88cf-439d-b0c0-923d53e8d551.png
:width: 25%
:alt: {fmt}
.. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg .. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux
@ -26,9 +28,8 @@
**{fmt}** is an open-source formatting library providing a fast and safe **{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams. alternative to C stdio and C++ iostreams.
If you like this project, please consider donating to the BYSOL If you like this project, please consider donating to one of the funds that
Foundation that helps victims of political repressions in Belarus: help victims of the war in Ukraine: https://www.stopputin.net/.
https://bysol.org/en/bs/general/.
`Documentation <https://fmt.dev>`__ `Documentation <https://fmt.dev>`__
@ -123,7 +124,7 @@ Output::
Default format: 42s 100ms Default format: 42s 100ms
strftime-like format: 03:15:30 strftime-like format: 03:15:30
**Print a container** (`run <https://godbolt.org/z/MjsY7c>`_) **Print a container** (`run <https://godbolt.org/z/MxM1YqjE7>`_)
.. code:: c++ .. code:: c++

View File

@ -95,10 +95,10 @@ class dynamic_format_arg_store
}; };
template <typename T> template <typename T>
using stored_type = conditional_t<detail::is_string<T>::value && using stored_type = conditional_t<
!has_formatter<T, Context>::value && std::is_convertible<T, std::basic_string<char_type>>::value &&
!detail::is_reference_wrapper<T>::value, !detail::is_reference_wrapper<T>::value,
std::basic_string<char_type>, T>; std::basic_string<char_type>, T>;
// Storage of basic_format_arg must be contiguous. // Storage of basic_format_arg must be contiguous.
std::vector<basic_format_arg<Context>> data_; std::vector<basic_format_arg<Context>> data_;

View File

@ -10,6 +10,8 @@
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
#include <cmath> // std::isfinite
#include <cstring> // std::memcpy
#include <ctime> #include <ctime>
#include <iterator> #include <iterator>
#include <locale> #include <locale>
@ -321,14 +323,13 @@ constexpr const size_t codecvt_result<CodeUnit>::max_size;
template <typename CodeUnit> template <typename CodeUnit>
void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf, void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
const std::locale& loc) { const std::locale& loc) {
using codecvt = std::codecvt<CodeUnit, char, std::mbstate_t>;
#if FMT_CLANG_VERSION #if FMT_CLANG_VERSION
# pragma clang diagnostic push # pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated" # pragma clang diagnostic ignored "-Wdeprecated"
auto& f = std::use_facet<codecvt>(loc); auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
# pragma clang diagnostic pop # pragma clang diagnostic pop
#else #else
auto& f = std::use_facet<codecvt>(loc); auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
#endif #endif
auto mb = std::mbstate_t(); auto mb = std::mbstate_t();
const char* from_next = nullptr; const char* from_next = nullptr;
@ -562,10 +563,10 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
constexpr const size_t len = 8; constexpr const size_t len = 8;
if (const_check(is_big_endian())) { if (const_check(is_big_endian())) {
char tmp[len]; char tmp[len];
memcpy(tmp, &digits, len); std::memcpy(tmp, &digits, len);
std::reverse_copy(tmp, tmp + len, buf); std::reverse_copy(tmp, tmp + len, buf);
} else { } else {
memcpy(buf, &digits, len); std::memcpy(buf, &digits, len);
} }
} }
@ -1214,7 +1215,7 @@ template <typename OutputIt, typename Char> class tm_writer {
char buf[10]; char buf[10];
size_t offset = 0; size_t offset = 0;
if (year >= 0 && year < 10000) { if (year >= 0 && year < 10000) {
copy2(buf, digits2(to_unsigned(year / 100))); copy2(buf, digits2(static_cast<size_t>(year / 100)));
} else { } else {
offset = 4; offset = 4;
write_year_extended(year); write_year_extended(year);
@ -1387,15 +1388,6 @@ struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
FMT_CONSTEXPR void on_duration_unit() {} FMT_CONSTEXPR void on_duration_unit() {}
}; };
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline bool isnan(T) {
return false;
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
inline bool isnan(T value) {
return std::isnan(value);
}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline bool isfinite(T) { inline bool isfinite(T) {
return true; return true;
@ -1470,14 +1462,23 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
#endif #endif
} }
// Returns the number of fractional digits in the range [0, 18] according to the // Counts the number of fractional digits in the range [0, 18] according to the
// C++20 spec. If more than 18 fractional digits are required then returns 6 for // C++20 spec. If more than 18 fractional digits are required then returns 6 for
// microseconds precision. // microseconds precision.
constexpr int count_fractional_digits(long long num, long long den, int n = 0) { template <long long Num, long long Den, int N = 0,
return num % den == 0 bool Enabled =
? n (N < 19) && (Num <= std::numeric_limits<long long>::max() / 10)>
: (n > 18 ? 6 : count_fractional_digits(num * 10, den, n + 1)); struct count_fractional_digits {
} static constexpr int value =
Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
};
// Base case that doesn't instantiate any more templates
// in order to avoid overflow.
template <long long Num, long long Den, int N>
struct count_fractional_digits<Num, Den, N, false> {
static constexpr int value = (Num % Den == 0) ? N : 6;
};
constexpr long long pow10(std::uint32_t n) { constexpr long long pow10(std::uint32_t n) {
return n == 0 ? 1 : 10 * pow10(n - 1); return n == 0 ? 1 : 10 * pow10(n - 1);
@ -1663,9 +1664,11 @@ struct chrono_formatter {
out = format_decimal<char_type>(out, n, num_digits).end; out = format_decimal<char_type>(out, n, num_digits).end;
} }
template <class Duration> void write_fractional_seconds(Duration d) { template <typename Duration> void write_fractional_seconds(Duration d) {
FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
constexpr auto num_fractional_digits = constexpr auto num_fractional_digits =
count_fractional_digits(Duration::period::num, Duration::period::den); count_fractional_digits<Duration::period::num,
Duration::period::den>::value;
using subsecond_precision = std::chrono::duration< using subsecond_precision = std::chrono::duration<
typename std::common_type<typename Duration::rep, typename std::common_type<typename Duration::rep,
@ -1674,12 +1677,9 @@ struct chrono_formatter {
if (std::ratio_less<typename subsecond_precision::period, if (std::ratio_less<typename subsecond_precision::period,
std::chrono::seconds::period>::value) { std::chrono::seconds::period>::value) {
*out++ = '.'; *out++ = '.';
// Don't convert long double to integer seconds to avoid overflow. auto fractional =
using sec = conditional_t< detail::abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d);
std::is_same<typename Duration::rep, long double>::value, auto subseconds =
std::chrono::duration<long double>, std::chrono::seconds>;
auto fractional = detail::abs(d) - std::chrono::duration_cast<sec>(d);
const auto subseconds =
std::chrono::treat_as_floating_point< std::chrono::treat_as_floating_point<
typename subsecond_precision::rep>::value typename subsecond_precision::rep>::value
? fractional.count() ? fractional.count()
@ -1770,8 +1770,22 @@ struct chrono_formatter {
if (handle_nan_inf()) return; if (handle_nan_inf()) return;
if (ns == numeric_system::standard) { if (ns == numeric_system::standard) {
write(second(), 2); if (std::is_floating_point<rep>::value) {
write_fractional_seconds(std::chrono::duration<rep, Period>{val}); constexpr auto num_fractional_digits =
count_fractional_digits<Period::num, Period::den>::value;
auto buf = memory_buffer();
format_to(std::back_inserter(buf), runtime("{:.{}f}"),
std::fmod(val * static_cast<rep>(Period::num) /
static_cast<rep>(Period::den),
60),
num_fractional_digits);
if (negative) *out++ = '-';
if (buf.size() < 2 || buf[1] == '.') *out++ = '0';
out = std::copy(buf.begin(), buf.end(), out);
} else {
write(second(), 2);
write_fractional_seconds(std::chrono::duration<rep, Period>(val));
}
return; return;
} }
auto time = tm(); auto time = tm();

View File

@ -214,17 +214,16 @@ FMT_BEGIN_DETAIL_NAMESPACE
// color is a struct of either a rgb color or a terminal color. // color is a struct of either a rgb color or a terminal color.
struct color_type { struct color_type {
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} {
value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color); value.rgb_color = static_cast<uint32_t>(rgb_color);
} }
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} { FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} {
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) | value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b; (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
} }
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), FMT_CONSTEXPR color_type(terminal_color term_color) noexcept
value{} { : is_rgb(), value{} {
value.term_color = static_cast<uint8_t>(term_color); value.term_color = static_cast<uint8_t>(term_color);
} }
bool is_rgb; bool is_rgb;
@ -239,10 +238,8 @@ FMT_END_DETAIL_NAMESPACE
/** A text style consisting of foreground and background colors and emphasis. */ /** A text style consisting of foreground and background colors and emphasis. */
class text_style { class text_style {
public: public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
: set_foreground_color(), : set_foreground_color(), set_background_color(), ems(em) {}
set_background_color(),
ems(em) {}
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
if (!set_foreground_color) { if (!set_foreground_color) {
@ -283,34 +280,32 @@ class text_style {
return lhs.and_assign(rhs); return lhs.and_assign(rhs);
} }
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { FMT_CONSTEXPR bool has_foreground() const noexcept {
return set_foreground_color; return set_foreground_color;
} }
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { FMT_CONSTEXPR bool has_background() const noexcept {
return set_background_color; return set_background_color;
} }
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { FMT_CONSTEXPR bool has_emphasis() const noexcept {
return static_cast<uint8_t>(ems) != 0; return static_cast<uint8_t>(ems) != 0;
} }
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT { FMT_CONSTEXPR detail::color_type get_foreground() const noexcept {
FMT_ASSERT(has_foreground(), "no foreground specified for this style"); FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color; return foreground_color;
} }
FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT { FMT_CONSTEXPR detail::color_type get_background() const noexcept {
FMT_ASSERT(has_background(), "no background specified for this style"); FMT_ASSERT(has_background(), "no background specified for this style");
return background_color; return background_color;
} }
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { FMT_CONSTEXPR emphasis get_emphasis() const noexcept {
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems; return ems;
} }
private: private:
FMT_CONSTEXPR text_style(bool is_foreground, FMT_CONSTEXPR text_style(bool is_foreground,
detail::color_type text_color) FMT_NOEXCEPT detail::color_type text_color) noexcept
: set_foreground_color(), : set_foreground_color(), set_background_color(), ems() {
set_background_color(),
ems() {
if (is_foreground) { if (is_foreground) {
foreground_color = text_color; foreground_color = text_color;
set_foreground_color = true; set_foreground_color = true;
@ -345,11 +340,11 @@ class text_style {
return *this; return *this;
} }
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground) friend FMT_CONSTEXPR_DECL text_style
FMT_NOEXCEPT; fg(detail::color_type foreground) noexcept;
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background) friend FMT_CONSTEXPR_DECL text_style
FMT_NOEXCEPT; bg(detail::color_type background) noexcept;
detail::color_type foreground_color; detail::color_type foreground_color;
detail::color_type background_color; detail::color_type background_color;
@ -359,17 +354,16 @@ class text_style {
}; };
/** Creates a text style from the foreground (text) color. */ /** Creates a text style from the foreground (text) color. */
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) FMT_NOEXCEPT { FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept {
return text_style(true, foreground); return text_style(true, foreground);
} }
/** Creates a text style from the background color. */ /** Creates a text style from the background color. */
FMT_CONSTEXPR inline text_style bg(detail::color_type background) FMT_NOEXCEPT { FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept {
return text_style(false, background); return text_style(false, background);
} }
FMT_CONSTEXPR inline text_style operator|(emphasis lhs, FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs; return text_style(lhs) | rhs;
} }
@ -377,7 +371,7 @@ FMT_BEGIN_DETAIL_NAMESPACE
template <typename Char> struct ansi_color_escape { template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
const char* esc) FMT_NOEXCEPT { const char* esc) noexcept {
// If we have a terminal color, we need to output another escape code // If we have a terminal color, we need to output another escape code
// sequence. // sequence.
if (!text_color.is_rgb) { if (!text_color.is_rgb) {
@ -412,7 +406,7 @@ template <typename Char> struct ansi_color_escape {
to_esc(color.b, buffer + 15, 'm'); to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0); buffer[19] = static_cast<Char>(0);
} }
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
uint8_t em_codes[num_emphases] = {}; uint8_t em_codes[num_emphases] = {};
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; 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::faint)) em_codes[1] = 2;
@ -433,10 +427,10 @@ template <typename Char> struct ansi_color_escape {
} }
buffer[index++] = static_cast<Char>(0); buffer[index++] = static_cast<Char>(0);
} }
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; }
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT { FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept {
return buffer + std::char_traits<Char>::length(buffer); return buffer + std::char_traits<Char>::length(buffer);
} }
@ -445,59 +439,64 @@ template <typename Char> struct ansi_color_escape {
Char buffer[7u + 3u * num_emphases + 1u]; Char buffer[7u + 3u * num_emphases + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) FMT_NOEXCEPT { char delimiter) noexcept {
out[0] = static_cast<Char>('0' + c / 100); out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10); out[1] = static_cast<Char>('0' + c / 10 % 10);
out[2] = static_cast<Char>('0' + c % 10); out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter); out[3] = static_cast<Char>(delimiter);
} }
static FMT_CONSTEXPR bool has_emphasis(emphasis em, static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept {
emphasis mask) FMT_NOEXCEPT {
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask); return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
} }
}; };
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color( FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
detail::color_type foreground) FMT_NOEXCEPT { detail::color_type foreground) noexcept {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;"); return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color( FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
detail::color_type background) FMT_NOEXCEPT { detail::color_type background) noexcept {
return ansi_color_escape<Char>(background, "\x1b[48;2;"); return ansi_color_escape<Char>(background, "\x1b[48;2;");
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT { FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
return ansi_color_escape<Char>(em); return ansi_color_escape<Char>(em);
} }
template <typename Char> template <typename Char> inline void fputs(const Char* chars, FILE* stream) {
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { int result = std::fputs(chars, stream);
std::fputs(chars, stream); if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
} }
template <> template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) {
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { int result = std::fputws(chars, stream);
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) FMT_NOEXCEPT { template <typename Char> inline void reset_color(FILE* stream) {
fputs("\x1b[0m", stream); fputs("\x1b[0m", stream);
} }
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT { template <> inline void reset_color<wchar_t>(FILE* stream) {
fputs(L"\x1b[0m", stream); fputs(L"\x1b[0m", stream);
} }
template <typename Char> template <typename Char> inline void reset_color(buffer<Char>& buffer) {
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
auto reset_color = string_view("\x1b[0m"); auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end()); buffer.append(reset_color.begin(), reset_color.end());
} }
template <typename T> struct styled_arg {
const T& value;
text_style style;
};
template <typename Char> template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts, void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str, basic_string_view<Char> format_str,
@ -529,8 +528,12 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format), args); detail::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0)); if (detail::is_utf8()) {
detail::fputs(buf.data(), f); detail::print(f, basic_string_view<Char>(buf.begin(), buf.size()));
} else {
buf.push_back(Char(0));
detail::fputs(buf.data(), f);
}
} }
/** /**
@ -549,7 +552,7 @@ template <typename S, typename... Args,
void print(std::FILE* f, const text_style& ts, const S& format_str, void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) { const Args&... args) {
vprint(f, ts, format_str, vprint(f, ts, format_str,
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_format_args<buffer_context<char_t<S>>>(args...));
} }
/** /**
@ -594,7 +597,7 @@ template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str, inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) { const Args&... args) {
return fmt::vformat(ts, to_string_view(format_str), return fmt::vformat(ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_format_args<buffer_context<Char>>(args...));
} }
/** /**
@ -629,7 +632,60 @@ inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) -> Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type { typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, to_string_view(format_str), return vformat_to(out, ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...)); 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: {s:.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_MODULE_EXPORT_END

View File

@ -13,45 +13,6 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
// An output iterator that counts the number of objects written to it and
// discards them.
class counting_iterator {
private:
size_t count_;
public:
using iterator_category = std::output_iterator_tag;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
using _Unchecked_type = counting_iterator; // Mark iterator as checked.
struct value_type {
template <typename T> void operator=(const T&) {}
};
counting_iterator() : count_(0) {}
size_t count() const { return count_; }
counting_iterator& operator++() {
++count_;
return *this;
}
counting_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
friend counting_iterator operator+(counting_iterator it, difference_type n) {
it.count_ += static_cast<size_t>(n);
return it;
}
value_type operator*() const { return {}; }
};
template <typename Char, typename InputIt> template <typename Char, typename InputIt>
inline counting_iterator copy_str(InputIt begin, InputIt end, inline counting_iterator copy_str(InputIt begin, InputIt end,
counting_iterator it) { counting_iterator it) {
@ -75,8 +36,7 @@ template <typename OutputIt> class truncating_iterator_base {
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using pointer = void; using pointer = void;
using reference = void; using reference = void;
using _Unchecked_type = FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
truncating_iterator_base; // Mark iterator as checked.
OutputIt base() const { return out_; } OutputIt base() const { return out_; }
size_t count() const { return count_; } size_t count() const { return count_; }
@ -168,7 +128,7 @@ template <typename Char, size_t N,
fmt::detail_exported::fixed_string<Char, N> Str> fmt::detail_exported::fixed_string<Char, N> Str>
struct udl_compiled_string : compiled_string { struct udl_compiled_string : compiled_string {
using char_type = Char; using char_type = Char;
constexpr operator basic_string_view<char_type>() const { explicit constexpr operator basic_string_view<char_type>() const {
return {Str.data, N - 1}; return {Str.data, N - 1};
} }
}; };
@ -573,10 +533,11 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
constexpr auto compiled = detail::compile<Args...>(S()); constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) { detail::unknown_format>()) {
return format(static_cast<basic_string_view<typename S::char_type>>(S()), return fmt::format(
std::forward<Args>(args)...); static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else { } else {
return format(compiled, std::forward<Args>(args)...); return fmt::format(compiled, std::forward<Args>(args)...);
} }
} }
@ -586,11 +547,11 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S()); constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) { detail::unknown_format>()) {
return format_to(out, return fmt::format_to(
static_cast<basic_string_view<typename S::char_type>>(S()), out, static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...); std::forward<Args>(args)...);
} else { } else {
return format_to(out, compiled, std::forward<Args>(args)...); return fmt::format_to(out, compiled, std::forward<Args>(args)...);
} }
} }
#endif #endif
@ -599,22 +560,23 @@ template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const S& format_str, Args&&... args) { const S& format_str, Args&&... args) {
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str, auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
std::forward<Args>(args)...); format_str, std::forward<Args>(args)...);
return {it.base(), it.count()}; return {it.base(), it.count()};
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
size_t formatted_size(const S& format_str, const Args&... args) { size_t formatted_size(const S& format_str, const Args&... args) {
return format_to(detail::counting_iterator(), format_str, args...).count(); return fmt::format_to(detail::counting_iterator(), format_str, args...)
.count();
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(std::FILE* f, const S& format_str, const Args&... args) { void print(std::FILE* f, const S& format_str, const Args&... args) {
memory_buffer buffer; memory_buffer buffer;
format_to(std::back_inserter(buffer), format_str, args...); fmt::format_to(std::back_inserter(buffer), format_str, args...);
detail::print(f, {buffer.data(), buffer.size()}); detail::print(f, {buffer.data(), buffer.size()});
} }
@ -626,12 +588,10 @@ void print(const S& format_str, const Args&... args) {
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
inline namespace literals { inline namespace literals {
template <detail_exported::fixed_string Str> template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
constexpr detail::udl_compiled_string< using char_t = remove_cvref_t<decltype(Str.data[0])>;
remove_cvref_t<decltype(Str.data[0])>, return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> Str>();
operator""_cf() {
return {};
} }
} // namespace literals } // namespace literals
#endif #endif

View File

@ -10,14 +10,14 @@
#include <cstddef> // std::byte #include <cstddef> // std::byte
#include <cstdio> // std::FILE #include <cstdio> // std::FILE
#include <cstring> #include <cstring> // std::strlen
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch. // The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 80101 #define FMT_VERSION 80102
#if defined(__clang__) && !defined(__ibmxl__) #if defined(__clang__) && !defined(__ibmxl__)
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
@ -49,6 +49,13 @@
# define FMT_ICC_VERSION 0 # define FMT_ICC_VERSION 0
#endif #endif
#ifdef __NVCOMPILER
# define FMT_NVCOMPILER_VERSION \
(__NVCOMPILER_MAJOR__ * 100 + __NVCOMPILER_MINOR__)
#else
# define FMT_NVCOMPILER_VERSION 0
#endif
#ifdef __NVCC__ #ifdef __NVCC__
# define FMT_NVCC __NVCC__ # define FMT_NVCC __NVCC__
#else #else
@ -145,28 +152,6 @@
# endif # endif
#endif #endif
// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature).
#ifndef FMT_USE_NOEXCEPT
# define FMT_USE_NOEXCEPT 0
#endif
#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
FMT_GCC_VERSION >= 408 || FMT_MSC_VER >= 1900
# define FMT_DETECTED_NOEXCEPT noexcept
# define FMT_HAS_CXX11_NOEXCEPT 1
#else
# define FMT_DETECTED_NOEXCEPT throw()
# define FMT_HAS_CXX11_NOEXCEPT 0
#endif
#ifndef FMT_NOEXCEPT
# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT
# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT
# else
# define FMT_NOEXCEPT
# endif
#endif
// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code // [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code
// warnings. // warnings.
#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \ #if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \
@ -233,6 +218,13 @@
# endif # endif
#endif #endif
#ifdef _MSC_VER
# define FMT_UNCHECKED_ITERATOR(It) \
using _Unchecked_type = It // Mark iterator as checked.
#else
# define FMT_UNCHECKED_ITERATOR(It) using DummyTypeName = It
#endif
#ifndef FMT_BEGIN_NAMESPACE #ifndef FMT_BEGIN_NAMESPACE
# define FMT_BEGIN_NAMESPACE \ # define FMT_BEGIN_NAMESPACE \
namespace fmt { \ namespace fmt { \
@ -309,7 +301,7 @@
// Enable minimal optimizations for more compact code in debug mode. // Enable minimal optimizations for more compact code in debug mode.
FMT_GCC_PRAGMA("GCC push_options") FMT_GCC_PRAGMA("GCC push_options")
#ifndef __OPTIMIZE__ #if !defined(__OPTIMIZE__) && !FMT_NVCOMPILER_VERSION
FMT_GCC_PRAGMA("GCC optimize(\"Og\")") FMT_GCC_PRAGMA("GCC optimize(\"Og\")")
#endif #endif
@ -330,6 +322,8 @@ template <typename T>
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type; using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
template <typename T> struct type_identity { using type = T; }; template <typename T> struct type_identity { using type = T; };
template <typename T> using type_identity_t = typename type_identity<T>::type; template <typename T> using type_identity_t = typename type_identity<T>::type;
template <typename T>
using underlying_t = typename std::underlying_type<T>::type;
struct monostate { struct monostate {
constexpr monostate() {} constexpr monostate() {}
@ -351,8 +345,8 @@ FMT_BEGIN_DETAIL_NAMESPACE
// (void)var does not work on many Intel compilers. // (void)var does not work on many Intel compilers.
template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {} template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false) constexpr FMT_INLINE auto is_constant_evaluated(
FMT_NOEXCEPT -> bool { bool default_value = false) noexcept -> bool {
#ifdef __cpp_lib_is_constant_evaluated #ifdef __cpp_lib_is_constant_evaluated
ignore_unused(default_value); ignore_unused(default_value);
return std::is_constant_evaluated(); return std::is_constant_evaluated();
@ -382,12 +376,6 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
# endif # endif
#endif #endif
#ifdef __cpp_lib_byte
using byte = std::byte;
#else
enum class byte : unsigned char {};
#endif
#if defined(FMT_USE_STRING_VIEW) #if defined(FMT_USE_STRING_VIEW)
template <typename Char> using std_string_view = std::basic_string_view<Char>; template <typename Char> using std_string_view = std::basic_string_view<Char>;
#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) #elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
@ -402,8 +390,8 @@ template <typename T> struct std_string_view {};
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \ #elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \
!(FMT_CLANG_VERSION && FMT_MSC_VER) !(FMT_CLANG_VERSION && FMT_MSC_VER)
# define FMT_USE_INT128 1 # define FMT_USE_INT128 1
using int128_t = __int128_t; using int128_opt = __int128_t; // An optional native 128-bit integer.
using uint128_t = __uint128_t; using uint128_opt = __uint128_t;
template <typename T> inline auto convert_for_visit(T value) -> T { template <typename T> inline auto convert_for_visit(T value) -> T {
return value; return value;
} }
@ -411,12 +399,10 @@ template <typename T> inline auto convert_for_visit(T value) -> T {
# define FMT_USE_INT128 0 # define FMT_USE_INT128 0
#endif #endif
#if !FMT_USE_INT128 #if !FMT_USE_INT128
enum class int128_t {}; enum class int128_opt {};
enum class uint128_t {}; enum class uint128_opt {};
// Reduce template instantiations. // Reduce template instantiations.
template <typename T> inline auto convert_for_visit(T) -> monostate { template <typename T> auto convert_for_visit(T) -> monostate { return {}; }
return {};
}
#endif #endif
// Casts a nonnegative integer to unsigned. // Casts a nonnegative integer to unsigned.
@ -454,12 +440,11 @@ template <typename Char> class basic_string_view {
using value_type = Char; using value_type = Char;
using iterator = const Char*; using iterator = const Char*;
constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {} constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
/** Constructs a string reference object from a C string and a size. */ /** Constructs a string reference object from a C string and a size. */
constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT constexpr basic_string_view(const Char* s, size_t count) noexcept
: data_(s), : data_(s), size_(count) {}
size_(count) {}
/** /**
\rst \rst
@ -479,29 +464,28 @@ template <typename Char> class basic_string_view {
/** Constructs a string reference from a ``std::basic_string`` object. */ /** Constructs a string reference from a ``std::basic_string`` object. */
template <typename Traits, typename Alloc> template <typename Traits, typename Alloc>
FMT_CONSTEXPR basic_string_view( FMT_CONSTEXPR basic_string_view(
const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT const std::basic_string<Char, Traits, Alloc>& s) noexcept
: data_(s.data()), : data_(s.data()), size_(s.size()) {}
size_(s.size()) {}
template <typename S, FMT_ENABLE_IF(std::is_same< template <typename S, FMT_ENABLE_IF(std::is_same<
S, detail::std_string_view<Char>>::value)> S, detail::std_string_view<Char>>::value)>
FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()), FMT_CONSTEXPR basic_string_view(S s) noexcept
size_(s.size()) {} : data_(s.data()), size_(s.size()) {}
/** Returns a pointer to the string data. */ /** Returns a pointer to the string data. */
constexpr auto data() const FMT_NOEXCEPT -> const Char* { return data_; } constexpr auto data() const noexcept -> const Char* { return data_; }
/** Returns the string size. */ /** Returns the string size. */
constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; } constexpr auto size() const noexcept -> size_t { return size_; }
constexpr auto begin() const FMT_NOEXCEPT -> iterator { return data_; } constexpr auto begin() const noexcept -> iterator { return data_; }
constexpr auto end() const FMT_NOEXCEPT -> iterator { return data_ + size_; } constexpr auto end() const noexcept -> iterator { return data_ + size_; }
constexpr auto operator[](size_t pos) const FMT_NOEXCEPT -> const Char& { constexpr auto operator[](size_t pos) const noexcept -> const Char& {
return data_[pos]; return data_[pos];
} }
FMT_CONSTEXPR void remove_prefix(size_t n) FMT_NOEXCEPT { FMT_CONSTEXPR void remove_prefix(size_t n) noexcept {
data_ += n; data_ += n;
size_ -= n; size_ -= n;
} }
@ -648,16 +632,14 @@ class basic_format_parse_context : private ErrorHandler {
Returns an iterator to the beginning of the format string range being Returns an iterator to the beginning of the format string range being
parsed. parsed.
*/ */
constexpr auto begin() const FMT_NOEXCEPT -> iterator { constexpr auto begin() const noexcept -> iterator {
return format_str_.begin(); return format_str_.begin();
} }
/** /**
Returns an iterator past the end of the format string range being parsed. Returns an iterator past the end of the format string range being parsed.
*/ */
constexpr auto end() const FMT_NOEXCEPT -> iterator { constexpr auto end() const noexcept -> iterator { return format_str_.end(); }
return format_str_.end();
}
/** Advances the begin iterator to ``it``. */ /** Advances the begin iterator to ``it``. */
FMT_CONSTEXPR void advance_to(iterator it) { FMT_CONSTEXPR void advance_to(iterator it) {
@ -784,18 +766,16 @@ template <typename T> class buffer {
protected: protected:
// Don't initialize ptr_ since it is not accessed to save a few cycles. // Don't initialize ptr_ since it is not accessed to save a few cycles.
FMT_MSC_WARNING(suppress : 26495) FMT_MSC_WARNING(suppress : 26495)
buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {} buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {}
FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept
size_t cap = 0) FMT_NOEXCEPT : ptr_(p), : ptr_(p), size_(sz), capacity_(cap) {}
size_(sz),
capacity_(cap) {}
FMT_CONSTEXPR20 ~buffer() = default; FMT_CONSTEXPR20 ~buffer() = default;
buffer(buffer&&) = default; buffer(buffer&&) = default;
/** Sets the buffer data and capacity. */ /** Sets the buffer data and capacity. */
FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT { FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept {
ptr_ = buf_data; ptr_ = buf_data;
capacity_ = buf_capacity; capacity_ = buf_capacity;
} }
@ -810,23 +790,23 @@ template <typename T> class buffer {
buffer(const buffer&) = delete; buffer(const buffer&) = delete;
void operator=(const buffer&) = delete; void operator=(const buffer&) = delete;
auto begin() FMT_NOEXCEPT -> T* { return ptr_; } auto begin() noexcept -> T* { return ptr_; }
auto end() FMT_NOEXCEPT -> T* { return ptr_ + size_; } auto end() noexcept -> T* { return ptr_ + size_; }
auto begin() const FMT_NOEXCEPT -> const T* { return ptr_; } auto begin() const noexcept -> const T* { return ptr_; }
auto end() const FMT_NOEXCEPT -> const T* { return ptr_ + size_; } auto end() const noexcept -> const T* { return ptr_ + size_; }
/** Returns the size of this buffer. */ /** Returns the size of this buffer. */
constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; } constexpr auto size() const noexcept -> size_t { return size_; }
/** Returns the capacity of this buffer. */ /** Returns the capacity of this buffer. */
constexpr auto capacity() const FMT_NOEXCEPT -> size_t { return capacity_; } constexpr auto capacity() const noexcept -> size_t { return capacity_; }
/** Returns a pointer to the buffer data. */ /** Returns a pointer to the buffer data. */
FMT_CONSTEXPR auto data() FMT_NOEXCEPT -> T* { return ptr_; } FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
/** Returns a pointer to the buffer data. */ /** Returns a pointer to the buffer data. */
FMT_CONSTEXPR auto data() const FMT_NOEXCEPT -> const T* { return ptr_; } FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; }
/** Clears this buffer. */ /** Clears this buffer. */
void clear() { size_ = 0; } void clear() { size_ = 0; }
@ -1044,7 +1024,11 @@ struct fallback_formatter {
// Specifies if T has an enabled fallback_formatter specialization. // Specifies if T has an enabled fallback_formatter specialization.
template <typename T, typename Char> template <typename T, typename Char>
using has_fallback_formatter = using has_fallback_formatter =
#ifdef FMT_DEPRECATED_OSTREAM
std::is_constructible<fallback_formatter<T, Char>>; std::is_constructible<fallback_formatter<T, Char>>;
#else
std::false_type;
#endif
struct view {}; struct view {};
@ -1164,8 +1148,8 @@ FMT_TYPE_CONSTANT(int, int_type);
FMT_TYPE_CONSTANT(unsigned, uint_type); FMT_TYPE_CONSTANT(unsigned, uint_type);
FMT_TYPE_CONSTANT(long long, long_long_type); FMT_TYPE_CONSTANT(long long, long_long_type);
FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
FMT_TYPE_CONSTANT(int128_t, int128_type); FMT_TYPE_CONSTANT(int128_opt, int128_type);
FMT_TYPE_CONSTANT(uint128_t, uint128_type); FMT_TYPE_CONSTANT(uint128_opt, uint128_type);
FMT_TYPE_CONSTANT(bool, bool_type); FMT_TYPE_CONSTANT(bool, bool_type);
FMT_TYPE_CONSTANT(Char, char_type); FMT_TYPE_CONSTANT(Char, char_type);
FMT_TYPE_CONSTANT(float, float_type); FMT_TYPE_CONSTANT(float, float_type);
@ -1215,8 +1199,8 @@ template <typename Context> class value {
unsigned uint_value; unsigned uint_value;
long long long_long_value; long long long_long_value;
unsigned long long ulong_long_value; unsigned long long ulong_long_value;
int128_t int128_value; int128_opt int128_value;
uint128_t uint128_value; uint128_opt uint128_value;
bool bool_value; bool bool_value;
char_type char_value; char_type char_value;
float float_value; float float_value;
@ -1233,8 +1217,8 @@ template <typename Context> class value {
constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} constexpr FMT_INLINE value(unsigned val) : uint_value(val) {}
constexpr FMT_INLINE value(long long val) : long_long_value(val) {} constexpr FMT_INLINE value(long long val) : long_long_value(val) {}
constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
FMT_INLINE value(int128_t val) : int128_value(val) {} FMT_INLINE value(int128_opt val) : int128_value(val) {}
FMT_INLINE value(uint128_t val) : uint128_value(val) {} FMT_INLINE value(uint128_opt val) : uint128_value(val) {}
constexpr FMT_INLINE value(float val) : float_value(val) {} constexpr FMT_INLINE value(float val) : float_value(val) {}
constexpr FMT_INLINE value(double val) : double_value(val) {} constexpr FMT_INLINE value(double val) : double_value(val) {}
FMT_INLINE value(long double val) : long_double_value(val) {} FMT_INLINE value(long double val) : long_double_value(val) {}
@ -1284,7 +1268,7 @@ template <typename Context> class value {
}; };
template <typename Context, typename T> template <typename Context, typename T>
FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context>; FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context>;
// To minimize the number of types we need to deal with, long is translated // To minimize the number of types we need to deal with, long is translated
// either to int or to long long depending on its size. // either to int or to long long depending on its size.
@ -1292,6 +1276,21 @@ enum { long_short = sizeof(long) == sizeof(int) };
using long_type = conditional_t<long_short, int, long long>; using long_type = conditional_t<long_short, int, long long>;
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>; using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
#ifdef __cpp_lib_byte
inline auto format_as(std::byte b) -> unsigned char {
return static_cast<unsigned char>(b);
}
#endif
template <typename T> struct has_format_as {
template <typename U, typename V = decltype(format_as(U())),
FMT_ENABLE_IF(std::is_enum<U>::value&& std::is_integral<V>::value)>
static auto check(U*) -> std::true_type;
static auto check(...) -> std::false_type;
enum { value = decltype(check(static_cast<T*>(nullptr)))::value };
};
// Maps formatting arguments to core types. // Maps formatting arguments to core types.
// arg_mapper reports errors by returning unformattable instead of using // arg_mapper reports errors by returning unformattable instead of using
// static_assert because it's used in the is_formattable trait. // static_assert because it's used in the is_formattable trait.
@ -1317,8 +1316,12 @@ template <typename Context> struct arg_mapper {
-> unsigned long long { -> unsigned long long {
return val; return val;
} }
FMT_CONSTEXPR FMT_INLINE auto map(int128_t val) -> int128_t { return val; } FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt {
FMT_CONSTEXPR FMT_INLINE auto map(uint128_t val) -> uint128_t { return val; } return val;
}
FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt {
return val;
}
FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; } FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; }
template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value || template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value ||
@ -1365,20 +1368,19 @@ template <typename Context> struct arg_mapper {
} }
template <typename T, template <typename T,
FMT_ENABLE_IF( FMT_ENABLE_IF(
std::is_constructible<basic_string_view<char_type>, T>::value && std::is_convertible<T, basic_string_view<char_type>>::value &&
!is_string<T>::value && !has_formatter<T, Context>::value && !is_string<T>::value && !has_formatter<T, Context>::value &&
!has_fallback_formatter<T, char_type>::value)> !has_fallback_formatter<T, char_type>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& val) FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-> basic_string_view<char_type> { -> basic_string_view<char_type> {
return basic_string_view<char_type>(val); return basic_string_view<char_type>(val);
} }
template < template <typename T,
typename T, FMT_ENABLE_IF(
FMT_ENABLE_IF( std::is_convertible<T, std_string_view<char_type>>::value &&
std::is_constructible<std_string_view<char_type>, T>::value && !std::is_convertible<T, basic_string_view<char_type>>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value && !is_string<T>::value && !has_formatter<T, Context>::value &&
!is_string<T>::value && !has_formatter<T, Context>::value && !has_fallback_formatter<T, char_type>::value)>
!has_fallback_formatter<T, char_type>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& val) FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-> basic_string_view<char_type> { -> basic_string_view<char_type> {
return std_string_view<char_type>(val); return std_string_view<char_type>(val);
@ -1417,10 +1419,11 @@ template <typename Context> struct arg_mapper {
template < template <
typename T, typename T,
FMT_ENABLE_IF( FMT_ENABLE_IF(
std::is_member_pointer<T>::value || std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
std::is_function<typename std::remove_pointer<T>::type>::value || std::is_function<typename std::remove_pointer<T>::type>::value ||
(std::is_convertible<const T&, const void*>::value && (std::is_convertible<const T&, const void*>::value &&
!std::is_convertible<const T&, const char_type*>::value))> !std::is_convertible<const T&, const char_type*>::value &&
!has_formatter<T, Context>::value))>
FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer {
return {}; return {};
} }
@ -1438,12 +1441,15 @@ template <typename Context> struct arg_mapper {
!has_fallback_formatter<T, char_type>::value)> !has_fallback_formatter<T, char_type>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& val) FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-> decltype(std::declval<arg_mapper>().map( -> decltype(std::declval<arg_mapper>().map(
static_cast<typename std::underlying_type<T>::type>(val))) { static_cast<underlying_t<T>>(val))) {
return map(static_cast<typename std::underlying_type<T>::type>(val)); return map(static_cast<underlying_t<T>>(val));
} }
FMT_CONSTEXPR FMT_INLINE auto map(detail::byte val) -> unsigned { template <typename T, FMT_ENABLE_IF(has_format_as<T>::value &&
return map(static_cast<unsigned char>(val)); !has_formatter<T, Context>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-> decltype(std::declval<arg_mapper>().map(format_as(T()))) {
return map(format_as(val));
} }
template <typename T, typename U = remove_cvref_t<T>> template <typename T, typename U = remove_cvref_t<T>>
@ -1452,8 +1458,9 @@ template <typename Context> struct arg_mapper {
!std::is_const<remove_reference_t<T>>::value || !std::is_const<remove_reference_t<T>>::value ||
has_fallback_formatter<U, char_type>::value> {}; has_fallback_formatter<U, char_type>::value> {};
#if FMT_MSC_VER != 0 && FMT_MSC_VER < 1910 #if (FMT_MSC_VER != 0 && FMT_MSC_VER < 1910) || FMT_ICC_VERSION != 0 || \
// Workaround a bug in MSVC. FMT_NVCC != 0
// Workaround a bug in MSVC and Intel (Issue 2746).
template <typename T> FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { template <typename T> FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
return val; return val;
} }
@ -1471,6 +1478,8 @@ template <typename Context> struct arg_mapper {
template <typename T, typename U = remove_cvref_t<T>, template <typename T, typename U = remove_cvref_t<T>,
FMT_ENABLE_IF(!is_string<U>::value && !is_char<U>::value && FMT_ENABLE_IF(!is_string<U>::value && !is_char<U>::value &&
!std::is_array<U>::value && !std::is_array<U>::value &&
!std::is_pointer<U>::value &&
!has_format_as<U>::value &&
(has_formatter<U, Context>::value || (has_formatter<U, Context>::value ||
has_fallback_formatter<U, char_type>::value))> has_fallback_formatter<U, char_type>::value))>
FMT_CONSTEXPR FMT_INLINE auto map(T&& val) FMT_CONSTEXPR FMT_INLINE auto map(T&& val)
@ -1513,12 +1522,12 @@ class appender : public std::back_insert_iterator<detail::buffer<char>> {
public: public:
using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator; using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
appender(base it) FMT_NOEXCEPT : base(it) {} appender(base it) noexcept : base(it) {}
using _Unchecked_type = appender; // Mark iterator as checked. FMT_UNCHECKED_ITERATOR(appender);
auto operator++() FMT_NOEXCEPT -> appender& { return *this; } auto operator++() noexcept -> appender& { return *this; }
auto operator++(int) FMT_NOEXCEPT -> appender { return *this; } auto operator++(int) noexcept -> appender { return *this; }
}; };
// A formatting argument. It is a trivially copyable/constructible type to // A formatting argument. It is a trivially copyable/constructible type to
@ -1529,7 +1538,7 @@ template <typename Context> class basic_format_arg {
detail::type type_; detail::type type_;
template <typename ContextType, typename T> template <typename ContextType, typename T>
friend FMT_CONSTEXPR auto detail::make_arg(const T& value) friend FMT_CONSTEXPR auto detail::make_arg(T&& value)
-> basic_format_arg<ContextType>; -> basic_format_arg<ContextType>;
template <typename Visitor, typename Ctx> template <typename Visitor, typename Ctx>
@ -1564,7 +1573,7 @@ template <typename Context> class basic_format_arg {
constexpr basic_format_arg() : type_(detail::type::none_type) {} constexpr basic_format_arg() : type_(detail::type::none_type) {}
constexpr explicit operator bool() const FMT_NOEXCEPT { constexpr explicit operator bool() const noexcept {
return type_ != detail::type::none_type; return type_ != detail::type::none_type;
} }
@ -1674,7 +1683,7 @@ class locale_ref {
constexpr locale_ref() : locale_(nullptr) {} constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale> explicit locale_ref(const Locale& loc); template <typename Locale> explicit locale_ref(const Locale& loc);
explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } explicit operator bool() const noexcept { return locale_ != nullptr; }
template <typename Locale> auto get() const -> Locale; template <typename Locale> auto get() const -> Locale;
}; };
@ -1690,10 +1699,40 @@ constexpr auto encode_types() -> unsigned long long {
} }
template <typename Context, typename T> template <typename Context, typename T>
FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context> { FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value<Context> {
const auto& arg = arg_mapper<Context>().map(std::forward<T>(val));
[[maybe_unused]] constexpr bool formattable_char =
!std::is_same<decltype(arg), const unformattable_char&>::value;
static_assert(formattable_char, "Mixing character types is disallowed.");
[[maybe_unused]] constexpr bool formattable_const =
!std::is_same<decltype(arg), const unformattable_const&>::value;
static_assert(formattable_const, "Cannot format a const argument.");
// Formatting of arbitrary pointers is disallowed. If you want to output
// a pointer cast it to "void *" or "const void *". In particular, this
// forbids formatting of "[const] volatile char *" which is printed as bool
// by iostreams.
[[maybe_unused]] constexpr bool formattable_pointer =
!std::is_same<decltype(arg), const unformattable_pointer&>::value;
static_assert(formattable_pointer,
"Formatting of non-void pointers is disallowed.");
[[maybe_unused]] constexpr bool formattable =
!std::is_same<decltype(arg), const unformattable&>::value;
static_assert(
formattable,
"Cannot format an argument. To make type T formattable provide a "
"formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
return {arg};
}
template <typename Context, typename T>
FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context> {
basic_format_arg<Context> arg; basic_format_arg<Context> arg;
arg.type_ = mapped_type_constant<T, Context>::value; arg.type_ = mapped_type_constant<T, Context>::value;
arg.value_ = arg_mapper<Context>().map(value); arg.value_ = make_value<Context>(value);
return arg; return arg;
} }
@ -1703,37 +1742,12 @@ FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context> {
template <bool IS_PACKED, typename Context, type, typename T, template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(IS_PACKED)> FMT_ENABLE_IF(IS_PACKED)>
FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> { FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
const auto& arg = arg_mapper<Context>().map(std::forward<T>(val)); return make_value<Context>(val);
constexpr bool formattable_char =
!std::is_same<decltype(arg), const unformattable_char&>::value;
static_assert(formattable_char, "Mixing character types is disallowed.");
constexpr bool formattable_const =
!std::is_same<decltype(arg), const unformattable_const&>::value;
static_assert(formattable_const, "Cannot format a const argument.");
// Formatting of arbitrary pointers is disallowed. If you want to output
// a pointer cast it to "void *" or "const void *". In particular, this
// forbids formatting of "[const] volatile char *" which is printed as bool
// by iostreams.
constexpr bool formattable_pointer =
!std::is_same<decltype(arg), const unformattable_pointer&>::value;
static_assert(formattable_pointer,
"Formatting of non-void pointers is disallowed.");
constexpr bool formattable =
!std::is_same<decltype(arg), const unformattable&>::value;
static_assert(
formattable,
"Cannot format an argument. To make type T formattable provide a "
"formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
return {arg};
} }
template <bool IS_PACKED, typename Context, type, typename T, template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(!IS_PACKED)> FMT_ENABLE_IF(!IS_PACKED)>
inline auto make_arg(const T& value) -> basic_format_arg<Context> { FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg<Context> {
return make_arg<Context>(value); return make_arg<Context>(value);
} }
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
@ -2015,14 +2029,23 @@ template <typename Context> class basic_format_args {
// between clang and gcc on ARM (#1919). // between clang and gcc on ARM (#1919).
using format_args = basic_format_args<format_context>; using format_args = basic_format_args<format_context>;
// We cannot use enum classes as bit fields because of a gcc bug // We cannot use enum classes as bit fields because of a gcc bug,
// so we put them in namespaces instead.
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414.
// Additionally, if an underlying type is specified, older gcc incorrectly warns
// that the type is too small for all the enum values.
// Both these bugs are fixed as of gcc 9.3.
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903
# define FMT_ENUM_UNDERLYING_TYPE(type)
#else
# define FMT_ENUM_UNDERLYING_TYPE(type) : type
#endif
namespace align { namespace align {
enum type { none, left, right, center, numeric }; enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char) { none, left, right, center, numeric };
} }
using align_t = align::type; using align_t = align::type;
namespace sign { namespace sign {
enum type { none, minus, plus, space }; enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char) { none, minus, plus, space };
} }
using sign_t = sign::type; using sign_t = sign::type;
@ -2072,7 +2095,8 @@ enum class presentation_type : unsigned char {
general_upper, // 'G' general_upper, // 'G'
chr, // 'c' chr, // 'c'
string, // 's' string, // 's'
pointer // 'p' pointer, // 'p'
debug // '?'
}; };
// Format specifiers for built-in and string types. // Format specifiers for built-in and string types.
@ -2231,13 +2255,12 @@ template <typename Char> constexpr bool is_ascii_letter(Char c) {
// Converts a character to ASCII. Returns a number > 127 on conversion failure. // Converts a character to ASCII. Returns a number > 127 on conversion failure.
template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)> template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
constexpr auto to_ascii(Char value) -> Char { constexpr auto to_ascii(Char c) -> Char {
return value; return c;
} }
template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)> template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
constexpr auto to_ascii(Char value) -> constexpr auto to_ascii(Char c) -> underlying_t<Char> {
typename std::underlying_type<Char>::type { return c;
return value;
} }
template <typename Char> template <typename Char>
@ -2302,7 +2325,7 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
FMT_ASSERT(begin != end, ""); FMT_ASSERT(begin != end, "");
auto align = align::none; auto align = align::none;
auto p = begin + code_point_length(begin); auto p = begin + code_point_length(begin);
if (p >= end) p = begin; if (end - p <= 0) p = begin;
for (;;) { for (;;) {
switch (to_ascii(*p)) { switch (to_ascii(*p)) {
case '<': case '<':
@ -2488,6 +2511,8 @@ FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type {
return presentation_type::string; return presentation_type::string;
case 'p': case 'p':
return presentation_type::pointer; return presentation_type::pointer;
case '?':
return presentation_type::debug;
default: default:
return presentation_type::none; return presentation_type::none;
} }
@ -2661,17 +2686,27 @@ FMT_CONSTEXPR FMT_INLINE void parse_format_string(
} }
} }
template <typename T, bool = is_named_arg<T>::value> struct strip_named_arg {
using type = T;
};
template <typename T> struct strip_named_arg<T, true> {
using type = remove_cvref_t<decltype(T::value)>;
};
template <typename T, typename ParseContext> template <typename T, typename ParseContext>
FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
-> decltype(ctx.begin()) { -> decltype(ctx.begin()) {
using char_type = typename ParseContext::char_type; using char_type = typename ParseContext::char_type;
using context = buffer_context<char_type>; using context = buffer_context<char_type>;
using stripped_type = typename strip_named_arg<T>::type;
using mapped_type = conditional_t< using mapped_type = conditional_t<
mapped_type_constant<T, context>::value != type::custom_type, mapped_type_constant<T, context>::value != type::custom_type,
decltype(arg_mapper<context>().map(std::declval<const T&>())), T>; decltype(arg_mapper<context>().map(std::declval<const T&>())),
stripped_type>;
auto f = conditional_t<has_formatter<mapped_type, context>::value, auto f = conditional_t<has_formatter<mapped_type, context>::value,
formatter<mapped_type, char_type>, formatter<mapped_type, char_type>,
fallback_formatter<T, char_type>>(); fallback_formatter<stripped_type, char_type>>();
return f.parse(ctx); return f.parse(ctx);
} }
@ -2717,7 +2752,8 @@ template <typename Char, typename ErrorHandler = error_handler>
FMT_CONSTEXPR auto check_char_specs(const basic_format_specs<Char>& specs, FMT_CONSTEXPR auto check_char_specs(const basic_format_specs<Char>& specs,
ErrorHandler&& eh = {}) -> bool { ErrorHandler&& eh = {}) -> bool {
if (specs.type != presentation_type::none && if (specs.type != presentation_type::none &&
specs.type != presentation_type::chr) { specs.type != presentation_type::chr &&
specs.type != presentation_type::debug) {
check_int_type_spec(specs.type, eh); check_int_type_spec(specs.type, eh);
return false; return false;
} }
@ -2741,7 +2777,6 @@ struct float_specs {
bool upper : 1; bool upper : 1;
bool locale : 1; bool locale : 1;
bool binary32 : 1; bool binary32 : 1;
bool fallback : 1;
bool showpoint : 1; bool showpoint : 1;
}; };
@ -2801,7 +2836,8 @@ FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type,
template <typename ErrorHandler = error_handler> template <typename ErrorHandler = error_handler>
FMT_CONSTEXPR void check_string_type_spec(presentation_type type, FMT_CONSTEXPR void check_string_type_spec(presentation_type type,
ErrorHandler&& eh = {}) { ErrorHandler&& eh = {}) {
if (type != presentation_type::none && type != presentation_type::string) if (type != presentation_type::none && type != presentation_type::string &&
type != presentation_type::debug)
eh.on_error("invalid type specifier"); eh.on_error("invalid type specifier");
} }
@ -2835,7 +2871,8 @@ template <typename Handler> class specs_checker : public Handler {
FMT_CONSTEXPR void on_sign(sign_t s) { FMT_CONSTEXPR void on_sign(sign_t s) {
require_numeric_argument(); require_numeric_argument();
if (is_integral_type(arg_type_) && arg_type_ != type::int_type && if (is_integral_type(arg_type_) && arg_type_ != type::int_type &&
arg_type_ != type::long_long_type && arg_type_ != type::char_type) { arg_type_ != type::long_long_type && arg_type_ != type::int128_type &&
arg_type_ != type::char_type) {
this->on_error("format specifier requires signed argument"); this->on_error("format specifier requires signed argument");
} }
Handler::on_sign(s); Handler::on_sign(s);
@ -3043,6 +3080,27 @@ struct formatter<T, Char,
-> decltype(ctx.out()); -> decltype(ctx.out());
}; };
#define FMT_FORMAT_AS(Type, Base) \
template <typename Char> \
struct formatter<Type, Char> : formatter<Base, Char> { \
template <typename FormatContext> \
auto format(Type const& val, FormatContext& ctx) const \
-> decltype(ctx.out()) { \
return formatter<Base, Char>::format(static_cast<Base>(val), ctx); \
} \
}
FMT_FORMAT_AS(signed char, int);
FMT_FORMAT_AS(unsigned char, unsigned);
FMT_FORMAT_AS(short, int);
FMT_FORMAT_AS(unsigned short, unsigned);
FMT_FORMAT_AS(long, long long);
FMT_FORMAT_AS(unsigned long, unsigned long long);
FMT_FORMAT_AS(Char*, const Char*);
FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
FMT_FORMAT_AS(std::nullptr_t, const void*);
FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
template <typename Char> struct basic_runtime { basic_string_view<Char> str; }; template <typename Char> struct basic_runtime { basic_string_view<Char> str; };
/** A compile-time format string. */ /** A compile-time format string. */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -9,10 +9,8 @@
#define FMT_OS_H_ #define FMT_OS_H_
#include <cerrno> #include <cerrno>
#include <clocale> // locale_t
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdio>
#include <cstdlib> // strtod_l
#include <system_error> // std::system_error #include <system_error> // std::system_error
#if defined __APPLE__ || defined(__FreeBSD__) #if defined __APPLE__ || defined(__FreeBSD__)
@ -141,7 +139,7 @@ template <typename Char> struct formatter<std::error_code, Char> {
}; };
#ifdef _WIN32 #ifdef _WIN32
FMT_API const std::error_category& system_category() FMT_NOEXCEPT; FMT_API const std::error_category& system_category() noexcept;
FMT_BEGIN_DETAIL_NAMESPACE FMT_BEGIN_DETAIL_NAMESPACE
// A converter from UTF-16 to UTF-8. // A converter from UTF-16 to UTF-8.
@ -165,7 +163,7 @@ class utf16_to_utf8 {
}; };
FMT_API void format_windows_error(buffer<char>& out, int error_code, FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) FMT_NOEXCEPT; const char* message) noexcept;
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
FMT_API std::system_error vwindows_error(int error_code, string_view format_str, FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
@ -207,10 +205,9 @@ std::system_error windows_error(int error_code, string_view message,
// Reports a Windows error without throwing an exception. // Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors. // Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code, FMT_API void report_windows_error(int error_code, const char* message) noexcept;
const char* message) FMT_NOEXCEPT;
#else #else
inline const std::error_category& system_category() FMT_NOEXCEPT { inline const std::error_category& system_category() noexcept {
return std::system_category(); return std::system_category();
} }
#endif // _WIN32 #endif // _WIN32
@ -237,13 +234,13 @@ class buffered_file {
void operator=(const buffered_file&) = delete; void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file. // Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {} buffered_file() noexcept : file_(nullptr) {}
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT; FMT_API ~buffered_file() noexcept;
public: public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) { buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
other.file_ = nullptr; other.file_ = nullptr;
} }
@ -261,10 +258,11 @@ class buffered_file {
FMT_API void close(); FMT_API void close();
// Returns the pointer to a FILE object representing this file. // Returns the pointer to a FILE object representing this file.
FILE* get() const FMT_NOEXCEPT { return file_; } FILE* get() const noexcept { return file_; }
// We place parentheses around fileno to workaround a bug in some versions // We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro. // of MinGW that define fileno as a macro.
// DEPRECATED! Rename to descriptor to avoid issues with macros.
FMT_API int(fileno)() const; FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) { void vprint(string_view format_str, format_args args) {
@ -279,12 +277,12 @@ class buffered_file {
#if FMT_USE_FCNTL #if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1. // A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw // Methods that are not declared with noexcept may throw
// fmt::system_error in case of failure. Note that some errors such as // 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 // closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the // than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler. // invalid parameter handler with _set_invalid_parameter_handler.
class file { class FMT_API file {
private: private:
int fd_; // File descriptor. int fd_; // File descriptor.
@ -303,16 +301,16 @@ class file {
}; };
// Constructs a file object which doesn't represent any file. // Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {} file() noexcept : fd_(-1) {}
// Opens a file and constructs a file object representing this file. // Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag); file(cstring_view path, int oflag);
public: public:
file(const file&) = delete; file(const file&) = delete;
void operator=(const file&) = delete; void operator=(const file&) = delete;
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw. // Move assignment is not noexcept because close may throw.
file& operator=(file&& other) { file& operator=(file&& other) {
@ -323,43 +321,43 @@ class file {
} }
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT; ~file() noexcept;
// Returns the file descriptor. // Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; } int descriptor() const noexcept { return fd_; }
// Closes the file. // Closes the file.
FMT_API void close(); void close();
// Returns the file size. The size has signed type for consistency with // Returns the file size. The size has signed type for consistency with
// stat::st_size. // stat::st_size.
FMT_API long long size() const; long long size() const;
// Attempts to read count bytes from the file into the specified buffer. // Attempts to read count bytes from the file into the specified buffer.
FMT_API size_t read(void* buffer, size_t count); size_t read(void* buffer, size_t count);
// Attempts to write count bytes from the specified buffer to the file. // Attempts to write count bytes from the specified buffer to the file.
FMT_API size_t write(const void* buffer, size_t count); size_t write(const void* buffer, size_t count);
// Duplicates a file descriptor with the dup function and returns // Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object. // the duplicate as a file object.
FMT_API static file dup(int fd); static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd); void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd, std::error_code& ec) FMT_NOEXCEPT; void dup2(int fd, std::error_code& ec) noexcept;
// Creates a pipe setting up read_end and write_end file objects for reading // Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively. // and writing respectively.
FMT_API static void pipe(file& read_end, file& write_end); static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches // Creates a buffered_file object associated with this file and detaches
// this file object from the file. // this file object from the file.
FMT_API buffered_file fdopen(const char* mode); buffered_file fdopen(const char* mode);
}; };
// Returns the memory page size. // Returns the memory page size.
@ -462,7 +460,7 @@ class FMT_API ostream final : private detail::buffer<char> {
* ``<integer>``: Flags passed to `open * ``<integer>``: Flags passed to `open
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_ <https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
(``file::WRONLY | file::CREATE`` by default) (``file::WRONLY | file::CREATE | file::TRUNC`` by default)
* ``buffer_size=<integer>``: Output buffer size * ``buffer_size=<integer>``: Output buffer size
**Example**:: **Example**::
@ -477,50 +475,6 @@ inline ostream output_file(cstring_view path, T... params) {
} }
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
static void freelocale(locale_t loc) { _free_locale(loc); }
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
return _strtod_l(nptr, endptr, loc);
}
# endif
locale_t locale_;
public:
using type = locale_t;
locale(const locale&) = delete;
void operator=(const locale&) = delete;
locale() {
# ifndef _WIN32
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
# else
locale_ = _create_locale(LC_NUMERIC, "C");
# endif
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~locale() { freelocale(locale_); }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
FMT_DEPRECATED double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE
FMT_MODULE_EXPORT_END FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -8,6 +8,7 @@
#ifndef FMT_OSTREAM_H_ #ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
#include <fstream>
#include <ostream> #include <ostream>
#include "format.h" #include "format.h"
@ -45,15 +46,57 @@ struct is_streamable<
enable_if_t< enable_if_t<
std::is_arithmetic<T>::value || std::is_array<T>::value || std::is_arithmetic<T>::value || std::is_array<T>::value ||
std::is_pointer<T>::value || std::is_same<T, char8_type>::value || std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
std::is_same<T, std::basic_string<Char>>::value || std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
std::is_same<T, std_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::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
: std::false_type {}; : std::false_type {};
template <typename Char> FILE* get_file(std::basic_filebuf<Char>&) {
return nullptr;
}
struct dummy_filebuf {
FILE* _Myfile;
};
template <typename T, typename U = int> struct ms_filebuf {
using type = dummy_filebuf;
};
template <typename T> struct ms_filebuf<T, decltype(T::_Myfile, 0)> {
using type = T;
};
using filebuf_type = ms_filebuf<std::filebuf>::type;
FILE* get_file(filebuf_type& buf);
// Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace.
namespace {
struct filebuf_access_tag {};
} // namespace
template <typename Tag, typename FileMemberPtr, FileMemberPtr file>
class filebuf_access {
friend FILE* get_file(filebuf_type& buf) { return buf.*file; }
};
template class filebuf_access<filebuf_access_tag,
decltype(&filebuf_type::_Myfile),
&filebuf_type::_Myfile>;
inline bool write(std::filebuf& buf, fmt::string_view data) {
print(get_file(buf), data);
return true;
}
inline bool write(std::wfilebuf&, fmt::basic_string_view<wchar_t>) {
return false;
}
// Write the content of buf to os. // Write the content of buf to os.
// It is a separate function rather than a part of vprint to simplify testing. // It is a separate function rather than a part of vprint to simplify testing.
template <typename Char> template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
if (const_check(FMT_MSC_VER)) {
auto filebuf = dynamic_cast<std::basic_filebuf<Char>*>(os.rdbuf());
if (filebuf && write(*filebuf, {buf.data(), buf.size()})) return;
}
const Char* buf_data = buf.data(); const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size(); unsigned_streamsize size = buf.size();
@ -76,27 +119,34 @@ void format_value(buffer<Char>& buf, const T& value,
#endif #endif
output << value; output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.try_resize(buf.size());
} }
} // namespace detail
// Formats an object of type T that has an overloaded ostream operator<<. // Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char> template <typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
: private formatter<basic_string_view<Char>, Char> { template <typename T, typename OutputIt>
using formatter<basic_string_view<Char>, Char>::parse; auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
template <typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
-> OutputIt { -> OutputIt {
auto buffer = basic_memory_buffer<Char>(); auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale()); format_value(buffer, value, ctx.locale());
return formatter<basic_string_view<Char>, Char>::format( return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx); {buffer.data(), buffer.size()}, ctx);
} }
};
using ostream_formatter = basic_ostream_formatter<char>;
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;
// DEPRECATED! // DEPRECATED!
template <typename OutputIt> template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx) auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx) const
-> OutputIt { -> OutputIt {
auto buffer = basic_memory_buffer<Char>(); auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale()); format_value(buffer, value, ctx.locale());
@ -107,7 +157,8 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
FMT_MODULE_EXPORT FMT_MODULE_EXPORT
template <typename Char> template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, 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) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto buffer = basic_memory_buffer<Char>(); auto buffer = basic_memory_buffer<Char>();
detail::vformat_to(buffer, format_str, args); detail::vformat_to(buffer, format_str, args);
@ -124,12 +175,19 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
\endrst \endrst
*/ */
FMT_MODULE_EXPORT FMT_MODULE_EXPORT
template <typename S, typename... Args, template <typename... T>
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) { vprint(os, fmt, fmt::make_format_args(args...));
vprint(os, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
} }
FMT_MODULE_EXPORT
template <typename... Args>
void print(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
}
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_ #endif // FMT_OSTREAM_H_

View File

@ -203,7 +203,7 @@ using make_index_sequence = make_integer_sequence<size_t, N>;
#endif #endif
template <class Tuple, class F, size_t... Is> template <class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT { void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) noexcept {
using std::get; using std::get;
// using free function get<I>(T) now. // using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
@ -221,9 +221,28 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
} }
#if FMT_MSC_VER
// 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> template <typename Range>
using value_type = using range_reference_type =
remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>; 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 OutputIt> OutputIt write_delimiter(OutputIt out) { template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
*out++ = ','; *out++ = ',';
@ -231,286 +250,9 @@ template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
return out; return out;
} }
struct singleton {
unsigned char upper;
unsigned char lower_count;
};
inline auto is_printable(uint16_t x, const singleton* singletons,
size_t singletons_size,
const unsigned char* singleton_lowers,
const unsigned char* normal, size_t normal_size)
-> bool {
auto upper = x >> 8;
auto lower_start = 0;
for (size_t i = 0; i < singletons_size; ++i) {
auto s = singletons[i];
auto lower_end = lower_start + s.lower_count;
if (upper < s.upper) break;
if (upper == s.upper) {
for (auto j = lower_start; j < lower_end; ++j) {
if (singleton_lowers[j] == (x & 0xff)) return false;
}
}
lower_start = lower_end;
}
auto xsigned = static_cast<int>(x);
auto current = true;
for (size_t i = 0; i < normal_size; ++i) {
auto v = static_cast<int>(normal[i]);
auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v;
xsigned -= len;
if (xsigned < 0) break;
current = !current;
}
return current;
}
// Returns true iff the code point cp is printable.
// This code is generated by support/printable.py.
inline auto is_printable(uint32_t cp) -> bool {
static constexpr singleton singletons0[] = {
{0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8},
{0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13},
{0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5},
{0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22},
{0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3},
{0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8},
{0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9},
};
static constexpr unsigned char singletons0_lower[] = {
0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90,
0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f,
0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04,
0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d,
0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d,
0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d,
0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5,
0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49,
0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7,
0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e,
0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16,
0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f,
0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf,
0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27,
0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91,
0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
0xfe, 0xff,
};
static constexpr singleton singletons1[] = {
{0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2},
{0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5},
{0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5},
{0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2},
{0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5},
{0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2},
{0xfa, 2}, {0xfb, 1},
};
static constexpr unsigned char singletons1_lower[] = {
0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07,
0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36,
0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b,
0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66,
0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27,
0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7,
0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6,
0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66,
0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0,
0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
};
static constexpr unsigned char normal0[] = {
0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04,
0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0,
0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03,
0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03,
0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15,
0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f,
0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07,
0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06,
0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac,
0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c,
0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c,
0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b,
0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03,
0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80,
0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c,
0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17,
0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80,
0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d,
};
static constexpr unsigned char normal1[] = {
0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f,
0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e,
0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09,
0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16,
0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36,
0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33,
0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e,
0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41,
0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22,
0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04,
0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03,
0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81,
0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1,
0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a,
0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09,
0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89,
0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09,
0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50,
0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83,
0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05,
0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80,
0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07,
0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07,
0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06,
};
auto lower = static_cast<uint16_t>(cp);
if (cp < 0x10000) {
return is_printable(lower, singletons0,
sizeof(singletons0) / sizeof(*singletons0),
singletons0_lower, normal0, sizeof(normal0));
}
if (cp < 0x20000) {
return is_printable(lower, singletons1,
sizeof(singletons1) / sizeof(*singletons1),
singletons1_lower, normal1, sizeof(normal1));
}
if (0x2a6de <= cp && cp < 0x2a700) return false;
if (0x2b735 <= cp && cp < 0x2b740) return false;
if (0x2b81e <= cp && cp < 0x2b820) return false;
if (0x2cea2 <= cp && cp < 0x2ceb0) return false;
if (0x2ebe1 <= cp && cp < 0x2f800) return false;
if (0x2fa1e <= cp && cp < 0x30000) return false;
if (0x3134b <= cp && cp < 0xe0100) return false;
if (0xe01f0 <= cp && cp < 0x110000) return false;
return cp < 0x110000;
}
inline auto needs_escape(uint32_t cp) -> bool {
return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
!is_printable(cp);
}
template <typename Char> struct find_escape_result {
const Char* begin;
const Char* end;
uint32_t cp;
};
template <typename Char>
auto find_escape(const Char* begin, const Char* end)
-> find_escape_result<Char> {
for (; begin != end; ++begin) {
auto cp = static_cast<typename std::make_unsigned<Char>::type>(*begin);
if (sizeof(Char) == 1 && cp >= 0x80) continue;
if (needs_escape(cp)) return {begin, begin + 1, cp};
}
return {begin, nullptr, 0};
}
inline auto find_escape(const char* begin, const char* end)
-> find_escape_result<char> {
if (!is_utf8()) return find_escape<char>(begin, end);
auto result = find_escape_result<char>{end, nullptr, 0};
for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
[&](uint32_t cp, string_view sv) {
if (needs_escape(cp)) {
result = {sv.begin(), sv.end(), cp};
return false;
}
return true;
});
return result;
}
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt { auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
*out++ = '"'; return write_escaped_string(out, str);
auto begin = str.begin(), end = str.end();
do {
auto escape = find_escape(begin, end);
out = copy_str<Char>(begin, escape.begin, out);
begin = escape.end;
if (!begin) break;
auto c = static_cast<Char>(escape.cp);
switch (escape.cp) {
case '\n':
*out++ = '\\';
c = 'n';
break;
case '\r':
*out++ = '\\';
c = 'r';
break;
case '\t':
*out++ = '\\';
c = 't';
break;
case '"':
FMT_FALLTHROUGH;
case '\\':
*out++ = '\\';
break;
default:
if (is_utf8()) {
if (escape.cp < 0x100) {
out = format_to(out, "\\x{:02x}", escape.cp);
continue;
}
if (escape.cp < 0x10000) {
out = format_to(out, "\\u{:04x}", escape.cp);
continue;
}
if (escape.cp < 0x110000) {
out = format_to(out, "\\U{:08x}", escape.cp);
continue;
}
}
for (Char escape_char : basic_string_view<Char>(
escape.begin, to_unsigned(escape.end - escape.begin))) {
out = format_to(
out, "\\x{:02x}",
static_cast<typename std::make_unsigned<Char>::type>(escape_char));
}
continue;
}
*out++ = c;
} while (begin != end);
*out++ = '"';
return out;
} }
template <typename Char, typename OutputIt, typename T, template <typename Char, typename OutputIt, typename T,
@ -523,10 +265,7 @@ inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
template <typename Char, typename OutputIt, typename Arg, template <typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)> FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
OutputIt write_range_entry(OutputIt out, const Arg v) { OutputIt write_range_entry(OutputIt out, const Arg v) {
*out++ = '\''; return write_escaped_char(out, v);
*out++ = v;
*out++ = '\'';
return out;
} }
template < template <
@ -565,7 +304,8 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
} }
template <typename FormatContext = format_context> template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const TupleT& values, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out(); auto out = ctx.out();
*out++ = '('; *out++ = '(';
detail::for_each(values, format_each<FormatContext>{0, out}); detail::for_each(values, format_each<FormatContext>{0, out});
@ -582,43 +322,96 @@ template <typename T, typename Char> struct is_range {
!std::is_constructible<detail::std_string_view<Char>, T>::value; !std::is_constructible<detail::std_string_view<Char>, T>::value;
}; };
template <typename T, typename Char> 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>;
} // namespace detail
template <typename R, typename Char>
struct formatter< struct formatter<
T, Char, R, Char,
enable_if_t< enable_if_t<
fmt::is_range<T, Char>::value fmt::is_range<R, Char>::value
// Workaround a bug in MSVC 2019 and earlier. // Workaround a bug in MSVC 2019 and earlier.
#if !FMT_MSC_VER #if !FMT_MSC_VER
&& (is_formattable<detail::value_type<T>, Char>::value || &&
detail::has_fallback_formatter<detail::value_type<T>, Char>::value) (is_formattable<detail::uncvref_type<detail::maybe_const_range<R>>,
Char>::value ||
detail::has_fallback_formatter<
detail::uncvref_type<detail::maybe_const_range<R>>, Char>::value)
#endif #endif
>> { >> {
using range_type = detail::maybe_const_range<R>;
using formatter_type =
detail::range_formatter_type<Char, detail::uncvref_type<range_type>>;
formatter_type underlying_;
bool custom_specs_ = false;
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it != ':')
FMT_THROW(format_error("no top-level range formatters supported"));
custom_specs_ = true;
++it;
ctx.advance_to(it);
return underlying_.parse(ctx);
} }
template < template <typename FormatContext>
typename FormatContext, typename U, auto format(range_type& range, FormatContext& ctx) const
FMT_ENABLE_IF( -> decltype(ctx.out()) {
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
const T, T>>::value)>
auto format(U& range, FormatContext& ctx) -> decltype(ctx.out()) {
#ifdef FMT_DEPRECATED_BRACED_RANGES #ifdef FMT_DEPRECATED_BRACED_RANGES
Char prefix = '{'; Char prefix = '{';
Char postfix = '}'; Char postfix = '}';
#else #else
Char prefix = detail::is_set<T>::value ? '{' : '['; Char prefix = detail::is_set<R>::value ? '{' : '[';
Char postfix = detail::is_set<T>::value ? '}' : ']'; Char postfix = detail::is_set<R>::value ? '}' : ']';
#endif #endif
detail::range_mapper<buffer_context<Char>> mapper;
auto out = ctx.out(); auto out = ctx.out();
*out++ = prefix; *out++ = prefix;
int i = 0; int i = 0;
auto it = std::begin(range); auto it = detail::range_begin(range);
auto end = std::end(range); auto end = detail::range_end(range);
for (; it != end; ++it) { for (; it != end; ++it) {
if (i > 0) out = detail::write_delimiter(out); if (i > 0) out = detail::write_delimiter(out);
out = detail::write_range_entry<Char>(out, *it); if (custom_specs_) {
ctx.advance_to(out);
out = underlying_.format(mapper.map(*it), ctx);
} else {
out = detail::write_range_entry<Char>(out, *it);
}
++i; ++i;
} }
*out++ = postfix; *out++ = postfix;
@ -629,14 +422,14 @@ struct formatter<
template <typename T, typename Char> template <typename T, typename Char>
struct formatter< struct formatter<
T, Char, T, Char,
enable_if_t< enable_if_t<detail::is_map<T>::value
detail::is_map<T>::value
// Workaround a bug in MSVC 2019 and earlier. // Workaround a bug in MSVC 2019 and earlier.
#if !FMT_MSC_VER #if !FMT_MSC_VER
&& (is_formattable<detail::value_type<T>, Char>::value || && (is_formattable<detail::uncvref_type<T>, Char>::value ||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value) detail::has_fallback_formatter<detail::uncvref_type<T>,
Char>::value)
#endif #endif
>> { >> {
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return ctx.begin();
@ -647,7 +440,7 @@ struct formatter<
FMT_ENABLE_IF( FMT_ENABLE_IF(
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value, std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
const T, T>>::value)> const T, T>>::value)>
auto format(U& map, FormatContext& ctx) -> decltype(ctx.out()) { auto format(U& map, FormatContext& ctx) const -> decltype(ctx.out()) {
auto out = ctx.out(); auto out = ctx.out();
*out++ = '{'; *out++ = '{';
int i = 0; int i = 0;

View File

@ -92,8 +92,8 @@ auto vformat(basic_string_view<Char> format_str,
template <typename S, typename... Args, typename Char = char_t<S>, template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)> FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> { auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); return vformat(to_string_view(format_str),
return vformat(to_string_view(format_str), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename Locale, typename S, typename Char = char_t<S>, template <typename Locale, typename S, typename Char = char_t<S>,
@ -113,7 +113,7 @@ template <typename Locale, typename S, typename... Args,
inline auto format(const Locale& loc, const S& format_str, Args&&... args) inline auto format(const Locale& loc, const S& format_str, Args&&... args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str), return detail::vformat(loc, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename OutputIt, typename S, typename Char = char_t<S>, template <typename OutputIt, typename S, typename Char = char_t<S>,
@ -132,8 +132,8 @@ template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); return vformat_to(out, to_string_view(fmt),
return vformat_to(out, to_string_view(fmt), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename S, typename... Args, typename Char, size_t SIZE, template <typename S, typename... Args, typename Char, size_t SIZE,
@ -141,8 +141,8 @@ template <typename S, typename... Args, typename Char, size_t SIZE,
FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf, FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf,
const S& format_str, Args&&... args) -> const S& format_str, Args&&... args) ->
typename buffer_context<Char>::iterator { typename buffer_context<Char>::iterator {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); detail::vformat_to(buf, to_string_view(format_str),
detail::vformat_to(buf, to_string_view(format_str), vargs, {}); fmt::make_format_args<buffer_context<Char>>(args...), {});
return detail::buffer_appender<Char>(buf); return detail::buffer_appender<Char>(buf);
} }
@ -167,8 +167,8 @@ template <
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
Args&&... args) -> Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type { typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); return vformat_to(out, loc, to_string_view(format_str),
return vformat_to(out, loc, to_string_view(format_str), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename OutputIt, typename Char, typename... Args, template <typename OutputIt, typename Char, typename... Args,
@ -190,16 +190,16 @@ template <typename OutputIt, typename S, typename... Args,
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
const Args&... args) -> format_to_n_result<OutputIt> { const Args&... args) -> format_to_n_result<OutputIt> {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); return vformat_to_n(out, n, to_string_view(fmt),
return vformat_to_n(out, n, to_string_view(fmt), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename S, typename... Args, typename Char = char_t<S>, template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t { inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
detail::counting_buffer<Char> buf; detail::counting_buffer<Char> buf;
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); detail::vformat_to(buf, to_string_view(fmt),
detail::vformat_to(buf, to_string_view(fmt), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
return buf.count(); return buf.count();
} }

View File

@ -56,24 +56,10 @@ constexpr const char basic_data<T>::right_padding_shifts[];
template <typename T> constexpr const unsigned basic_data<T>::prefixes[]; template <typename T> constexpr const unsigned basic_data<T>::prefixes[];
#endif #endif
template <typename T> template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(
int format_float(char* buf, std::size_t size, const char* format, int precision, float x) noexcept;
T value) { template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(
#ifdef FMT_FUZZ double x) noexcept;
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about nonliteral format string.
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
FMT_NOEXCEPT;
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
FMT_NOEXCEPT;
} // namespace detail } // namespace detail
// Workaround a bug in MSVC2013 that prevents instantiation of format_float. // Workaround a bug in MSVC2013 that prevents instantiation of format_float.
@ -100,11 +86,6 @@ template FMT_API void detail::vformat_to(
detail::buffer<char>&, string_view, detail::buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref); basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::snprintf_float(long double, int,
detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(double, int, detail::float_specs, template FMT_API int detail::format_float(double, int, detail::float_specs,
detail::buffer<char>&); detail::buffer<char>&);
template FMT_API int detail::format_float(long double, int, detail::float_specs, template FMT_API int detail::format_float(long double, int, detail::float_specs,

View File

@ -35,9 +35,15 @@
# ifndef S_IRGRP # ifndef S_IRGRP
# define S_IRGRP 0 # define S_IRGRP 0
# endif # endif
# ifndef S_IWGRP
# define S_IWGRP 0
# endif
# ifndef S_IROTH # ifndef S_IROTH
# define S_IROTH 0 # define S_IROTH 0
# endif # endif
# ifndef S_IWOTH
# define S_IWOTH 0
# endif
# endif // _WIN32 # endif // _WIN32
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
@ -107,7 +113,7 @@ class system_message {
unsigned long result_; unsigned long result_;
wchar_t* message_; wchar_t* message_;
static bool is_whitespace(wchar_t c) FMT_NOEXCEPT { static bool is_whitespace(wchar_t c) noexcept {
return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0'; return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0';
} }
@ -126,15 +132,15 @@ class system_message {
} }
} }
~system_message() { LocalFree(message_); } ~system_message() { LocalFree(message_); }
explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; } explicit operator bool() const noexcept { return result_ != 0; }
operator basic_string_view<wchar_t>() const FMT_NOEXCEPT { operator basic_string_view<wchar_t>() const noexcept {
return basic_string_view<wchar_t>(message_, result_); return basic_string_view<wchar_t>(message_, result_);
} }
}; };
class utf8_system_category final : public std::error_category { class utf8_system_category final : public std::error_category {
public: public:
const char* name() const FMT_NOEXCEPT override { return "system"; } const char* name() const noexcept override { return "system"; }
std::string message(int error_code) const override { std::string message(int error_code) const override {
system_message msg(error_code); system_message msg(error_code);
if (msg) { if (msg) {
@ -149,7 +155,7 @@ class utf8_system_category final : public std::error_category {
} // namespace detail } // namespace detail
FMT_API const std::error_category& system_category() FMT_NOEXCEPT { FMT_API const std::error_category& system_category() noexcept {
static const detail::utf8_system_category category; static const detail::utf8_system_category category;
return category; return category;
} }
@ -161,7 +167,7 @@ std::system_error vwindows_error(int err_code, string_view format_str,
} }
void detail::format_windows_error(detail::buffer<char>& out, int error_code, void detail::format_windows_error(detail::buffer<char>& out, int error_code,
const char* message) FMT_NOEXCEPT { const char* message) noexcept {
FMT_TRY { FMT_TRY {
system_message msg(error_code); system_message msg(error_code);
if (msg) { if (msg) {
@ -176,12 +182,12 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
format_error_code(out, error_code, message); format_error_code(out, error_code, message);
} }
void report_windows_error(int error_code, const char* message) FMT_NOEXCEPT { void report_windows_error(int error_code, const char* message) noexcept {
report_error(detail::format_windows_error, error_code, message); report_error(detail::format_windows_error, error_code, message);
} }
#endif // _WIN32 #endif // _WIN32
buffered_file::~buffered_file() FMT_NOEXCEPT { buffered_file::~buffered_file() noexcept {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0) if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
report_system_error(errno, "cannot close file"); report_system_error(errno, "cannot close file");
} }
@ -214,7 +220,8 @@ file::file(cstring_view path, int oflag) {
# ifdef _WIN32 # ifdef _WIN32
using mode_t = int; using mode_t = int;
# endif # endif
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; constexpr mode_t mode =
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
# if defined(_WIN32) && !defined(__MINGW32__) # if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1; fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
@ -225,7 +232,7 @@ file::file(cstring_view path, int oflag) {
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
} }
file::~file() FMT_NOEXCEPT { file::~file() noexcept {
// Don't retry close in case of EINTR! // Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
@ -299,7 +306,7 @@ void file::dup2(int fd) {
} }
} }
void file::dup2(int fd, std::error_code& ec) FMT_NOEXCEPT { void file::dup2(int fd, std::error_code& ec) noexcept {
int result = 0; int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) ec = std::error_code(errno, std::generic_category()); if (result == -1) ec = std::error_code(errno, std::generic_category());

View File

@ -1 +1 @@
4.2.1 5.0.0

View File

@ -1,7 +1,11 @@
# C++14 feature support detection # C++14 feature support detection
include(CheckCXXSourceCompiles)
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
function (fmt_check_cxx_compiler_flag flag result)
if (NOT MSVC)
check_cxx_compiler_flag("${flag}" ${result})
endif ()
endfunction ()
if (NOT CMAKE_CXX_STANDARD) if (NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
@ -9,35 +13,38 @@ endif()
message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}") message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}")
if (CMAKE_CXX_STANDARD EQUAL 20) if (CMAKE_CXX_STANDARD EQUAL 20)
check_cxx_compiler_flag(-std=c++20 has_std_20_flag) fmt_check_cxx_compiler_flag(-std=c++20 has_std_20_flag)
check_cxx_compiler_flag(-std=c++2a has_std_2a_flag) fmt_check_cxx_compiler_flag(-std=c++2a has_std_2a_flag)
if (has_std_20_flag) if (has_std_20_flag)
set(CXX_STANDARD_FLAG -std=c++20) set(CXX_STANDARD_FLAG -std=c++20)
elseif (has_std_2a_flag) elseif (has_std_2a_flag)
set(CXX_STANDARD_FLAG -std=c++2a) set(CXX_STANDARD_FLAG -std=c++2a)
endif () endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 17) elseif (CMAKE_CXX_STANDARD EQUAL 17)
check_cxx_compiler_flag(-std=c++17 has_std_17_flag) fmt_check_cxx_compiler_flag(-std=c++17 has_std_17_flag)
check_cxx_compiler_flag(-std=c++1z has_std_1z_flag) fmt_check_cxx_compiler_flag(-std=c++1z has_std_1z_flag)
if (has_std_17_flag) if (has_std_17_flag)
set(CXX_STANDARD_FLAG -std=c++17) set(CXX_STANDARD_FLAG -std=c++17)
elseif (has_std_1z_flag) elseif (has_std_1z_flag)
set(CXX_STANDARD_FLAG -std=c++1z) set(CXX_STANDARD_FLAG -std=c++1z)
endif () endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 14) elseif (CMAKE_CXX_STANDARD EQUAL 14)
check_cxx_compiler_flag(-std=c++14 has_std_14_flag) fmt_check_cxx_compiler_flag(-std=c++14 has_std_14_flag)
check_cxx_compiler_flag(-std=c++1y has_std_1y_flag) fmt_check_cxx_compiler_flag(-std=c++1y has_std_1y_flag)
if (has_std_14_flag) if (has_std_14_flag)
set(CXX_STANDARD_FLAG -std=c++14) set(CXX_STANDARD_FLAG -std=c++14)
elseif (has_std_1y_flag) elseif (has_std_1y_flag)
set(CXX_STANDARD_FLAG -std=c++1y) set(CXX_STANDARD_FLAG -std=c++1y)
endif () endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 11) elseif (CMAKE_CXX_STANDARD EQUAL 11)
check_cxx_compiler_flag(-std=c++11 has_std_11_flag) fmt_check_cxx_compiler_flag(-std=c++11 has_std_11_flag)
check_cxx_compiler_flag(-std=c++0x has_std_0x_flag) fmt_check_cxx_compiler_flag(-std=c++0x has_std_0x_flag)
if (has_std_11_flag) if (has_std_11_flag)
set(CXX_STANDARD_FLAG -std=c++11) set(CXX_STANDARD_FLAG -std=c++11)
@ -45,26 +52,3 @@ elseif (CMAKE_CXX_STANDARD EQUAL 11)
set(CXX_STANDARD_FLAG -std=c++0x) set(CXX_STANDARD_FLAG -std=c++0x)
endif () endif ()
endif () endif ()
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
# Check if user-defined literals are available
check_cxx_source_compiles("
void operator\"\" _udl(long double);
int main() {}"
SUPPORTS_USER_DEFINED_LITERALS)
if (NOT SUPPORTS_USER_DEFINED_LITERALS)
set (SUPPORTS_USER_DEFINED_LITERALS OFF)
endif ()
# Check if <variant> is available
set(CMAKE_REQUIRED_FLAGS -std=c++1z)
check_cxx_source_compiles("
#include <variant>
int main() {}"
FMT_HAS_VARIANT)
if (NOT FMT_HAS_VARIANT)
set (FMT_HAS_VARIANT OFF)
endif ()
set(CMAKE_REQUIRED_FLAGS )

View File

@ -171,7 +171,7 @@ def main():
normal1 = compress_normal(normal1) normal1 = compress_normal(normal1)
print("""\ print("""\
inline auto is_printable(uint32_t cp) -> bool {\ FMT_FUNC auto is_printable(uint32_t cp) -> bool {\
""") """)
print_singletons(singletons0u, singletons0l, 'singletons0', 'singletons0_lower') print_singletons(singletons0u, singletons0l, 'singletons0', 'singletons0_lower')
print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower') print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower')

View File

@ -94,8 +94,7 @@ void GenericLogFmt(LogLevel level, LogType type, const char* file, int line, con
static_assert(NumFields == sizeof...(args), static_assert(NumFields == sizeof...(args),
"Unexpected number of replacement fields in format string; did you pass too few or " "Unexpected number of replacement fields in format string; did you pass too few or "
"too many arguments?"); "too many arguments?");
GenericLogFmtImpl(level, type, file, line, format, GenericLogFmtImpl(level, type, file, line, format, fmt::make_format_args(args...));
fmt::make_args_checked<Args...>(format, args...));
} }
void GenericLog(LogLevel level, LogType type, const char* file, int line, const char* fmt, ...) void GenericLog(LogLevel level, LogType type, const char* file, int line, const char* fmt, ...)

View File

@ -43,7 +43,7 @@ bool MsgAlertFmt(bool yes_no, MsgType style, Common::Log::LogType log_type, cons
"too many arguments?"); "too many arguments?");
static_assert(fmt::is_compile_string<S>::value); static_assert(fmt::is_compile_string<S>::value);
return MsgAlertFmtImpl(yes_no, style, log_type, file, line, format, return MsgAlertFmtImpl(yes_no, style, log_type, file, line, format,
fmt::make_args_checked<Args...>(format, args...)); fmt::make_format_args(args...));
} }
template <std::size_t NumFields, bool has_non_positional_args, typename S, typename... Args> template <std::size_t NumFields, bool has_non_positional_args, typename S, typename... Args>
@ -57,12 +57,7 @@ bool MsgAlertFmtT(bool yes_no, MsgType style, Common::Log::LogType log_type, con
"Unexpected number of replacement fields in format string; did you pass too few or " "Unexpected number of replacement fields in format string; did you pass too few or "
"too many arguments?"); "too many arguments?");
static_assert(fmt::is_compile_string<S>::value); static_assert(fmt::is_compile_string<S>::value);
// It's only possible for us to compile-time check the English-language string. auto arg_list = fmt::make_format_args(args...);
// make_args_checked uses static_asserts to verify that a string is formattable with the given
// arguments. But it can't do that if the string varies at runtime, so we can't check
// translations. Still, verifying that the English string is correct will help ensure that
// translations use valid strings.
auto arg_list = fmt::make_args_checked<Args...>(format, args...);
return MsgAlertFmtImpl(yes_no, style, log_type, file, line, translated_format, arg_list); return MsgAlertFmtImpl(yes_no, style, log_type, file, line, translated_format, arg_list);
} }

View File

@ -30,8 +30,6 @@
<AdditionalIncludeDirectories>$(ProjectDir)Settings;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(ProjectDir)Settings;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)TAS;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(ProjectDir)TAS;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)VideoInterface;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(ProjectDir)VideoInterface;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<!--Ignore warnings in locally-instantiated fmt templates-->
<ExternalTemplatesDiagnostics>false</ExternalTemplatesDiagnostics>
</ClCompile> </ClCompile>
<Manifest> <Manifest>
<AdditionalManifestFiles>DolphinQt.manifest;%(AdditionalManifestFiles)</AdditionalManifestFiles> <AdditionalManifestFiles>DolphinQt.manifest;%(AdditionalManifestFiles)</AdditionalManifestFiles>

View File

@ -1231,10 +1231,10 @@ struct fmt::formatter<ScissorPos>
template <typename FormatContext> template <typename FormatContext>
auto format(const ScissorPos& pos, FormatContext& ctx) auto format(const ScissorPos& pos, FormatContext& ctx)
{ {
return format_to(ctx.out(), return fmt::format_to(ctx.out(),
"X: {} (raw: {})\n" "X: {} (raw: {})\n"
"Y: {} (raw: {})", "Y: {} (raw: {})",
pos.x - 342, pos.x_full, pos.y - 342, pos.y_full); pos.x - 342, pos.x_full, pos.y - 342, pos.y_full);
} }
}; };
@ -1257,10 +1257,10 @@ struct fmt::formatter<ScissorOffset>
template <typename FormatContext> template <typename FormatContext>
auto format(const ScissorOffset& off, FormatContext& ctx) auto format(const ScissorOffset& off, FormatContext& ctx)
{ {
return format_to(ctx.out(), return fmt::format_to(ctx.out(),
"X: {} (raw: {})\n" "X: {} (raw: {})\n"
"Y: {} (raw: {})", "Y: {} (raw: {})",
(off.x << 1) - 342, off.x_full, (off.y << 1) - 342, off.y_full); (off.x << 1) - 342, off.x_full, (off.y << 1) - 342, off.y_full);
} }
}; };

View File

@ -6,7 +6,7 @@
#define STRINGIFY_HELPER(x) #x #define STRINGIFY_HELPER(x) #x
#define STRINGIFY(x) STRINGIFY_HELPER(x) #define STRINGIFY(x) STRINGIFY_HELPER(x)
#if defined _MSC_FULL_VER && _MSC_FULL_VER < 193030705 #if defined _MSC_FULL_VER && _MSC_FULL_VER < 193231328
#pragma message("Current _MSC_FULL_VER: " STRINGIFY(_MSC_FULL_VER)) #pragma message("Current _MSC_FULL_VER: " STRINGIFY(_MSC_FULL_VER))
#error Please update your build environment to the latest Visual Studio 2022! #error Please update your build environment to the latest Visual Studio 2022!
#endif #endif