Merge branch 'fmtlib:master' into master
This commit is contained in:
commit
4b4f314527
45
.gitignore
vendored
45
.gitignore
vendored
@ -1,37 +1,24 @@
|
|||||||
.vscode/
|
|
||||||
.vs/
|
|
||||||
|
|
||||||
*.iml
|
|
||||||
.idea/
|
|
||||||
.externalNativeBuild/
|
|
||||||
.gradle/
|
|
||||||
gradle/
|
|
||||||
gradlew*
|
|
||||||
local.properties
|
|
||||||
build/
|
|
||||||
support/.cxx
|
|
||||||
|
|
||||||
bin/
|
|
||||||
/_CPack_Packages
|
|
||||||
/CMakeScripts
|
|
||||||
/doc/doxyxml
|
|
||||||
/doc/html
|
|
||||||
/doc/node_modules
|
|
||||||
virtualenv
|
|
||||||
/Testing
|
|
||||||
/install_manifest.txt
|
|
||||||
*~
|
|
||||||
*.a
|
*.a
|
||||||
*.so*
|
*.so*
|
||||||
*.xcodeproj
|
*.xcodeproj
|
||||||
*.zip
|
*~
|
||||||
cmake_install.cmake
|
.vscode/
|
||||||
CPack*.cmake
|
/CMakeScripts
|
||||||
fmt-*.cmake
|
/Testing
|
||||||
CTestTestfile.cmake
|
/_CPack_Packages
|
||||||
|
/doc/doxyxml
|
||||||
|
/doc/html
|
||||||
|
/doc/node_modules
|
||||||
|
/install_manifest.txt
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
CMakeFiles
|
CMakeFiles
|
||||||
|
CPack*.cmake
|
||||||
|
CTestTestfile.cmake
|
||||||
FMT.build
|
FMT.build
|
||||||
Makefile
|
Makefile
|
||||||
run-msbuild.bat
|
bin/
|
||||||
|
build/
|
||||||
|
cmake_install.cmake
|
||||||
|
fmt-*.cmake
|
||||||
fmt.pc
|
fmt.pc
|
||||||
|
virtualenv
|
||||||
|
|||||||
@ -430,6 +430,51 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc,
|
|||||||
return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
|
return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Rep1, typename Rep2>
|
||||||
|
struct is_same_arithmetic_type
|
||||||
|
: public std::integral_constant<bool,
|
||||||
|
(std::is_integral<Rep1>::value &&
|
||||||
|
std::is_integral<Rep2>::value) ||
|
||||||
|
(std::is_floating_point<Rep1>::value &&
|
||||||
|
std::is_floating_point<Rep2>::value)> {
|
||||||
|
};
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename To, typename FromRep, typename FromPeriod,
|
||||||
|
FMT_ENABLE_IF(is_same_arithmetic_type<FromRep, typename To::rep>::value)>
|
||||||
|
To fmt_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
|
||||||
|
#if FMT_SAFE_DURATION_CAST
|
||||||
|
// throwing version of safe_duration_cast
|
||||||
|
// only available for integer<->integer or float<->float casts
|
||||||
|
int ec;
|
||||||
|
To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
|
||||||
|
if (ec) FMT_THROW(format_error("cannot format duration"));
|
||||||
|
return to;
|
||||||
|
#else
|
||||||
|
// standard duration cast, may overflow and invoke undefined behavior
|
||||||
|
return std::chrono::duration_cast<To>(from);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename To, typename FromRep, typename FromPeriod,
|
||||||
|
FMT_ENABLE_IF(!is_same_arithmetic_type<FromRep, typename To::rep>::value)>
|
||||||
|
To fmt_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
|
||||||
|
// mixed integer<->float cast is not supported with safe_duration_cast
|
||||||
|
// fallback to standard duration cast in this case
|
||||||
|
return std::chrono::duration_cast<To>(from);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Duration>
|
||||||
|
std::time_t to_time_t(
|
||||||
|
std::chrono::time_point<std::chrono::system_clock, Duration> time_point) {
|
||||||
|
// cannot use std::chrono::system_clock::to_time_t() since this would first
|
||||||
|
// require a cast to std::chrono::system_clock::time_point, which could
|
||||||
|
// overflow.
|
||||||
|
return fmt_duration_cast<std::chrono::duration<std::time_t>>(
|
||||||
|
time_point.time_since_epoch())
|
||||||
|
.count();
|
||||||
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
FMT_BEGIN_EXPORT
|
FMT_BEGIN_EXPORT
|
||||||
@ -478,8 +523,8 @@ inline std::tm localtime(std::time_t time) {
|
|||||||
#if FMT_USE_LOCAL_TIME
|
#if FMT_USE_LOCAL_TIME
|
||||||
template <typename Duration>
|
template <typename Duration>
|
||||||
inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
|
inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
|
||||||
return localtime(std::chrono::system_clock::to_time_t(
|
return localtime(
|
||||||
std::chrono::current_zone()->to_sys(time)));
|
detail::to_time_t(std::chrono::current_zone()->to_sys(time)));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -523,9 +568,10 @@ inline std::tm gmtime(std::time_t time) {
|
|||||||
return gt.tm_;
|
return gt.tm_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Duration>
|
||||||
inline std::tm gmtime(
|
inline std::tm gmtime(
|
||||||
std::chrono::time_point<std::chrono::system_clock> time_point) {
|
std::chrono::time_point<std::chrono::system_clock, Duration> time_point) {
|
||||||
return gmtime(std::chrono::system_clock::to_time_t(time_point));
|
return gmtime(detail::to_time_t(time_point));
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
@ -1051,13 +1097,12 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
|
|||||||
std::chrono::seconds::rep>::type,
|
std::chrono::seconds::rep>::type,
|
||||||
std::ratio<1, detail::pow10(num_fractional_digits)>>;
|
std::ratio<1, detail::pow10(num_fractional_digits)>>;
|
||||||
|
|
||||||
const auto fractional =
|
const auto fractional = d - fmt_duration_cast<std::chrono::seconds>(d);
|
||||||
d - std::chrono::duration_cast<std::chrono::seconds>(d);
|
|
||||||
const auto subseconds =
|
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()
|
||||||
: std::chrono::duration_cast<subsecond_precision>(fractional).count();
|
: fmt_duration_cast<subsecond_precision>(fractional).count();
|
||||||
auto n = static_cast<uint32_or_64_or_128_t<long long>>(subseconds);
|
auto n = static_cast<uint32_or_64_or_128_t<long long>>(subseconds);
|
||||||
const int num_digits = detail::count_digits(n);
|
const int num_digits = detail::count_digits(n);
|
||||||
|
|
||||||
@ -1620,17 +1665,6 @@ template <typename T> struct make_unsigned_or_unchanged<T, true> {
|
|||||||
using type = typename std::make_unsigned<T>::type;
|
using type = typename std::make_unsigned<T>::type;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if FMT_SAFE_DURATION_CAST
|
|
||||||
// throwing version of safe_duration_cast
|
|
||||||
template <typename To, typename FromRep, typename FromPeriod>
|
|
||||||
To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
|
|
||||||
int ec;
|
|
||||||
To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
|
|
||||||
if (ec) FMT_THROW(format_error("cannot format duration"));
|
|
||||||
return to;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <typename Rep, typename Period,
|
template <typename Rep, typename Period,
|
||||||
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
|
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
|
||||||
inline std::chrono::duration<Rep, std::milli> get_milliseconds(
|
inline std::chrono::duration<Rep, std::milli> get_milliseconds(
|
||||||
@ -1640,17 +1674,17 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
|
|||||||
#if FMT_SAFE_DURATION_CAST
|
#if FMT_SAFE_DURATION_CAST
|
||||||
using CommonSecondsType =
|
using CommonSecondsType =
|
||||||
typename std::common_type<decltype(d), std::chrono::seconds>::type;
|
typename std::common_type<decltype(d), std::chrono::seconds>::type;
|
||||||
const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
|
const auto d_as_common = fmt_duration_cast<CommonSecondsType>(d);
|
||||||
const auto d_as_whole_seconds =
|
const auto d_as_whole_seconds =
|
||||||
fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
|
fmt_duration_cast<std::chrono::seconds>(d_as_common);
|
||||||
// this conversion should be nonproblematic
|
// this conversion should be nonproblematic
|
||||||
const auto diff = d_as_common - d_as_whole_seconds;
|
const auto diff = d_as_common - d_as_whole_seconds;
|
||||||
const auto ms =
|
const auto ms =
|
||||||
fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
|
fmt_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
|
||||||
return ms;
|
return ms;
|
||||||
#else
|
#else
|
||||||
auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
|
auto s = fmt_duration_cast<std::chrono::seconds>(d);
|
||||||
return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
|
return fmt_duration_cast<std::chrono::milliseconds>(d - s);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1751,14 +1785,8 @@ struct chrono_formatter {
|
|||||||
|
|
||||||
// this may overflow and/or the result may not fit in the
|
// this may overflow and/or the result may not fit in the
|
||||||
// target type.
|
// target type.
|
||||||
#if FMT_SAFE_DURATION_CAST
|
|
||||||
// might need checked conversion (rep!=Rep)
|
// might need checked conversion (rep!=Rep)
|
||||||
auto tmpval = std::chrono::duration<rep, Period>(val);
|
s = fmt_duration_cast<seconds>(std::chrono::duration<rep, Period>(val));
|
||||||
s = fmt_safe_duration_cast<seconds>(tmpval);
|
|
||||||
#else
|
|
||||||
s = std::chrono::duration_cast<seconds>(
|
|
||||||
std::chrono::duration<rep, Period>(val));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if nan or inf, writes to out.
|
// returns true if nan or inf, writes to out.
|
||||||
@ -2082,25 +2110,22 @@ struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
|
|||||||
period::num != 1 || period::den != 1 ||
|
period::num != 1 || period::den != 1 ||
|
||||||
std::is_floating_point<typename Duration::rep>::value)) {
|
std::is_floating_point<typename Duration::rep>::value)) {
|
||||||
const auto epoch = val.time_since_epoch();
|
const auto epoch = val.time_since_epoch();
|
||||||
auto subsecs = std::chrono::duration_cast<Duration>(
|
auto subsecs = detail::fmt_duration_cast<Duration>(
|
||||||
epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
|
epoch - detail::fmt_duration_cast<std::chrono::seconds>(epoch));
|
||||||
|
|
||||||
if (subsecs.count() < 0) {
|
if (subsecs.count() < 0) {
|
||||||
auto second =
|
auto second =
|
||||||
std::chrono::duration_cast<Duration>(std::chrono::seconds(1));
|
detail::fmt_duration_cast<Duration>(std::chrono::seconds(1));
|
||||||
if (epoch.count() < ((Duration::min)() + second).count())
|
if (epoch.count() < ((Duration::min)() + second).count())
|
||||||
FMT_THROW(format_error("duration is too small"));
|
FMT_THROW(format_error("duration is too small"));
|
||||||
subsecs += second;
|
subsecs += second;
|
||||||
val -= second;
|
val -= second;
|
||||||
}
|
}
|
||||||
|
|
||||||
return formatter<std::tm, Char>::do_format(
|
return formatter<std::tm, Char>::do_format(gmtime(val), ctx, &subsecs);
|
||||||
gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx,
|
|
||||||
&subsecs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return formatter<std::tm, Char>::format(
|
return formatter<std::tm, Char>::format(gmtime(val), ctx);
|
||||||
gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2119,17 +2144,13 @@ struct formatter<std::chrono::local_time<Duration>, Char>
|
|||||||
if (period::num != 1 || period::den != 1 ||
|
if (period::num != 1 || period::den != 1 ||
|
||||||
std::is_floating_point<typename Duration::rep>::value) {
|
std::is_floating_point<typename Duration::rep>::value) {
|
||||||
const auto epoch = val.time_since_epoch();
|
const auto epoch = val.time_since_epoch();
|
||||||
const auto subsecs = std::chrono::duration_cast<Duration>(
|
const auto subsecs = detail::fmt_duration_cast<Duration>(
|
||||||
epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
|
epoch - detail::fmt_duration_cast<std::chrono::seconds>(epoch));
|
||||||
|
|
||||||
return formatter<std::tm, Char>::do_format(
|
return formatter<std::tm, Char>::do_format(localtime(val), ctx, &subsecs);
|
||||||
localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
|
|
||||||
ctx, &subsecs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return formatter<std::tm, Char>::format(
|
return formatter<std::tm, Char>::format(localtime(val), ctx);
|
||||||
localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
|
|
||||||
ctx);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
#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 100101
|
#define FMT_VERSION 100102
|
||||||
|
|
||||||
#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__)
|
||||||
@ -1318,6 +1318,7 @@ template <typename Context> class value {
|
|||||||
parse_ctx.advance_to(f.parse(parse_ctx));
|
parse_ctx.advance_to(f.parse(parse_ctx));
|
||||||
using qualified_type =
|
using qualified_type =
|
||||||
conditional_t<has_const_formatter<T, Context>(), const T, T>;
|
conditional_t<has_const_formatter<T, Context>(), const T, T>;
|
||||||
|
// Calling format through a mutable reference is deprecated.
|
||||||
ctx.advance_to(f.format(*static_cast<qualified_type*>(arg), ctx));
|
ctx.advance_to(f.format(*static_cast<qualified_type*>(arg), ctx));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1028,7 +1028,6 @@ class basic_memory_buffer final : public detail::buffer<T> {
|
|||||||
/** Increases the buffer capacity to *new_capacity*. */
|
/** Increases the buffer capacity to *new_capacity*. */
|
||||||
void reserve(size_t new_capacity) { this->try_reserve(new_capacity); }
|
void reserve(size_t new_capacity) { this->try_reserve(new_capacity); }
|
||||||
|
|
||||||
// Directly append data into the buffer
|
|
||||||
using detail::buffer<T>::append;
|
using detail::buffer<T>::append;
|
||||||
template <typename ContiguousRange>
|
template <typename ContiguousRange>
|
||||||
void append(const ContiguousRange& range) {
|
void append(const ContiguousRange& range) {
|
||||||
|
|||||||
@ -874,6 +874,20 @@ TEST(chrono_test, timestamps_ratios) {
|
|||||||
t4(std::chrono::duration<int, std::ratio<63>>(1));
|
t4(std::chrono::duration<int, std::ratio<63>>(1));
|
||||||
|
|
||||||
EXPECT_EQ(fmt::format("{:%M:%S}", t4), "01:03");
|
EXPECT_EQ(fmt::format("{:%M:%S}", t4), "01:03");
|
||||||
|
|
||||||
|
std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>
|
||||||
|
t5(std::chrono::seconds(32503680000));
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:%Y-%m-%d}", t5), "3000-01-01");
|
||||||
|
|
||||||
|
#if FMT_SAFE_DURATION_CAST
|
||||||
|
using years = std::chrono::duration<std::int64_t, std::ratio<31556952>>;
|
||||||
|
std::chrono::time_point<std::chrono::system_clock, years> t6(
|
||||||
|
(years(std::numeric_limits<std::int64_t>::max())));
|
||||||
|
|
||||||
|
EXPECT_THROW_MSG((void)fmt::format("{:%Y-%m-%d}", t6), fmt::format_error,
|
||||||
|
"cannot format duration");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(chrono_test, timestamps_sub_seconds) {
|
TEST(chrono_test, timestamps_sub_seconds) {
|
||||||
|
|||||||
@ -246,13 +246,6 @@ TEST(format_impl_test, format_error_code) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(format_impl_test, compute_width) {
|
|
||||||
EXPECT_EQ(4,
|
|
||||||
fmt::detail::compute_width(
|
|
||||||
fmt::basic_string_view<fmt::detail::char8_type>(
|
|
||||||
reinterpret_cast<const fmt::detail::char8_type*>("ёжик"))));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests fmt::detail::count_digits for integer type Int.
|
// Tests fmt::detail::count_digits for integer type Int.
|
||||||
template <typename Int> void test_count_digits() {
|
template <typename Int> void test_count_digits() {
|
||||||
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::detail::count_digits(i));
|
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::detail::count_digits(i));
|
||||||
|
|||||||
@ -173,6 +173,10 @@ TEST(util_test, parse_nonnegative_int) {
|
|||||||
EXPECT_EQ(fmt::detail::parse_nonnegative_int(begin, end, -1), -1);
|
EXPECT_EQ(fmt::detail::parse_nonnegative_int(begin, end, -1), -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(format_impl_test, compute_width) {
|
||||||
|
EXPECT_EQ(fmt::detail::compute_width("вожык"), 5);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(util_test, utf8_to_utf16) {
|
TEST(util_test, utf8_to_utf16) {
|
||||||
auto u = fmt::detail::utf8_to_utf16("лошадка");
|
auto u = fmt::detail::utf8_to_utf16("лошадка");
|
||||||
EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", u.str());
|
EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", u.str());
|
||||||
@ -1053,6 +1057,12 @@ TEST(format_test, precision) {
|
|||||||
EXPECT_EQ("123456", fmt::format("{0:.6}", "123456\xad"));
|
EXPECT_EQ("123456", fmt::format("{0:.6}", "123456\xad"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(xchar_test, utf8_precision) {
|
||||||
|
auto result = fmt::format("{:.4}", "caf\u00e9s"); // cafés
|
||||||
|
EXPECT_EQ(fmt::detail::compute_width(result), 4);
|
||||||
|
EXPECT_EQ(result, "caf\u00e9");
|
||||||
|
}
|
||||||
|
|
||||||
TEST(format_test, runtime_precision) {
|
TEST(format_test, runtime_precision) {
|
||||||
char format_str[buffer_size];
|
char format_str[buffer_size];
|
||||||
safe_sprintf(format_str, "{0:.{%u", UINT_MAX);
|
safe_sprintf(format_str, "{0:.{%u", UINT_MAX);
|
||||||
|
|||||||
@ -354,7 +354,7 @@ TEST(output_redirect_test, dup_error_in_ctor) {
|
|||||||
FMT_POSIX(close(fd));
|
FMT_POSIX(close(fd));
|
||||||
std::unique_ptr<output_redirect> redir{nullptr};
|
std::unique_ptr<output_redirect> redir{nullptr};
|
||||||
EXPECT_SYSTEM_ERROR_NOASSERT(
|
EXPECT_SYSTEM_ERROR_NOASSERT(
|
||||||
redir.reset(new output_redirect(f.get())), EBADF,
|
redir.reset(new output_redirect(f.get(), false)), EBADF,
|
||||||
fmt::format("cannot duplicate file descriptor {}", fd));
|
fmt::format("cannot duplicate file descriptor {}", fd));
|
||||||
copy.dup2(fd); // "undo" close or dtor will fail
|
copy.dup2(fd); // "undo" close or dtor will fail
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
using fmt::file;
|
using fmt::file;
|
||||||
|
|
||||||
output_redirect::output_redirect(FILE* f) : file_(f) {
|
output_redirect::output_redirect(FILE* f, bool flush) : file_(f) {
|
||||||
flush();
|
if (flush) this->flush();
|
||||||
int fd = FMT_POSIX(fileno(f));
|
int fd = FMT_POSIX(fileno(f));
|
||||||
// Create a file object referring to the original file.
|
// Create a file object referring to the original file.
|
||||||
original_ = file::dup(fd);
|
original_ = file::dup(fd);
|
||||||
|
|||||||
@ -77,7 +77,7 @@ class output_redirect {
|
|||||||
void restore();
|
void restore();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit output_redirect(FILE* file);
|
explicit output_redirect(FILE* file, bool flush = true);
|
||||||
~output_redirect() noexcept;
|
~output_redirect() noexcept;
|
||||||
|
|
||||||
output_redirect(const output_redirect&) = delete;
|
output_redirect(const output_redirect&) = delete;
|
||||||
|
|||||||
@ -70,7 +70,7 @@ namespace fmt {
|
|||||||
template <> struct scanner<tm> {
|
template <> struct scanner<tm> {
|
||||||
std::string format;
|
std::string format;
|
||||||
|
|
||||||
scan_parse_context::iterator parse(scan_parse_context& ctx) {
|
auto parse(scan_parse_context& ctx) -> scan_parse_context::iterator {
|
||||||
auto it = ctx.begin();
|
auto it = ctx.begin();
|
||||||
if (it != ctx.end() && *it == ':') ++it;
|
if (it != ctx.end() && *it == ':') ++it;
|
||||||
auto end = it;
|
auto end = it;
|
||||||
@ -82,7 +82,7 @@ template <> struct scanner<tm> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class ScanContext>
|
template <class ScanContext>
|
||||||
typename ScanContext::iterator scan(tm& t, ScanContext& ctx) {
|
auto scan(tm& t, ScanContext& ctx) const -> typename ScanContext::iterator {
|
||||||
auto result = strptime(ctx.begin(), format.c_str(), &t);
|
auto result = strptime(ctx.begin(), format.c_str(), &t);
|
||||||
if (!result) throw format_error("failed to parse time");
|
if (!result) throw format_error("failed to parse time");
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
46
test/scan.h
46
test/scan.h
@ -9,7 +9,7 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
|
||||||
#include "fmt/format.h"
|
#include "fmt/core.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
template <typename T, typename Char = char> struct scanner {
|
template <typename T, typename Char = char> struct scanner {
|
||||||
@ -27,8 +27,8 @@ class scan_parse_context {
|
|||||||
explicit FMT_CONSTEXPR scan_parse_context(string_view format)
|
explicit FMT_CONSTEXPR scan_parse_context(string_view format)
|
||||||
: format_(format) {}
|
: format_(format) {}
|
||||||
|
|
||||||
FMT_CONSTEXPR iterator begin() const { return format_.begin(); }
|
FMT_CONSTEXPR auto begin() const -> iterator { return format_.begin(); }
|
||||||
FMT_CONSTEXPR iterator end() const { return format_.end(); }
|
FMT_CONSTEXPR auto end() const -> iterator { return format_.end(); }
|
||||||
|
|
||||||
void advance_to(iterator it) {
|
void advance_to(iterator it) {
|
||||||
format_.remove_prefix(detail::to_unsigned(it - begin()));
|
format_.remove_prefix(detail::to_unsigned(it - begin()));
|
||||||
@ -44,8 +44,8 @@ struct scan_context {
|
|||||||
|
|
||||||
explicit FMT_CONSTEXPR scan_context(string_view input) : input_(input) {}
|
explicit FMT_CONSTEXPR scan_context(string_view input) : input_(input) {}
|
||||||
|
|
||||||
iterator begin() const { return input_.data(); }
|
auto begin() const -> iterator { return input_.data(); }
|
||||||
iterator end() const { return begin() + input_.size(); }
|
auto end() const -> iterator { return begin() + input_.size(); }
|
||||||
|
|
||||||
void advance_to(iterator it) {
|
void advance_to(iterator it) {
|
||||||
input_.remove_prefix(detail::to_unsigned(it - begin()));
|
input_.remove_prefix(detail::to_unsigned(it - begin()));
|
||||||
@ -106,7 +106,7 @@ class scan_arg {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
static void scan_custom_arg(void* arg, scan_parse_context& parse_ctx,
|
static void scan_custom_arg(void* arg, scan_parse_context& parse_ctx,
|
||||||
scan_context& ctx) {
|
scan_context& ctx) {
|
||||||
scanner<T> s;
|
auto s = scanner<T>();
|
||||||
parse_ctx.advance_to(s.parse(parse_ctx));
|
parse_ctx.advance_to(s.parse(parse_ctx));
|
||||||
ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx));
|
ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx));
|
||||||
}
|
}
|
||||||
@ -134,7 +134,7 @@ struct scan_handler : error_handler {
|
|||||||
int next_arg_id_;
|
int next_arg_id_;
|
||||||
scan_arg arg_;
|
scan_arg arg_;
|
||||||
|
|
||||||
template <typename T = unsigned> T read_uint() {
|
template <typename T = unsigned> auto read_uint() -> T {
|
||||||
T value = 0;
|
T value = 0;
|
||||||
auto it = scan_ctx_.begin(), end = scan_ctx_.end();
|
auto it = scan_ctx_.begin(), end = scan_ctx_.end();
|
||||||
while (it != end) {
|
while (it != end) {
|
||||||
@ -147,7 +147,7 @@ struct scan_handler : error_handler {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T = int> T read_int() {
|
template <typename T = int> auto read_int() -> T {
|
||||||
auto it = scan_ctx_.begin(), end = scan_ctx_.end();
|
auto it = scan_ctx_.begin(), end = scan_ctx_.end();
|
||||||
bool negative = it != end && *it == '-';
|
bool negative = it != end && *it == '-';
|
||||||
if (negative) ++it;
|
if (negative) ++it;
|
||||||
@ -162,7 +162,7 @@ struct scan_handler : error_handler {
|
|||||||
scan_args args)
|
scan_args args)
|
||||||
: parse_ctx_(format), scan_ctx_(input), args_(args), next_arg_id_(0) {}
|
: parse_ctx_(format), scan_ctx_(input), args_(args), next_arg_id_(0) {}
|
||||||
|
|
||||||
const char* pos() const { return scan_ctx_.begin(); }
|
auto pos() const -> const char* { return scan_ctx_.begin(); }
|
||||||
|
|
||||||
void on_text(const char* begin, const char* end) {
|
void on_text(const char* begin, const char* end) {
|
||||||
auto size = to_unsigned(end - begin);
|
auto size = to_unsigned(end - begin);
|
||||||
@ -172,13 +172,13 @@ struct scan_handler : error_handler {
|
|||||||
scan_ctx_.advance_to(it + size);
|
scan_ctx_.advance_to(it + size);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR int on_arg_id() { return on_arg_id(next_arg_id_++); }
|
FMT_CONSTEXPR auto on_arg_id() -> int { return on_arg_id(next_arg_id_++); }
|
||||||
FMT_CONSTEXPR int on_arg_id(int id) {
|
FMT_CONSTEXPR auto on_arg_id(int id) -> int {
|
||||||
if (id >= args_.size) on_error("argument index out of range");
|
if (id >= args_.size) on_error("argument index out of range");
|
||||||
arg_ = args_.data[id];
|
arg_ = args_.data[id];
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR int on_arg_id(string_view id) {
|
FMT_CONSTEXPR auto on_arg_id(string_view id) -> int {
|
||||||
if (id.data()) on_error("invalid format");
|
if (id.data()) on_error("invalid format");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -215,7 +215,7 @@ struct scan_handler : error_handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* on_format_specs(int, const char* begin, const char*) {
|
auto on_format_specs(int, const char* begin, const char*) -> const char* {
|
||||||
if (arg_.type != scan_type::custom_type) return begin;
|
if (arg_.type != scan_type::custom_type) return begin;
|
||||||
parse_ctx_.advance_to(begin);
|
parse_ctx_.advance_to(begin);
|
||||||
arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_);
|
arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_);
|
||||||
@ -224,21 +224,21 @@ struct scan_handler : error_handler {
|
|||||||
};
|
};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... T>
|
||||||
std::array<detail::scan_arg, sizeof...(Args)> make_scan_args(Args&... args) {
|
auto make_scan_args(T&... args) -> std::array<detail::scan_arg, sizeof...(T)> {
|
||||||
return {{args...}};
|
return {{args...}};
|
||||||
}
|
}
|
||||||
|
|
||||||
string_view::iterator vscan(string_view input, string_view format_str,
|
auto vscan(string_view input, string_view fmt, scan_args args)
|
||||||
scan_args args) {
|
-> string_view::iterator {
|
||||||
detail::scan_handler h(format_str, input, args);
|
auto h = detail::scan_handler(fmt, input, args);
|
||||||
detail::parse_format_string<false>(format_str, h);
|
detail::parse_format_string<false>(fmt, h);
|
||||||
return input.begin() + (h.pos() - &*input.begin());
|
return input.begin() + (h.pos() - &*input.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... T>
|
||||||
string_view::iterator scan(string_view input, string_view format_str,
|
auto scan(string_view input, string_view fmt, T&... args)
|
||||||
Args&... args) {
|
-> string_view::iterator {
|
||||||
return vscan(input, format_str, make_scan_args(args...));
|
return vscan(input, fmt, make_scan_args(args...));
|
||||||
}
|
}
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|||||||
@ -39,6 +39,11 @@ std::locale get_locale(const char* name, const char* alt_name) {
|
|||||||
auto loc = do_get_locale(name);
|
auto loc = do_get_locale(name);
|
||||||
if (loc == std::locale::classic() && alt_name)
|
if (loc == std::locale::classic() && alt_name)
|
||||||
loc = do_get_locale(alt_name);
|
loc = do_get_locale(alt_name);
|
||||||
|
#ifdef __OpenBSD__
|
||||||
|
// Locales are not working in OpenBSD:
|
||||||
|
// https://github.com/fmtlib/fmt/issues/3670.
|
||||||
|
loc = std::locale::classic();
|
||||||
|
#endif
|
||||||
if (loc == std::locale::classic())
|
if (loc == std::locale::classic())
|
||||||
fmt::print(stderr, "{} locale is missing.\n", name);
|
fmt::print(stderr, "{} locale is missing.\n", name);
|
||||||
return loc;
|
return loc;
|
||||||
|
|||||||
@ -187,18 +187,6 @@ template <typename S> std::string from_u8str(const S& str) {
|
|||||||
return std::string(str.begin(), str.end());
|
return std::string(str.begin(), str.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(xchar_test, format_utf8_precision) {
|
|
||||||
using str_type = std::basic_string<fmt::detail::char8_type>;
|
|
||||||
auto format =
|
|
||||||
str_type(reinterpret_cast<const fmt::detail::char8_type*>(u8"{:.4}"));
|
|
||||||
auto str = str_type(reinterpret_cast<const fmt::detail::char8_type*>(
|
|
||||||
u8"caf\u00e9s")); // cafés
|
|
||||||
auto result = fmt::format(format, str);
|
|
||||||
EXPECT_EQ(fmt::detail::compute_width(result), 4);
|
|
||||||
EXPECT_EQ(result.size(), 5);
|
|
||||||
EXPECT_EQ(from_u8str(result), from_u8str(str.substr(0, 5)));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(xchar_test, format_to) {
|
TEST(xchar_test, format_to) {
|
||||||
auto buf = std::vector<wchar_t>();
|
auto buf = std::vector<wchar_t>();
|
||||||
fmt::format_to(std::back_inserter(buf), L"{}{}", 42, L'\0');
|
fmt::format_to(std::back_inserter(buf), L"{}{}", 42, L'\0');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user