mirror of
https://github.com/gabime/spdlog.git
synced 2024-11-15 08:25:43 +08:00
Bumb fmt version to 7.0.3
This commit is contained in:
parent
fa501b46cf
commit
23c2c00d69
@ -9,7 +9,7 @@
|
|||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/async.h"
|
#include "spdlog/async.h"
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
#include "spdlog/fmt/bundled/locale.h"
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@ -29,7 +29,7 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
|
|||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable : 4996) // disable fopen warning under msvc
|
#pragma warning(disable : 4996) // disable fopen warning under msvc
|
||||||
#endif // _MSC_VER
|
#endif // _MSC_VER
|
||||||
|
|
||||||
int count_lines(const char *filename)
|
int count_lines(const char *filename)
|
||||||
{
|
{
|
||||||
@ -48,14 +48,14 @@ int count_lines(const char *filename)
|
|||||||
|
|
||||||
void verify_file(const char *filename, int expected_count)
|
void verify_file(const char *filename, int expected_count)
|
||||||
{
|
{
|
||||||
spdlog::info("Verifying {} to contain {:n} line..", filename, expected_count);
|
spdlog::info("Verifying {} to contain {} line..", filename, expected_count);
|
||||||
auto count = count_lines(filename);
|
auto count = count_lines(filename);
|
||||||
if (count != expected_count)
|
if (count != expected_count)
|
||||||
{
|
{
|
||||||
spdlog::error("Test failed. {} has {:n} lines instead of {:n}", filename, count, expected_count);
|
spdlog::error("Test failed. {} has {} lines instead of {}", filename, count, expected_count);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
spdlog::info("Line count OK ({:n})\n", count);
|
spdlog::info("Line count OK ({})\n", count);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
@ -98,11 +98,12 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
auto slot_size = sizeof(spdlog::details::async_msg);
|
auto slot_size = sizeof(spdlog::details::async_msg);
|
||||||
spdlog::info("-------------------------------------------------");
|
spdlog::info("-------------------------------------------------");
|
||||||
spdlog::info("Messages : {:n}", howmany);
|
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Messages : {:L}", howmany));
|
||||||
spdlog::info("Threads : {:n}", threads);
|
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Threads : {:L}", threads));
|
||||||
spdlog::info("Queue : {:n} slots", queue_size);
|
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Queue : {:L} slots", queue_size));
|
||||||
spdlog::info("Queue memory : {:n} x {} = {:n} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024);
|
spdlog::info(fmt::format(
|
||||||
spdlog::info("Total iters : {:n}", iters);
|
std::locale("en_US.UTF-8"), "Queue memory : {:L} x {:L} = {:L} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024));
|
||||||
|
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Total iters : {:L}", iters));
|
||||||
spdlog::info("-------------------------------------------------");
|
spdlog::info("-------------------------------------------------");
|
||||||
|
|
||||||
const char *filename = "logs/basic_async.log";
|
const char *filename = "logs/basic_async.log";
|
||||||
@ -175,5 +176,5 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> logger, int thread_co
|
|||||||
|
|
||||||
auto delta = high_resolution_clock::now() - start;
|
auto delta = high_resolution_clock::now() - start;
|
||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
spdlog::info("Elapsed: {} secs\t {:n}/sec", delta_d, int(howmany / delta_d));
|
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Elapsed: {} secs\t {:L}/sec", delta_d, int(howmany / delta_d)));
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "spdlog/sinks/daily_file_sink.h"
|
#include "spdlog/sinks/daily_file_sink.h"
|
||||||
#include "spdlog/sinks/null_sink.h"
|
#include "spdlog/sinks/null_sink.h"
|
||||||
#include "spdlog/sinks/rotating_file_sink.h"
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
|
#include "spdlog/fmt/bundled/locale.h"
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@ -32,7 +33,7 @@ static const int max_threads = 1000;
|
|||||||
void bench_threaded_logging(size_t threads, int iters)
|
void bench_threaded_logging(size_t threads, int iters)
|
||||||
{
|
{
|
||||||
spdlog::info("**************************************************************");
|
spdlog::info("**************************************************************");
|
||||||
spdlog::info("Multi threaded: {:n} threads, {:n} messages", threads, iters);
|
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Multi threaded: {:L} threads, {:L} messages", threads, iters));
|
||||||
spdlog::info("**************************************************************");
|
spdlog::info("**************************************************************");
|
||||||
|
|
||||||
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
|
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
|
||||||
@ -68,7 +69,7 @@ void bench_threaded_logging(size_t threads, int iters)
|
|||||||
void bench_single_threaded(int iters)
|
void bench_single_threaded(int iters)
|
||||||
{
|
{
|
||||||
spdlog::info("**************************************************************");
|
spdlog::info("**************************************************************");
|
||||||
spdlog::info("Single threaded: {:n} messages", iters);
|
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Single threaded: {} messages", iters));
|
||||||
spdlog::info("**************************************************************");
|
spdlog::info("**************************************************************");
|
||||||
|
|
||||||
auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true);
|
auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true);
|
||||||
@ -152,7 +153,8 @@ void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
|||||||
auto delta = high_resolution_clock::now() - start;
|
auto delta = high_resolution_clock::now() - start;
|
||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
|
|
||||||
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
spdlog::info(
|
||||||
|
fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d)));
|
||||||
spdlog::drop(log->name());
|
spdlog::drop(log->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +184,8 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, size_t thread_co
|
|||||||
|
|
||||||
auto delta = high_resolution_clock::now() - start;
|
auto delta = high_resolution_clock::now() - start;
|
||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
spdlog::info(
|
||||||
|
fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d)));
|
||||||
spdlog::drop(log->name());
|
spdlog::drop(log->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,7 +208,7 @@ void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log)
|
|||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
spdlog::drop(log->name());
|
spdlog::drop(log->name());
|
||||||
spdlog::set_default_logger(std::move(orig_default));
|
spdlog::set_default_logger(std::move(orig_default));
|
||||||
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||||
}
|
}
|
||||||
|
|
||||||
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
|
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||||
@ -232,7 +235,7 @@ void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
|
|||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
spdlog::drop(log->name());
|
spdlog::drop(log->name());
|
||||||
spdlog::set_default_logger(std::move(orig_default));
|
spdlog::set_default_logger(std::move(orig_default));
|
||||||
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
@ -48,7 +48,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
|||||||
// From fits in To without any problem.
|
// From fits in To without any problem.
|
||||||
} else {
|
} else {
|
||||||
// From does not always fit in To, resort to a dynamic check.
|
// From does not always fit in To, resort to a dynamic check.
|
||||||
if (from < T::min() || from > T::max()) {
|
if (from < (T::min)() || from > (T::max)()) {
|
||||||
// outside range.
|
// outside range.
|
||||||
ec = 1;
|
ec = 1;
|
||||||
return {};
|
return {};
|
||||||
@ -74,7 +74,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
|||||||
|
|
||||||
if (F::is_signed && !T::is_signed) {
|
if (F::is_signed && !T::is_signed) {
|
||||||
// From may be negative, not allowed!
|
// From may be negative, not allowed!
|
||||||
if (fmt::internal::is_negative(from)) {
|
if (fmt::detail::is_negative(from)) {
|
||||||
ec = 1;
|
ec = 1;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -84,7 +84,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
|||||||
// yes, From always fits in To.
|
// yes, From always fits in To.
|
||||||
} else {
|
} else {
|
||||||
// from may not fit in To, we have to do a dynamic check
|
// from may not fit in To, we have to do a dynamic check
|
||||||
if (from > static_cast<From>(T::max())) {
|
if (from > static_cast<From>((T::max)())) {
|
||||||
ec = 1;
|
ec = 1;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -97,7 +97,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
|||||||
// yes, From always fits in To.
|
// yes, From always fits in To.
|
||||||
} else {
|
} else {
|
||||||
// from may not fit in To, we have to do a dynamic check
|
// from may not fit in To, we have to do a dynamic check
|
||||||
if (from > static_cast<From>(T::max())) {
|
if (from > static_cast<From>((T::max)())) {
|
||||||
// outside range.
|
// outside range.
|
||||||
ec = 1;
|
ec = 1;
|
||||||
return {};
|
return {};
|
||||||
@ -141,7 +141,7 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
|
|||||||
|
|
||||||
// catch the only happy case
|
// catch the only happy case
|
||||||
if (std::isfinite(from)) {
|
if (std::isfinite(from)) {
|
||||||
if (from >= T::lowest() && from <= T::max()) {
|
if (from >= T::lowest() && from <= (T::max)()) {
|
||||||
return static_cast<To>(from);
|
return static_cast<To>(from);
|
||||||
}
|
}
|
||||||
// not within range.
|
// not within range.
|
||||||
@ -195,12 +195,13 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|||||||
}
|
}
|
||||||
// multiply with Factor::num without overflow or underflow
|
// multiply with Factor::num without overflow or underflow
|
||||||
if (Factor::num != 1) {
|
if (Factor::num != 1) {
|
||||||
const auto max1 = internal::max_value<IntermediateRep>() / Factor::num;
|
const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
|
||||||
if (count > max1) {
|
if (count > max1) {
|
||||||
ec = 1;
|
ec = 1;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num;
|
const auto min1 =
|
||||||
|
(std::numeric_limits<IntermediateRep>::min)() / Factor::num;
|
||||||
if (count < min1) {
|
if (count < min1) {
|
||||||
ec = 1;
|
ec = 1;
|
||||||
return {};
|
return {};
|
||||||
@ -269,7 +270,7 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|||||||
|
|
||||||
// multiply with Factor::num without overflow or underflow
|
// multiply with Factor::num without overflow or underflow
|
||||||
if (Factor::num != 1) {
|
if (Factor::num != 1) {
|
||||||
constexpr auto max1 = internal::max_value<IntermediateRep>() /
|
constexpr auto max1 = detail::max_value<IntermediateRep>() /
|
||||||
static_cast<IntermediateRep>(Factor::num);
|
static_cast<IntermediateRep>(Factor::num);
|
||||||
if (count > max1) {
|
if (count > max1) {
|
||||||
ec = 1;
|
ec = 1;
|
||||||
@ -306,12 +307,12 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|||||||
// Usage: f FMT_NOMACRO()
|
// Usage: f FMT_NOMACRO()
|
||||||
#define FMT_NOMACRO
|
#define FMT_NOMACRO
|
||||||
|
|
||||||
namespace internal {
|
namespace detail {
|
||||||
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
|
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
|
||||||
inline null<> localtime_s(...) { return null<>(); }
|
inline null<> localtime_s(...) { return null<>(); }
|
||||||
inline null<> gmtime_r(...) { return null<>(); }
|
inline null<> gmtime_r(...) { return null<>(); }
|
||||||
inline null<> gmtime_s(...) { return null<>(); }
|
inline null<> gmtime_s(...) { return null<>(); }
|
||||||
} // namespace internal
|
} // namespace detail
|
||||||
|
|
||||||
// Thread-safe replacement for std::localtime
|
// Thread-safe replacement for std::localtime
|
||||||
inline std::tm localtime(std::time_t time) {
|
inline std::tm localtime(std::time_t time) {
|
||||||
@ -322,22 +323,22 @@ inline std::tm localtime(std::time_t time) {
|
|||||||
dispatcher(std::time_t t) : time_(t) {}
|
dispatcher(std::time_t t) : time_(t) {}
|
||||||
|
|
||||||
bool run() {
|
bool run() {
|
||||||
using namespace fmt::internal;
|
using namespace fmt::detail;
|
||||||
return handle(localtime_r(&time_, &tm_));
|
return handle(localtime_r(&time_, &tm_));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handle(std::tm* tm) { return tm != nullptr; }
|
bool handle(std::tm* tm) { return tm != nullptr; }
|
||||||
|
|
||||||
bool handle(internal::null<>) {
|
bool handle(detail::null<>) {
|
||||||
using namespace fmt::internal;
|
using namespace fmt::detail;
|
||||||
return fallback(localtime_s(&tm_, &time_));
|
return fallback(localtime_s(&tm_, &time_));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fallback(int res) { return res == 0; }
|
bool fallback(int res) { return res == 0; }
|
||||||
|
|
||||||
#if !FMT_MSC_VER
|
#if !FMT_MSC_VER
|
||||||
bool fallback(internal::null<>) {
|
bool fallback(detail::null<>) {
|
||||||
using namespace fmt::internal;
|
using namespace fmt::detail;
|
||||||
std::tm* tm = std::localtime(&time_);
|
std::tm* tm = std::localtime(&time_);
|
||||||
if (tm) tm_ = *tm;
|
if (tm) tm_ = *tm;
|
||||||
return tm != nullptr;
|
return tm != nullptr;
|
||||||
@ -359,21 +360,21 @@ inline std::tm gmtime(std::time_t time) {
|
|||||||
dispatcher(std::time_t t) : time_(t) {}
|
dispatcher(std::time_t t) : time_(t) {}
|
||||||
|
|
||||||
bool run() {
|
bool run() {
|
||||||
using namespace fmt::internal;
|
using namespace fmt::detail;
|
||||||
return handle(gmtime_r(&time_, &tm_));
|
return handle(gmtime_r(&time_, &tm_));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handle(std::tm* tm) { return tm != nullptr; }
|
bool handle(std::tm* tm) { return tm != nullptr; }
|
||||||
|
|
||||||
bool handle(internal::null<>) {
|
bool handle(detail::null<>) {
|
||||||
using namespace fmt::internal;
|
using namespace fmt::detail;
|
||||||
return fallback(gmtime_s(&tm_, &time_));
|
return fallback(gmtime_s(&tm_, &time_));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fallback(int res) { return res == 0; }
|
bool fallback(int res) { return res == 0; }
|
||||||
|
|
||||||
#if !FMT_MSC_VER
|
#if !FMT_MSC_VER
|
||||||
bool fallback(internal::null<>) {
|
bool fallback(detail::null<>) {
|
||||||
std::tm* tm = std::gmtime(&time_);
|
std::tm* tm = std::gmtime(&time_);
|
||||||
if (tm) tm_ = *tm;
|
if (tm) tm_ = *tm;
|
||||||
return tm != nullptr;
|
return tm != nullptr;
|
||||||
@ -386,17 +387,17 @@ inline std::tm gmtime(std::time_t time) {
|
|||||||
return gt.tm_;
|
return gt.tm_;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace internal {
|
namespace detail {
|
||||||
inline std::size_t strftime(char* str, std::size_t count, const char* format,
|
inline size_t strftime(char* str, size_t count, const char* format,
|
||||||
const std::tm* time) {
|
const std::tm* time) {
|
||||||
return std::strftime(str, count, format, time);
|
return std::strftime(str, count, format, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::size_t strftime(wchar_t* str, std::size_t count,
|
inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
|
||||||
const wchar_t* format, const std::tm* time) {
|
const std::tm* time) {
|
||||||
return std::wcsftime(str, count, format, time);
|
return std::wcsftime(str, count, format, time);
|
||||||
}
|
}
|
||||||
} // namespace internal
|
} // namespace detail
|
||||||
|
|
||||||
template <typename Char> struct formatter<std::tm, Char> {
|
template <typename Char> struct formatter<std::tm, Char> {
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
@ -405,7 +406,7 @@ template <typename Char> struct formatter<std::tm, Char> {
|
|||||||
if (it != ctx.end() && *it == ':') ++it;
|
if (it != ctx.end() && *it == ':') ++it;
|
||||||
auto end = it;
|
auto end = it;
|
||||||
while (end != ctx.end() && *end != '}') ++end;
|
while (end != ctx.end() && *end != '}') ++end;
|
||||||
tm_format.reserve(internal::to_unsigned(end - it + 1));
|
tm_format.reserve(detail::to_unsigned(end - it + 1));
|
||||||
tm_format.append(it, end);
|
tm_format.append(it, end);
|
||||||
tm_format.push_back('\0');
|
tm_format.push_back('\0');
|
||||||
return end;
|
return end;
|
||||||
@ -414,11 +415,10 @@ template <typename Char> struct formatter<std::tm, Char> {
|
|||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) {
|
auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
basic_memory_buffer<Char> buf;
|
basic_memory_buffer<Char> buf;
|
||||||
std::size_t start = buf.size();
|
size_t start = buf.size();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
std::size_t size = buf.capacity() - start;
|
size_t size = buf.capacity() - start;
|
||||||
std::size_t count =
|
size_t count = detail::strftime(&buf[start], size, &tm_format[0], &tm);
|
||||||
internal::strftime(&buf[start], size, &tm_format[0], &tm);
|
|
||||||
if (count != 0) {
|
if (count != 0) {
|
||||||
buf.resize(start + count);
|
buf.resize(start + count);
|
||||||
break;
|
break;
|
||||||
@ -430,7 +430,7 @@ template <typename Char> struct formatter<std::tm, Char> {
|
|||||||
// https://github.com/fmtlib/fmt/issues/367
|
// https://github.com/fmtlib/fmt/issues/367
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const std::size_t MIN_GROWTH = 10;
|
const size_t MIN_GROWTH = 10;
|
||||||
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
||||||
}
|
}
|
||||||
return std::copy(buf.begin(), buf.end(), ctx.out());
|
return std::copy(buf.begin(), buf.end(), ctx.out());
|
||||||
@ -439,7 +439,7 @@ template <typename Char> struct formatter<std::tm, Char> {
|
|||||||
basic_memory_buffer<Char> tm_format;
|
basic_memory_buffer<Char> tm_format;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace internal {
|
namespace detail {
|
||||||
template <typename Period> FMT_CONSTEXPR const char* get_units() {
|
template <typename Period> FMT_CONSTEXPR const char* get_units() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -768,19 +768,25 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
|
|||||||
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
|
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
|
||||||
val);
|
val);
|
||||||
}
|
}
|
||||||
|
template <typename Char, typename OutputIt>
|
||||||
|
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
|
||||||
|
return std::copy(unit.begin(), unit.end(), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt>
|
||||||
|
OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
|
||||||
|
// This works when wchar_t is UTF-32 because units only contain characters
|
||||||
|
// that have the same representation in UTF-16 and UTF-32.
|
||||||
|
utf8_to_utf16 u(unit);
|
||||||
|
return std::copy(u.c_str(), u.c_str() + u.size(), out);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char, typename Period, typename OutputIt>
|
template <typename Char, typename Period, typename OutputIt>
|
||||||
OutputIt format_duration_unit(OutputIt out) {
|
OutputIt format_duration_unit(OutputIt out) {
|
||||||
if (const char* unit = get_units<Period>()) {
|
if (const char* unit = get_units<Period>())
|
||||||
string_view s(unit);
|
return copy_unit(string_view(unit), out, Char());
|
||||||
if (const_check(std::is_same<Char, wchar_t>())) {
|
|
||||||
utf8_to_utf16 u(s);
|
|
||||||
return std::copy(u.c_str(), u.c_str() + u.size(), out);
|
|
||||||
}
|
|
||||||
return std::copy(s.begin(), s.end(), out);
|
|
||||||
}
|
|
||||||
const Char num_f[] = {'[', '{', '}', ']', 's', 0};
|
const Char num_f[] = {'[', '{', '}', ']', 's', 0};
|
||||||
if (Period::den == 1) return format_to(out, num_f, Period::num);
|
if (const_check(Period::den == 1)) return format_to(out, num_f, Period::num);
|
||||||
const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0};
|
const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0};
|
||||||
return format_to(out, num_def_f, Period::num, Period::den);
|
return format_to(out, num_def_f, Period::num, Period::den);
|
||||||
}
|
}
|
||||||
@ -874,9 +880,9 @@ struct chrono_formatter {
|
|||||||
if (isnan(value)) return write_nan();
|
if (isnan(value)) return write_nan();
|
||||||
uint32_or_64_or_128_t<int> n =
|
uint32_or_64_or_128_t<int> n =
|
||||||
to_unsigned(to_nonnegative_int(value, max_value<int>()));
|
to_unsigned(to_nonnegative_int(value, max_value<int>()));
|
||||||
int num_digits = internal::count_digits(n);
|
int num_digits = detail::count_digits(n);
|
||||||
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
|
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
|
||||||
out = format_decimal<char_type>(out, n, num_digits);
|
out = format_decimal<char_type>(out, n, num_digits).end;
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_nan() { std::copy_n("nan", 3, out); }
|
void write_nan() { std::copy_n("nan", 3, out); }
|
||||||
@ -1004,14 +1010,14 @@ struct chrono_formatter {
|
|||||||
out = format_duration_unit<char_type, Period>(out);
|
out = format_duration_unit<char_type, Period>(out);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace internal
|
} // namespace detail
|
||||||
|
|
||||||
template <typename Rep, typename Period, typename Char>
|
template <typename Rep, typename Period, typename Char>
|
||||||
struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
||||||
private:
|
private:
|
||||||
basic_format_specs<Char> specs;
|
basic_format_specs<Char> specs;
|
||||||
int precision;
|
int precision;
|
||||||
using arg_ref_type = internal::arg_ref<Char>;
|
using arg_ref_type = detail::arg_ref<Char>;
|
||||||
arg_ref_type width_ref;
|
arg_ref_type width_ref;
|
||||||
arg_ref_type precision_ref;
|
arg_ref_type precision_ref;
|
||||||
mutable basic_string_view<Char> format_str;
|
mutable basic_string_view<Char> format_str;
|
||||||
@ -1032,7 +1038,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
|||||||
return arg_ref_type(arg_id);
|
return arg_ref_type(arg_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
|
FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) {
|
||||||
return arg_ref_type(context.next_arg_id());
|
return arg_ref_type(context.next_arg_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1062,17 +1068,17 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
|||||||
auto begin = ctx.begin(), end = ctx.end();
|
auto begin = ctx.begin(), end = ctx.end();
|
||||||
if (begin == end || *begin == '}') return {begin, begin};
|
if (begin == end || *begin == '}') return {begin, begin};
|
||||||
spec_handler handler{*this, ctx, format_str};
|
spec_handler handler{*this, ctx, format_str};
|
||||||
begin = internal::parse_align(begin, end, handler);
|
begin = detail::parse_align(begin, end, handler);
|
||||||
if (begin == end) return {begin, begin};
|
if (begin == end) return {begin, begin};
|
||||||
begin = internal::parse_width(begin, end, handler);
|
begin = detail::parse_width(begin, end, handler);
|
||||||
if (begin == end) return {begin, begin};
|
if (begin == end) return {begin, begin};
|
||||||
if (*begin == '.') {
|
if (*begin == '.') {
|
||||||
if (std::is_floating_point<Rep>::value)
|
if (std::is_floating_point<Rep>::value)
|
||||||
begin = internal::parse_precision(begin, end, handler);
|
begin = detail::parse_precision(begin, end, handler);
|
||||||
else
|
else
|
||||||
handler.on_error("precision not allowed for this argument type");
|
handler.on_error("precision not allowed for this argument type");
|
||||||
}
|
}
|
||||||
end = parse_chrono_format(begin, end, internal::chrono_format_checker());
|
end = parse_chrono_format(begin, end, detail::chrono_format_checker());
|
||||||
return {begin, end};
|
return {begin, end};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1083,7 +1089,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
|||||||
-> decltype(ctx.begin()) {
|
-> decltype(ctx.begin()) {
|
||||||
auto range = do_parse(ctx);
|
auto range = do_parse(ctx);
|
||||||
format_str = basic_string_view<Char>(
|
format_str = basic_string_view<Char>(
|
||||||
&*range.begin, internal::to_unsigned(range.end - range.begin));
|
&*range.begin, detail::to_unsigned(range.end - range.begin));
|
||||||
return range.end;
|
return range.end;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1094,23 +1100,21 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
|||||||
// is not specified.
|
// is not specified.
|
||||||
basic_memory_buffer<Char> buf;
|
basic_memory_buffer<Char> buf;
|
||||||
auto out = std::back_inserter(buf);
|
auto out = std::back_inserter(buf);
|
||||||
using range = internal::output_range<decltype(ctx.out()), Char>;
|
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref,
|
||||||
internal::basic_writer<range> w(range(ctx.out()));
|
ctx);
|
||||||
internal::handle_dynamic_spec<internal::width_checker>(specs.width,
|
detail::handle_dynamic_spec<detail::precision_checker>(precision,
|
||||||
width_ref, ctx);
|
precision_ref, ctx);
|
||||||
internal::handle_dynamic_spec<internal::precision_checker>(
|
|
||||||
precision, precision_ref, ctx);
|
|
||||||
if (begin == end || *begin == '}') {
|
if (begin == end || *begin == '}') {
|
||||||
out = internal::format_duration_value<Char>(out, d.count(), precision);
|
out = detail::format_duration_value<Char>(out, d.count(), precision);
|
||||||
internal::format_duration_unit<Char, Period>(out);
|
detail::format_duration_unit<Char, Period>(out);
|
||||||
} else {
|
} else {
|
||||||
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
|
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
|
||||||
ctx, out, d);
|
ctx, out, d);
|
||||||
f.precision = precision;
|
f.precision = precision;
|
||||||
parse_chrono_format(begin, end, f);
|
parse_chrono_format(begin, end, f);
|
||||||
}
|
}
|
||||||
w.write(buf.data(), buf.size(), specs);
|
return detail::write(
|
||||||
return w.out();
|
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -198,7 +198,7 @@ struct rgb {
|
|||||||
uint8_t b;
|
uint8_t b;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace internal {
|
namespace detail {
|
||||||
|
|
||||||
// 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 {
|
||||||
@ -221,7 +221,7 @@ struct color_type {
|
|||||||
uint32_t rgb_color;
|
uint32_t rgb_color;
|
||||||
} value;
|
} value;
|
||||||
};
|
};
|
||||||
} // namespace internal
|
} // namespace detail
|
||||||
|
|
||||||
// Experimental text formatting support.
|
// Experimental text formatting support.
|
||||||
class text_style {
|
class text_style {
|
||||||
@ -298,11 +298,11 @@ class text_style {
|
|||||||
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
||||||
return static_cast<uint8_t>(ems) != 0;
|
return static_cast<uint8_t>(ems) != 0;
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
|
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_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 internal::color_type get_background() const FMT_NOEXCEPT {
|
FMT_CONSTEXPR detail::color_type get_background() const FMT_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;
|
||||||
}
|
}
|
||||||
@ -313,7 +313,7 @@ class text_style {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
FMT_CONSTEXPR text_style(bool is_foreground,
|
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||||
internal::color_type text_color) FMT_NOEXCEPT
|
detail::color_type text_color) FMT_NOEXCEPT
|
||||||
: set_foreground_color(),
|
: set_foreground_color(),
|
||||||
set_background_color(),
|
set_background_color(),
|
||||||
ems() {
|
ems() {
|
||||||
@ -326,23 +326,23 @@ class text_style {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
|
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
|
||||||
FMT_NOEXCEPT;
|
FMT_NOEXCEPT;
|
||||||
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
|
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
|
||||||
FMT_NOEXCEPT;
|
FMT_NOEXCEPT;
|
||||||
|
|
||||||
internal::color_type foreground_color;
|
detail::color_type foreground_color;
|
||||||
internal::color_type background_color;
|
detail::color_type background_color;
|
||||||
bool set_foreground_color;
|
bool set_foreground_color;
|
||||||
bool set_background_color;
|
bool set_background_color;
|
||||||
emphasis ems;
|
emphasis ems;
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
|
FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
|
||||||
return text_style(/*is_foreground=*/true, foreground);
|
return text_style(/*is_foreground=*/true, foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
|
FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT {
|
||||||
return text_style(/*is_foreground=*/false, background);
|
return text_style(/*is_foreground=*/false, background);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,21 +350,21 @@ FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
|
|||||||
return text_style(lhs) | rhs;
|
return text_style(lhs) | rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace internal {
|
namespace detail {
|
||||||
|
|
||||||
template <typename Char> struct ansi_color_escape {
|
template <typename Char> struct ansi_color_escape {
|
||||||
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
|
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
|
||||||
const char* esc) FMT_NOEXCEPT {
|
const char* esc) FMT_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) {
|
||||||
bool is_background = esc == internal::data::background_color;
|
bool is_background = esc == detail::data::background_color;
|
||||||
uint32_t value = text_color.value.term_color;
|
uint32_t value = text_color.value.term_color;
|
||||||
// Background ASCII codes are the same as the foreground ones but with
|
// Background ASCII codes are the same as the foreground ones but with
|
||||||
// 10 more.
|
// 10 more.
|
||||||
if (is_background) value += 10u;
|
if (is_background) value += 10u;
|
||||||
|
|
||||||
std::size_t index = 0;
|
size_t index = 0;
|
||||||
buffer[index++] = static_cast<Char>('\x1b');
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
buffer[index++] = static_cast<Char>('[');
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
|
||||||
@ -398,7 +398,7 @@ template <typename Char> struct ansi_color_escape {
|
|||||||
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
||||||
em_codes[3] = 9;
|
em_codes[3] = 9;
|
||||||
|
|
||||||
std::size_t index = 0;
|
size_t index = 0;
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
if (!em_codes[i]) continue;
|
if (!em_codes[i]) continue;
|
||||||
buffer[index++] = static_cast<Char>('\x1b');
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
@ -429,14 +429,14 @@ template <typename Char> struct ansi_color_escape {
|
|||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
||||||
internal::color_type foreground) FMT_NOEXCEPT {
|
detail::color_type foreground) FMT_NOEXCEPT {
|
||||||
return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
|
return ansi_color_escape<Char>(foreground, detail::data::foreground_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
||||||
internal::color_type background) FMT_NOEXCEPT {
|
detail::color_type background) FMT_NOEXCEPT {
|
||||||
return ansi_color_escape<Char>(background, internal::data::background_color);
|
return ansi_color_escape<Char>(background, detail::data::background_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
@ -455,11 +455,11 @@ inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
|
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
|
||||||
fputs(internal::data::reset_color, stream);
|
fputs(detail::data::reset_color, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
||||||
fputs(internal::data::wreset_color, stream);
|
fputs(detail::data::wreset_color, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
@ -476,33 +476,31 @@ void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
|
|||||||
bool has_style = false;
|
bool has_style = false;
|
||||||
if (ts.has_emphasis()) {
|
if (ts.has_emphasis()) {
|
||||||
has_style = true;
|
has_style = true;
|
||||||
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
|
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
|
||||||
buf.append(emphasis.begin(), emphasis.end());
|
buf.append(emphasis.begin(), emphasis.end());
|
||||||
}
|
}
|
||||||
if (ts.has_foreground()) {
|
if (ts.has_foreground()) {
|
||||||
has_style = true;
|
has_style = true;
|
||||||
auto foreground =
|
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
|
||||||
internal::make_foreground_color<Char>(ts.get_foreground());
|
|
||||||
buf.append(foreground.begin(), foreground.end());
|
buf.append(foreground.begin(), foreground.end());
|
||||||
}
|
}
|
||||||
if (ts.has_background()) {
|
if (ts.has_background()) {
|
||||||
has_style = true;
|
has_style = true;
|
||||||
auto background =
|
auto background = detail::make_background_color<Char>(ts.get_background());
|
||||||
internal::make_background_color<Char>(ts.get_background());
|
|
||||||
buf.append(background.begin(), background.end());
|
buf.append(background.begin(), background.end());
|
||||||
}
|
}
|
||||||
internal::vformat_to(buf, format_str, args);
|
detail::vformat_to(buf, format_str, args);
|
||||||
if (has_style) internal::reset_color<Char>(buf);
|
if (has_style) detail::reset_color<Char>(buf);
|
||||||
}
|
}
|
||||||
} // namespace internal
|
} // namespace detail
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
basic_format_args<buffer_context<Char>> args) {
|
||||||
basic_memory_buffer<Char> buf;
|
basic_memory_buffer<Char> buf;
|
||||||
internal::vformat_to(buf, ts, to_string_view(format), args);
|
detail::vformat_to(buf, ts, to_string_view(format), args);
|
||||||
buf.push_back(Char(0));
|
buf.push_back(Char(0));
|
||||||
internal::fputs(buf.data(), f);
|
detail::fputs(buf.data(), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -513,10 +511,10 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
|
|||||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||||
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) {
|
||||||
internal::check_format_string<Args...>(format_str);
|
detail::check_format_string<Args...>(format_str);
|
||||||
using context = buffer_context<char_t<S>>;
|
using context = buffer_context<char_t<S>>;
|
||||||
format_arg_store<context, Args...> as{args...};
|
format_arg_store<context, Args...> as{args...};
|
||||||
vprint(f, ts, format_str, basic_format_args<context>(as));
|
vprint(f, ts, format_str, basic_format_args<context>(as));
|
||||||
@ -530,7 +528,7 @@ void print(std::FILE* f, const text_style& ts, const S& format_str,
|
|||||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||||
void print(const text_style& ts, const S& format_str, const Args&... args) {
|
void print(const text_style& ts, const S& format_str, const Args&... args) {
|
||||||
return print(stdout, ts, format_str, args...);
|
return print(stdout, ts, format_str, args...);
|
||||||
}
|
}
|
||||||
@ -540,7 +538,7 @@ inline std::basic_string<Char> vformat(
|
|||||||
const text_style& ts, const S& format_str,
|
const text_style& ts, const S& format_str,
|
||||||
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;
|
||||||
internal::vformat_to(buf, ts, to_string_view(format_str), args);
|
detail::vformat_to(buf, ts, to_string_view(format_str), args);
|
||||||
return fmt::to_string(buf);
|
return fmt::to_string(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -560,7 +558,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 vformat(ts, to_string_view(format_str),
|
return vformat(ts, to_string_view(format_str),
|
||||||
internal::make_args_checked<Args...>(format_str, args...));
|
detail::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
@ -13,7 +13,33 @@
|
|||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace internal {
|
namespace detail {
|
||||||
|
|
||||||
|
// A compile-time string which is compiled into fast formatting code.
|
||||||
|
class compiled_string {};
|
||||||
|
|
||||||
|
template <typename S>
|
||||||
|
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Converts a string literal *s* into a format string that will be parsed at
|
||||||
|
compile time and converted into efficient formatting code. Requires C++17
|
||||||
|
``constexpr if`` compiler support.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
// Converts 42 into std::string using the most efficient method and no
|
||||||
|
// runtime format string processing.
|
||||||
|
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string)
|
||||||
|
|
||||||
|
template <typename T, typename... Tail>
|
||||||
|
const T& first(const T& value, const Tail&...) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
// Part of a compiled format string. It can be either literal text or a
|
// Part of a compiled format string. It can be either literal text or a
|
||||||
// replacement field.
|
// replacement field.
|
||||||
@ -62,13 +88,15 @@ template <typename Char> struct part_counter {
|
|||||||
if (begin != end) ++num_parts;
|
if (begin != end) ++num_parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
|
FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; }
|
||||||
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; }
|
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
|
||||||
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
|
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
|
||||||
|
return ++num_parts, 0;
|
||||||
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
|
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
|
||||||
|
|
||||||
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
|
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
|
||||||
const Char* end) {
|
const Char* end) {
|
||||||
// Find the matching brace.
|
// Find the matching brace.
|
||||||
unsigned brace_counter = 0;
|
unsigned brace_counter = 0;
|
||||||
@ -116,25 +144,28 @@ class format_string_compiler : public error_handler {
|
|||||||
handler_(part::make_text({begin, to_unsigned(end - begin)}));
|
handler_(part::make_text({begin, to_unsigned(end - begin)}));
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_arg_id() {
|
FMT_CONSTEXPR int on_arg_id() {
|
||||||
part_ = part::make_arg_index(parse_context_.next_arg_id());
|
part_ = part::make_arg_index(parse_context_.next_arg_id());
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_arg_id(int id) {
|
FMT_CONSTEXPR int on_arg_id(int id) {
|
||||||
parse_context_.check_arg_id(id);
|
parse_context_.check_arg_id(id);
|
||||||
part_ = part::make_arg_index(id);
|
part_ = part::make_arg_index(id);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
|
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
|
||||||
part_ = part::make_arg_name(id);
|
part_ = part::make_arg_name(id);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
|
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
|
||||||
part_.arg_id_end = ptr;
|
part_.arg_id_end = ptr;
|
||||||
handler_(part_);
|
handler_(part_);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
|
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
|
||||||
const Char* end) {
|
const Char* end) {
|
||||||
auto repl = typename part::replacement();
|
auto repl = typename part::replacement();
|
||||||
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
|
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
|
||||||
@ -160,23 +191,24 @@ FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
|
|||||||
format_string_compiler<Char, PartHandler>(format_str, handler));
|
format_string_compiler<Char, PartHandler>(format_str, handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Range, typename Context, typename Id>
|
template <typename OutputIt, typename Context, typename Id>
|
||||||
void format_arg(
|
void format_arg(
|
||||||
basic_format_parse_context<typename Range::value_type>& parse_ctx,
|
basic_format_parse_context<typename Context::char_type>& parse_ctx,
|
||||||
Context& ctx, Id arg_id) {
|
Context& ctx, Id arg_id) {
|
||||||
ctx.advance_to(
|
ctx.advance_to(visit_format_arg(
|
||||||
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
|
arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx),
|
||||||
|
ctx.arg(arg_id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// vformat_to is defined in a subnamespace to prevent ADL.
|
// vformat_to is defined in a subnamespace to prevent ADL.
|
||||||
namespace cf {
|
namespace cf {
|
||||||
template <typename Context, typename Range, typename CompiledFormat>
|
template <typename Context, typename OutputIt, typename CompiledFormat>
|
||||||
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
|
auto vformat_to(OutputIt out, CompiledFormat& cf,
|
||||||
-> typename Context::iterator {
|
basic_format_args<Context> args) -> typename Context::iterator {
|
||||||
using char_type = typename Context::char_type;
|
using char_type = typename Context::char_type;
|
||||||
basic_format_parse_context<char_type> parse_ctx(
|
basic_format_parse_context<char_type> parse_ctx(
|
||||||
to_string_view(cf.format_str_));
|
to_string_view(cf.format_str_));
|
||||||
Context ctx(out.begin(), args);
|
Context ctx(out, args);
|
||||||
|
|
||||||
const auto& parts = cf.parts();
|
const auto& parts = cf.parts();
|
||||||
for (auto part_it = std::begin(parts); part_it != std::end(parts);
|
for (auto part_it = std::begin(parts); part_it != std::end(parts);
|
||||||
@ -197,12 +229,12 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
|
|||||||
|
|
||||||
case format_part_t::kind::arg_index:
|
case format_part_t::kind::arg_index:
|
||||||
advance_to(parse_ctx, part.arg_id_end);
|
advance_to(parse_ctx, part.arg_id_end);
|
||||||
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
|
detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case format_part_t::kind::arg_name:
|
case format_part_t::kind::arg_name:
|
||||||
advance_to(parse_ctx, part.arg_id_end);
|
advance_to(parse_ctx, part.arg_id_end);
|
||||||
internal::format_arg<Range>(parse_ctx, ctx, value.str);
|
detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case format_part_t::kind::replacement: {
|
case format_part_t::kind::replacement: {
|
||||||
@ -226,7 +258,9 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
|
|||||||
|
|
||||||
advance_to(parse_ctx, part.arg_id_end);
|
advance_to(parse_ctx, part.arg_id_end);
|
||||||
ctx.advance_to(
|
ctx.advance_to(
|
||||||
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
|
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
|
||||||
|
ctx, nullptr, &specs),
|
||||||
|
arg));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,7 +274,7 @@ struct basic_compiled_format {};
|
|||||||
template <typename S, typename = void>
|
template <typename S, typename = void>
|
||||||
struct compiled_format_base : basic_compiled_format {
|
struct compiled_format_base : basic_compiled_format {
|
||||||
using char_type = char_t<S>;
|
using char_type = char_t<S>;
|
||||||
using parts_container = std::vector<internal::format_part<char_type>>;
|
using parts_container = std::vector<detail::format_part<char_type>>;
|
||||||
|
|
||||||
parts_container compiled_parts;
|
parts_container compiled_parts;
|
||||||
|
|
||||||
@ -305,7 +339,7 @@ struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
|
|||||||
const parts_container& parts() const {
|
const parts_container& parts() const {
|
||||||
static FMT_CONSTEXPR_DECL const auto compiled_parts =
|
static FMT_CONSTEXPR_DECL const auto compiled_parts =
|
||||||
compile_to_parts<char_type, num_format_parts>(
|
compile_to_parts<char_type, num_format_parts>(
|
||||||
internal::to_string_view(S()));
|
detail::to_string_view(S()));
|
||||||
return compiled_parts.data;
|
return compiled_parts.data;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -318,8 +352,8 @@ class compiled_format : private compiled_format_base<S> {
|
|||||||
private:
|
private:
|
||||||
basic_string_view<char_type> format_str_;
|
basic_string_view<char_type> format_str_;
|
||||||
|
|
||||||
template <typename Context, typename Range, typename CompiledFormat>
|
template <typename Context, typename OutputIt, typename CompiledFormat>
|
||||||
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
|
friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
|
||||||
basic_format_args<Context> args) ->
|
basic_format_args<Context> args) ->
|
||||||
typename Context::iterator;
|
typename Context::iterator;
|
||||||
|
|
||||||
@ -359,8 +393,7 @@ template <typename Char> struct text {
|
|||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... Args>
|
||||||
OutputIt format(OutputIt out, const Args&...) const {
|
OutputIt format(OutputIt out, const Args&...) const {
|
||||||
// TODO: reserve
|
return write<Char>(out, data);
|
||||||
return copy_str<Char>(data.begin(), data.end(), out);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -373,33 +406,6 @@ constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
|||||||
return {{&s[pos], size}};
|
return {{&s[pos], size}};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename OutputIt, typename T,
|
|
||||||
std::enable_if_t<std::is_integral_v<T>, int> = 0>
|
|
||||||
OutputIt format_default(OutputIt out, T value) {
|
|
||||||
// TODO: reserve
|
|
||||||
format_int fi(value);
|
|
||||||
return std::copy(fi.data(), fi.data() + fi.size(), out);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename OutputIt>
|
|
||||||
OutputIt format_default(OutputIt out, double value) {
|
|
||||||
writer w(out);
|
|
||||||
w.write(value);
|
|
||||||
return w.out();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename OutputIt>
|
|
||||||
OutputIt format_default(OutputIt out, Char value) {
|
|
||||||
*out++ = value;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename OutputIt>
|
|
||||||
OutputIt format_default(OutputIt out, const Char* value) {
|
|
||||||
auto length = std::char_traits<Char>::length(value);
|
|
||||||
return copy_str<Char>(value, value + length, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A replacement field that refers to argument N.
|
// A replacement field that refers to argument N.
|
||||||
template <typename Char, typename T, int N> struct field {
|
template <typename Char, typename T, int N> struct field {
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
@ -408,13 +414,30 @@ template <typename Char, typename T, int N> struct field {
|
|||||||
OutputIt format(OutputIt out, const Args&... args) const {
|
OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
// This ensures that the argument type is convertile to `const T&`.
|
// This ensures that the argument type is convertile to `const T&`.
|
||||||
const T& arg = get<N>(args...);
|
const T& arg = get<N>(args...);
|
||||||
return format_default<Char>(out, arg);
|
return write<Char>(out, arg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename T, int N>
|
template <typename Char, typename T, int N>
|
||||||
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
||||||
|
|
||||||
|
// A replacement field that refers to argument N and has format specifiers.
|
||||||
|
template <typename Char, typename T, int N> struct spec_field {
|
||||||
|
using char_type = Char;
|
||||||
|
mutable formatter<T, Char> fmt;
|
||||||
|
|
||||||
|
template <typename OutputIt, typename... Args>
|
||||||
|
OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
|
// This ensures that the argument type is convertile to `const T&`.
|
||||||
|
const T& arg = get<N>(args...);
|
||||||
|
basic_format_context<OutputIt, Char> ctx(out, {});
|
||||||
|
return fmt.format(arg, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename T, int N>
|
||||||
|
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
|
||||||
|
|
||||||
template <typename L, typename R> struct concat {
|
template <typename L, typename R> struct concat {
|
||||||
L lhs;
|
L lhs;
|
||||||
R rhs;
|
R rhs;
|
||||||
@ -450,7 +473,8 @@ constexpr auto compile_format_string(S format_str);
|
|||||||
|
|
||||||
template <typename Args, size_t POS, int ID, typename T, typename S>
|
template <typename Args, size_t POS, int ID, typename T, typename S>
|
||||||
constexpr auto parse_tail(T head, S format_str) {
|
constexpr auto parse_tail(T head, S format_str) {
|
||||||
if constexpr (POS != to_string_view(format_str).size()) {
|
if constexpr (POS !=
|
||||||
|
basic_string_view<typename S::char_type>(format_str).size()) {
|
||||||
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
|
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
|
||||||
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
|
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
|
||||||
unknown_format>())
|
unknown_format>())
|
||||||
@ -462,6 +486,21 @@ constexpr auto parse_tail(T head, S format_str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Char> struct parse_specs_result {
|
||||||
|
formatter<T, Char> fmt;
|
||||||
|
size_t end;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Char>
|
||||||
|
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
||||||
|
size_t pos) {
|
||||||
|
str.remove_prefix(pos);
|
||||||
|
auto ctx = basic_format_parse_context<Char>(str);
|
||||||
|
auto f = formatter<T, Char>();
|
||||||
|
auto end = f.parse(ctx);
|
||||||
|
return {f, pos + (end - str.data()) + 1};
|
||||||
|
}
|
||||||
|
|
||||||
// Compiles a non-empty format string and returns the compiled representation
|
// Compiles a non-empty format string and returns the compiled representation
|
||||||
// or unknown_format() on unrecognized input.
|
// or unknown_format() on unrecognized input.
|
||||||
template <typename Args, size_t POS, int ID, typename S>
|
template <typename Args, size_t POS, int ID, typename S>
|
||||||
@ -475,12 +514,13 @@ constexpr auto compile_format_string(S format_str) {
|
|||||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
} else if constexpr (str[POS + 1] == '}') {
|
} else if constexpr (str[POS + 1] == '}') {
|
||||||
using type = get_type<ID, Args>;
|
using type = get_type<ID, Args>;
|
||||||
if constexpr (std::is_same<type, int>::value) {
|
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
|
||||||
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
|
format_str);
|
||||||
format_str);
|
} else if constexpr (str[POS + 1] == ':') {
|
||||||
} else {
|
using type = get_type<ID, Args>;
|
||||||
return unknown_format();
|
constexpr auto result = parse_specs<type>(str, POS + 2);
|
||||||
}
|
return parse_tail<Args, result.end, ID + 1>(
|
||||||
|
spec_field<char_type, type, ID>{result.fmt}, format_str);
|
||||||
} else {
|
} else {
|
||||||
return unknown_format();
|
return unknown_format();
|
||||||
}
|
}
|
||||||
@ -494,100 +534,130 @@ constexpr auto compile_format_string(S format_str) {
|
|||||||
format_str);
|
format_str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // __cpp_if_constexpr
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
#if FMT_USE_CONSTEXPR
|
|
||||||
# ifdef __cpp_if_constexpr
|
|
||||||
template <typename... Args, typename S,
|
template <typename... Args, typename S,
|
||||||
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
FMT_ENABLE_IF(is_compile_string<S>::value ||
|
||||||
|
detail::is_compiled_string<S>::value)>
|
||||||
constexpr auto compile(S format_str) {
|
constexpr auto compile(S format_str) {
|
||||||
constexpr basic_string_view<typename S::char_type> str = format_str;
|
constexpr basic_string_view<typename S::char_type> str = format_str;
|
||||||
if constexpr (str.size() == 0) {
|
if constexpr (str.size() == 0) {
|
||||||
return internal::make_text(str, 0, 0);
|
return detail::make_text(str, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
constexpr auto result =
|
constexpr auto result =
|
||||||
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
|
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
|
||||||
format_str);
|
format_str);
|
||||||
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
|
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
|
||||||
internal::unknown_format>()) {
|
detail::unknown_format>()) {
|
||||||
return internal::compiled_format<S, Args...>(to_string_view(format_str));
|
return detail::compiled_format<S, Args...>(to_string_view(format_str));
|
||||||
} else {
|
} else {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
template <typename CompiledFormat, typename... Args,
|
|
||||||
typename Char = typename CompiledFormat::char_type,
|
|
||||||
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
|
|
||||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
cf.format(std::back_inserter(buffer), args...);
|
|
||||||
return to_string(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
|
||||||
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
|
|
||||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
|
||||||
const Args&... args) {
|
|
||||||
return cf.format(out, args...);
|
|
||||||
}
|
|
||||||
# else
|
|
||||||
template <typename... Args, typename S,
|
template <typename... Args, typename S,
|
||||||
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||||
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
|
constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
|
||||||
return internal::compiled_format<S, Args...>(to_string_view(format_str));
|
return detail::compiled_format<S, Args...>(to_string_view(format_str));
|
||||||
}
|
}
|
||||||
# endif // __cpp_if_constexpr
|
#endif // __cpp_if_constexpr
|
||||||
#endif // FMT_USE_CONSTEXPR
|
|
||||||
|
|
||||||
// Compiles the format string which must be a string literal.
|
// Compiles the format string which must be a string literal.
|
||||||
template <typename... Args, typename Char, size_t N>
|
template <typename... Args, typename Char, size_t N>
|
||||||
auto compile(const Char (&format_str)[N])
|
auto compile(const Char (&format_str)[N])
|
||||||
-> internal::compiled_format<const Char*, Args...> {
|
-> detail::compiled_format<const Char*, Args...> {
|
||||||
return internal::compiled_format<const Char*, Args...>(
|
return detail::compiled_format<const Char*, Args...>(
|
||||||
basic_string_view<Char>(format_str, N - 1));
|
basic_string_view<Char>(format_str, N - 1));
|
||||||
}
|
}
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
// DEPRECATED! use FMT_COMPILE instead.
|
||||||
|
template <typename... Args>
|
||||||
|
FMT_DEPRECATED auto compile(const Args&... args)
|
||||||
|
-> decltype(detail::compile(args...)) {
|
||||||
|
return detail::compile(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FMT_USE_CONSTEXPR
|
||||||
|
# ifdef __cpp_if_constexpr
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args,
|
template <typename CompiledFormat, typename... Args,
|
||||||
typename Char = typename CompiledFormat::char_type,
|
typename Char = typename CompiledFormat::char_type,
|
||||||
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
|
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||||
CompiledFormat>::value)>
|
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
||||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
const Args&... args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
using range = buffer_range<Char>;
|
detail::buffer<Char>& base = buffer;
|
||||||
using context = buffer_context<Char>;
|
cf.format(std::back_inserter(base), args...);
|
||||||
internal::cf::vformat_to<context>(range(buffer), cf,
|
|
||||||
make_format_args<context>(args...));
|
|
||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||||
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
|
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||||
|
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||||
|
const Args&... args) {
|
||||||
|
return cf.format(out, args...);
|
||||||
|
}
|
||||||
|
# endif // __cpp_if_constexpr
|
||||||
|
#endif // FMT_USE_CONSTEXPR
|
||||||
|
|
||||||
|
template <typename CompiledFormat, typename... Args,
|
||||||
|
typename Char = typename CompiledFormat::char_type,
|
||||||
|
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
|
||||||
|
CompiledFormat>::value)>
|
||||||
|
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
using context = buffer_context<Char>;
|
||||||
|
detail::buffer<Char>& base = buffer;
|
||||||
|
detail::cf::vformat_to<context>(std::back_inserter(base), cf,
|
||||||
|
make_format_args<context>(args...));
|
||||||
|
return to_string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
|
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
||||||
|
Args&&... args) {
|
||||||
|
constexpr basic_string_view<typename S::char_type> str = S();
|
||||||
|
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
|
||||||
|
return fmt::to_string(detail::first(args...));
|
||||||
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
|
return format(compiled, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||||
|
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
|
||||||
CompiledFormat>::value)>
|
CompiledFormat>::value)>
|
||||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
using char_type = typename CompiledFormat::char_type;
|
using char_type = typename CompiledFormat::char_type;
|
||||||
using range = internal::output_range<OutputIt, char_type>;
|
|
||||||
using context = format_context_t<OutputIt, char_type>;
|
using context = format_context_t<OutputIt, char_type>;
|
||||||
return internal::cf::vformat_to<context>(range(out), cf,
|
return detail::cf::vformat_to<context>(out, cf,
|
||||||
make_format_args<context>(args...));
|
make_format_args<context>(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
|
OutputIt format_to(OutputIt out, const S&, const Args&... args) {
|
||||||
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
|
return format_to(out, compiled, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename OutputIt, typename CompiledFormat, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&& std::is_base_of<
|
||||||
|
detail::basic_compiled_format, CompiledFormat>::value)>
|
||||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||||
const CompiledFormat& cf,
|
const CompiledFormat& cf,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
auto it =
|
auto it =
|
||||||
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
|
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
|
||||||
return {it.base(), it.count()};
|
return {it.base(), it.count()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args>
|
template <typename CompiledFormat, typename... Args>
|
||||||
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
||||||
return format_to(internal::counting_iterator(), cf, args...).count();
|
return format_to(detail::counting_iterator(), cf, args...).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -15,6 +15,7 @@
|
|||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstring> // for std::memmove
|
#include <cstring> // for std::memmove
|
||||||
#include <cwchar>
|
#include <cwchar>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
@ -22,8 +23,16 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
# if !defined(NOMINMAX) && !defined(WIN32_LEAN_AND_MEAN)
|
||||||
|
# define NOMINMAX
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
# include <windows.h>
|
||||||
|
# undef WIN32_LEAN_AND_MEAN
|
||||||
|
# undef NOMINMAX
|
||||||
|
# else
|
||||||
|
# include <windows.h>
|
||||||
|
# endif
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
# include <windows.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
@ -33,15 +42,19 @@
|
|||||||
|
|
||||||
// Dummy implementations of strerror_r and strerror_s called if corresponding
|
// Dummy implementations of strerror_r and strerror_s called if corresponding
|
||||||
// system functions are not available.
|
// system functions are not available.
|
||||||
inline fmt::internal::null<> strerror_r(int, char*, ...) { return {}; }
|
inline fmt::detail::null<> strerror_r(int, char*, ...) { return {}; }
|
||||||
inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) { return {}; }
|
inline fmt::detail::null<> strerror_s(char*, size_t, ...) { return {}; }
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace internal {
|
namespace detail {
|
||||||
|
|
||||||
FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
|
FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
|
||||||
print(stderr, "{}:{}: assertion failed: {}", file, line, message);
|
// Use unchecked std::fprintf to avoid triggering another assertion when
|
||||||
std::abort();
|
// writing to stderr fails
|
||||||
|
std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
|
||||||
|
// Chosen instead of std::abort to satisfy Clang in CUDA mode during device
|
||||||
|
// code pass.
|
||||||
|
std::terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
@ -67,14 +80,14 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
|
|||||||
// other - failure
|
// other - failure
|
||||||
// Buffer should be at least of size 1.
|
// Buffer should be at least of size 1.
|
||||||
FMT_FUNC int safe_strerror(int error_code, char*& buffer,
|
FMT_FUNC int safe_strerror(int error_code, char*& buffer,
|
||||||
std::size_t buffer_size) FMT_NOEXCEPT {
|
size_t buffer_size) FMT_NOEXCEPT {
|
||||||
FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer");
|
FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer");
|
||||||
|
|
||||||
class dispatcher {
|
class dispatcher {
|
||||||
private:
|
private:
|
||||||
int error_code_;
|
int error_code_;
|
||||||
char*& buffer_;
|
char*& buffer_;
|
||||||
std::size_t buffer_size_;
|
size_t buffer_size_;
|
||||||
|
|
||||||
// A noop assignment operator to avoid bogus warnings.
|
// A noop assignment operator to avoid bogus warnings.
|
||||||
void operator=(const dispatcher&) {}
|
void operator=(const dispatcher&) {}
|
||||||
@ -97,7 +110,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
|
|||||||
|
|
||||||
// Handle the case when strerror_r is not available.
|
// Handle the case when strerror_r is not available.
|
||||||
FMT_MAYBE_UNUSED
|
FMT_MAYBE_UNUSED
|
||||||
int handle(internal::null<>) {
|
int handle(detail::null<>) {
|
||||||
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
|
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +124,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
|
|||||||
|
|
||||||
#if !FMT_MSC_VER
|
#if !FMT_MSC_VER
|
||||||
// Fallback to strerror if strerror_r and strerror_s are not available.
|
// Fallback to strerror if strerror_r and strerror_s are not available.
|
||||||
int fallback(internal::null<>) {
|
int fallback(detail::null<>) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
buffer_ = strerror(error_code_);
|
buffer_ = strerror(error_code_);
|
||||||
return errno;
|
return errno;
|
||||||
@ -119,7 +132,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
dispatcher(int err_code, char*& buf, std::size_t buf_size)
|
dispatcher(int err_code, char*& buf, size_t buf_size)
|
||||||
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
|
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
|
||||||
|
|
||||||
int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); }
|
int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); }
|
||||||
@ -127,7 +140,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
|
|||||||
return dispatcher(error_code, buffer, buffer_size).run();
|
return dispatcher(error_code, buffer, buffer_size).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
|
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
|
||||||
string_view message) FMT_NOEXCEPT {
|
string_view message) FMT_NOEXCEPT {
|
||||||
// Report error code making sure that the output fits into
|
// Report error code making sure that the output fits into
|
||||||
// inline_buffer_size to avoid dynamic memory allocation and potential
|
// inline_buffer_size to avoid dynamic memory allocation and potential
|
||||||
@ -136,20 +149,17 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
|
|||||||
static const char SEP[] = ": ";
|
static const char SEP[] = ": ";
|
||||||
static const char ERROR_STR[] = "error ";
|
static const char ERROR_STR[] = "error ";
|
||||||
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
|
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
|
||||||
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
|
size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
|
||||||
auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
|
auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
|
||||||
if (internal::is_negative(error_code)) {
|
if (detail::is_negative(error_code)) {
|
||||||
abs_value = 0 - abs_value;
|
abs_value = 0 - abs_value;
|
||||||
++error_code_size;
|
++error_code_size;
|
||||||
}
|
}
|
||||||
error_code_size += internal::to_unsigned(internal::count_digits(abs_value));
|
error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
|
||||||
internal::writer w(out);
|
auto it = std::back_inserter(out);
|
||||||
if (message.size() <= inline_buffer_size - error_code_size) {
|
if (message.size() <= inline_buffer_size - error_code_size)
|
||||||
w.write(message);
|
format_to(it, "{}{}", message, SEP);
|
||||||
w.write(SEP);
|
format_to(it, "{}{}", ERROR_STR, error_code);
|
||||||
}
|
|
||||||
w.write(ERROR_STR);
|
|
||||||
w.write(error_code);
|
|
||||||
assert(out.size() <= inline_buffer_size);
|
assert(out.size() <= inline_buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,10 +178,10 @@ FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
|
|||||||
size_t written = std::fwrite(ptr, size, count, stream);
|
size_t written = std::fwrite(ptr, size, count, stream);
|
||||||
if (written < count) FMT_THROW(system_error(errno, "cannot write to file"));
|
if (written < count) FMT_THROW(system_error(errno, "cannot write to file"));
|
||||||
}
|
}
|
||||||
} // namespace internal
|
} // namespace detail
|
||||||
|
|
||||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
namespace internal {
|
namespace detail {
|
||||||
|
|
||||||
template <typename Locale>
|
template <typename Locale>
|
||||||
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
|
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
|
||||||
@ -194,18 +204,16 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
|
|||||||
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
|
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
|
||||||
.decimal_point();
|
.decimal_point();
|
||||||
}
|
}
|
||||||
} // namespace internal
|
} // namespace detail
|
||||||
#else
|
#else
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_FUNC std::string internal::grouping_impl(locale_ref) {
|
FMT_FUNC std::string detail::grouping_impl(locale_ref) {
|
||||||
return "\03";
|
return "\03";
|
||||||
}
|
}
|
||||||
template <typename Char>
|
template <typename Char> FMT_FUNC Char detail::thousands_sep_impl(locale_ref) {
|
||||||
FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
|
|
||||||
return FMT_STATIC_THOUSANDS_SEPARATOR;
|
return FMT_STATIC_THOUSANDS_SEPARATOR;
|
||||||
}
|
}
|
||||||
template <typename Char>
|
template <typename Char> FMT_FUNC Char detail::decimal_point_impl(locale_ref) {
|
||||||
FMT_FUNC Char internal::decimal_point_impl(locale_ref) {
|
|
||||||
return '.';
|
return '.';
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -222,9 +230,9 @@ FMT_FUNC void system_error::init(int err_code, string_view format_str,
|
|||||||
base = std::runtime_error(to_string(buffer));
|
base = std::runtime_error(to_string(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace internal {
|
namespace detail {
|
||||||
|
|
||||||
template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) {
|
template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) {
|
||||||
// fallback_uintptr is always stored in little endian.
|
// fallback_uintptr is always stored in little endian.
|
||||||
int i = static_cast<int>(sizeof(void*)) - 1;
|
int i = static_cast<int>(sizeof(void*)) - 1;
|
||||||
while (i > 0 && n.value[i] == 0) --i;
|
while (i > 0 && n.value[i] == 0) --i;
|
||||||
@ -233,12 +241,27 @@ template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const char basic_data<T>::digits[] =
|
const typename basic_data<T>::digit_pair basic_data<T>::digits[] = {
|
||||||
"0001020304050607080910111213141516171819"
|
{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'},
|
||||||
"2021222324252627282930313233343536373839"
|
{'0', '5'}, {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'},
|
||||||
"4041424344454647484950515253545556575859"
|
{'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'},
|
||||||
"6061626364656667686970717273747576777879"
|
{'1', '5'}, {'1', '6'}, {'1', '7'}, {'1', '8'}, {'1', '9'},
|
||||||
"8081828384858687888990919293949596979899";
|
{'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'},
|
||||||
|
{'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
|
||||||
|
{'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'},
|
||||||
|
{'3', '5'}, {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'},
|
||||||
|
{'4', '0'}, {'4', '1'}, {'4', '2'}, {'4', '3'}, {'4', '4'},
|
||||||
|
{'4', '5'}, {'4', '6'}, {'4', '7'}, {'4', '8'}, {'4', '9'},
|
||||||
|
{'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, {'5', '4'},
|
||||||
|
{'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
|
||||||
|
{'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'},
|
||||||
|
{'6', '5'}, {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'},
|
||||||
|
{'7', '0'}, {'7', '1'}, {'7', '2'}, {'7', '3'}, {'7', '4'},
|
||||||
|
{'7', '5'}, {'7', '6'}, {'7', '7'}, {'7', '8'}, {'7', '9'},
|
||||||
|
{'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, {'8', '4'},
|
||||||
|
{'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
|
||||||
|
{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'},
|
||||||
|
{'9', '5'}, {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const char basic_data<T>::hex_digits[] = "0123456789abcdef";
|
const char basic_data<T>::hex_digits[] = "0123456789abcdef";
|
||||||
@ -317,6 +340,10 @@ const char basic_data<T>::background_color[] = "\x1b[48;2;";
|
|||||||
template <typename T> const char basic_data<T>::reset_color[] = "\x1b[0m";
|
template <typename T> const char basic_data<T>::reset_color[] = "\x1b[0m";
|
||||||
template <typename T> const wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m";
|
template <typename T> const wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m";
|
||||||
template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};
|
template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};
|
||||||
|
template <typename T>
|
||||||
|
const char basic_data<T>::left_padding_shifts[] = {31, 31, 0, 1, 0};
|
||||||
|
template <typename T>
|
||||||
|
const char basic_data<T>::right_padding_shifts[] = {0, 31, 0, 1, 0};
|
||||||
|
|
||||||
template <typename T> struct bits {
|
template <typename T> struct bits {
|
||||||
static FMT_CONSTEXPR_DECL const int value =
|
static FMT_CONSTEXPR_DECL const int value =
|
||||||
@ -576,9 +603,10 @@ class bigint {
|
|||||||
void operator=(const bigint&) = delete;
|
void operator=(const bigint&) = delete;
|
||||||
|
|
||||||
void assign(const bigint& other) {
|
void assign(const bigint& other) {
|
||||||
bigits_.resize(other.bigits_.size());
|
auto size = other.bigits_.size();
|
||||||
|
bigits_.resize(size);
|
||||||
auto data = other.bigits_.data();
|
auto data = other.bigits_.data();
|
||||||
std::copy(data, data + other.bigits_.size(), bigits_.data());
|
std::copy(data, data + size, make_checked(bigits_.data(), size));
|
||||||
exp_ = other.exp_;
|
exp_ = other.exp_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,7 +622,7 @@ class bigint {
|
|||||||
|
|
||||||
int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
|
int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
|
||||||
|
|
||||||
bigint& operator<<=(int shift) {
|
FMT_NOINLINE bigint& operator<<=(int shift) {
|
||||||
assert(shift >= 0);
|
assert(shift >= 0);
|
||||||
exp_ += shift / bigit_bits;
|
exp_ += shift / bigit_bits;
|
||||||
shift %= bigit_bits;
|
shift %= bigit_bits;
|
||||||
@ -1125,7 +1153,7 @@ int snprintf_float(T value, int precision, float_specs specs,
|
|||||||
precision = (precision >= 0 ? precision : 6) - 1;
|
precision = (precision >= 0 ? precision : 6) - 1;
|
||||||
|
|
||||||
// Build the format string.
|
// Build the format string.
|
||||||
enum { max_format_size = 7 }; // Ths longest format is "%#.*Le".
|
enum { max_format_size = 7 }; // The longest format is "%#.*Le".
|
||||||
char format[max_format_size];
|
char format[max_format_size];
|
||||||
char* format_ptr = format;
|
char* format_ptr = format;
|
||||||
*format_ptr++ = '%';
|
*format_ptr++ = '%';
|
||||||
@ -1145,13 +1173,13 @@ int snprintf_float(T value, int precision, float_specs specs,
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
auto begin = buf.data() + offset;
|
auto begin = buf.data() + offset;
|
||||||
auto capacity = buf.capacity() - offset;
|
auto capacity = buf.capacity() - offset;
|
||||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
#ifdef FMT_FUZZ
|
||||||
if (precision > 100000)
|
if (precision > 100000)
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"fuzz mode - avoid large allocation inside snprintf");
|
"fuzz mode - avoid large allocation inside snprintf");
|
||||||
#endif
|
#endif
|
||||||
// Suppress the warning about a nonliteral format string.
|
// Suppress the warning about a nonliteral format string.
|
||||||
// Cannot use auto becase of a bug in MinGW (#1532).
|
// Cannot use auto because of a bug in MinGW (#1532).
|
||||||
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
|
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
|
||||||
int result = precision >= 0
|
int result = precision >= 0
|
||||||
? snprintf_ptr(begin, capacity, format, precision, value)
|
? snprintf_ptr(begin, capacity, format, precision, value)
|
||||||
@ -1268,14 +1296,14 @@ FMT_FUNC const char* utf8_decode(const char* buf, uint32_t* c, int* e) {
|
|||||||
|
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
} // namespace internal
|
} // namespace detail
|
||||||
|
|
||||||
template <> struct formatter<internal::bigint> {
|
template <> struct formatter<detail::bigint> {
|
||||||
format_parse_context::iterator parse(format_parse_context& ctx) {
|
format_parse_context::iterator parse(format_parse_context& ctx) {
|
||||||
return ctx.begin();
|
return ctx.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
format_context::iterator format(const internal::bigint& n,
|
format_context::iterator format(const detail::bigint& n,
|
||||||
format_context& ctx) {
|
format_context& ctx) {
|
||||||
auto out = ctx.out();
|
auto out = ctx.out();
|
||||||
bool first = true;
|
bool first = true;
|
||||||
@ -1289,12 +1317,12 @@ template <> struct formatter<internal::bigint> {
|
|||||||
out = format_to(out, "{:08x}", value);
|
out = format_to(out, "{:08x}", value);
|
||||||
}
|
}
|
||||||
if (n.exp_ > 0)
|
if (n.exp_ > 0)
|
||||||
out = format_to(out, "p{}", n.exp_ * internal::bigint::bigit_bits);
|
out = format_to(out, "p{}", n.exp_ * detail::bigint::bigit_bits);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
||||||
auto transcode = [this](const char* p) {
|
auto transcode = [this](const char* p) {
|
||||||
auto cp = uint32_t();
|
auto cp = uint32_t();
|
||||||
auto error = 0;
|
auto error = 0;
|
||||||
@ -1325,7 +1353,7 @@ FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
|||||||
buffer_.push_back(0);
|
buffer_.push_back(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
|
FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
|
||||||
string_view message) FMT_NOEXCEPT {
|
string_view message) FMT_NOEXCEPT {
|
||||||
FMT_TRY {
|
FMT_TRY {
|
||||||
memory_buffer buf;
|
memory_buffer buf;
|
||||||
@ -1333,12 +1361,9 @@ FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
char* system_message = &buf[0];
|
char* system_message = &buf[0];
|
||||||
int result =
|
int result =
|
||||||
internal::safe_strerror(error_code, system_message, buf.size());
|
detail::safe_strerror(error_code, system_message, buf.size());
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
internal::writer w(out);
|
format_to(std::back_inserter(out), "{}: {}", message, system_message);
|
||||||
w.write(message);
|
|
||||||
w.write(": ");
|
|
||||||
w.write(system_message);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (result != ERANGE)
|
if (result != ERANGE)
|
||||||
@ -1350,7 +1375,7 @@ FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
|
|||||||
format_error_code(out, error_code, message);
|
format_error_code(out, error_code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC void internal::error_handler::on_error(const char* message) {
|
FMT_FUNC void detail::error_handler::on_error(const char* message) {
|
||||||
FMT_THROW(format_error(message));
|
FMT_THROW(format_error(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1359,14 +1384,39 @@ FMT_FUNC void report_system_error(int error_code,
|
|||||||
report_error(format_system_error, error_code, message);
|
report_error(format_system_error, error_code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct stringifier {
|
||||||
|
template <typename T> FMT_INLINE std::string operator()(T value) const {
|
||||||
|
return to_string(value);
|
||||||
|
}
|
||||||
|
std::string operator()(basic_format_arg<format_context>::handle h) const {
|
||||||
|
memory_buffer buf;
|
||||||
|
detail::buffer<char>& base = buf;
|
||||||
|
format_parse_context parse_ctx({});
|
||||||
|
format_context format_ctx(std::back_inserter(base), {}, {});
|
||||||
|
h.format(parse_ctx, format_ctx);
|
||||||
|
return to_string(buf);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_FUNC std::string detail::vformat(string_view format_str, format_args args) {
|
||||||
|
if (format_str.size() == 2 && equal2(format_str.data(), "{}")) {
|
||||||
|
auto arg = args.get(0);
|
||||||
|
if (!arg) error_handler().on_error("argument not found");
|
||||||
|
return visit_format_arg(stringifier(), arg);
|
||||||
|
}
|
||||||
|
memory_buffer buffer;
|
||||||
|
detail::vformat_to(buffer, format_str, args);
|
||||||
|
return to_string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
|
FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
|
||||||
memory_buffer buffer;
|
memory_buffer buffer;
|
||||||
internal::vformat_to(buffer, format_str,
|
detail::vformat_to(buffer, format_str,
|
||||||
basic_format_args<buffer_context<char>>(args));
|
basic_format_args<buffer_context<char>>(args));
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
auto fd = _fileno(f);
|
auto fd = _fileno(f);
|
||||||
if (_isatty(fd)) {
|
if (_isatty(fd)) {
|
||||||
internal::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size()));
|
detail::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size()));
|
||||||
auto written = DWORD();
|
auto written = DWORD();
|
||||||
if (!WriteConsoleW(reinterpret_cast<HANDLE>(_get_osfhandle(fd)),
|
if (!WriteConsoleW(reinterpret_cast<HANDLE>(_get_osfhandle(fd)),
|
||||||
u16.c_str(), static_cast<DWORD>(u16.size()), &written,
|
u16.c_str(), static_cast<DWORD>(u16.size()), &written,
|
||||||
@ -1376,16 +1426,16 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
internal::fwrite_fully(buffer.data(), 1, buffer.size(), f);
|
detail::fwrite_fully(buffer.data(), 1, buffer.size(), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Print assuming legacy (non-Unicode) encoding.
|
// Print assuming legacy (non-Unicode) encoding.
|
||||||
FMT_FUNC void internal::vprint_mojibake(std::FILE* f, string_view format_str,
|
FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str,
|
||||||
format_args args) {
|
format_args args) {
|
||||||
memory_buffer buffer;
|
memory_buffer buffer;
|
||||||
internal::vformat_to(buffer, format_str,
|
detail::vformat_to(buffer, format_str,
|
||||||
basic_format_args<buffer_context<char>>(args));
|
basic_format_args<buffer_context<char>>(args));
|
||||||
fwrite_fully(buffer.data(), 1, buffer.size(), f);
|
fwrite_fully(buffer.data(), 1, buffer.size(), f);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -14,15 +14,15 @@
|
|||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
namespace internal {
|
namespace detail {
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
typename buffer_context<Char>::iterator vformat_to(
|
typename buffer_context<Char>::iterator vformat_to(
|
||||||
const std::locale& loc, buffer<Char>& buf,
|
const std::locale& loc, buffer<Char>& buf,
|
||||||
basic_string_view<Char> format_str,
|
basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
using range = buffer_range<Char>;
|
using af = arg_formatter<typename buffer_context<Char>::iterator, Char>;
|
||||||
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
|
return vformat_to<af>(std::back_inserter(buf), to_string_view(format_str),
|
||||||
internal::locale_ref(loc));
|
args, detail::locale_ref(loc));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
@ -30,43 +30,43 @@ std::basic_string<Char> vformat(
|
|||||||
const std::locale& loc, basic_string_view<Char> format_str,
|
const std::locale& loc, basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
internal::vformat_to(loc, buffer, format_str, args);
|
detail::vformat_to(loc, buffer, format_str, args);
|
||||||
return fmt::to_string(buffer);
|
return fmt::to_string(buffer);
|
||||||
}
|
}
|
||||||
} // namespace internal
|
} // namespace detail
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline std::basic_string<Char> vformat(
|
inline std::basic_string<Char> vformat(
|
||||||
const std::locale& loc, const S& format_str,
|
const std::locale& loc, const S& format_str,
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
return internal::vformat(loc, to_string_view(format_str), args);
|
return detail::vformat(loc, to_string_view(format_str), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
inline std::basic_string<Char> format(const std::locale& loc,
|
inline std::basic_string<Char> format(const std::locale& loc,
|
||||||
const S& format_str, Args&&... args) {
|
const S& format_str, Args&&... args) {
|
||||||
return internal::vformat(
|
return detail::vformat(
|
||||||
loc, to_string_view(format_str),
|
loc, to_string_view(format_str),
|
||||||
internal::make_args_checked<Args...>(format_str, args...));
|
detail::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename OutputIt, typename... Args,
|
template <typename S, typename OutputIt, typename... Args,
|
||||||
typename Char = enable_if_t<
|
typename Char = enable_if_t<
|
||||||
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
|
detail::is_output_iterator<OutputIt>::value, char_t<S>>>
|
||||||
inline OutputIt vformat_to(
|
inline OutputIt vformat_to(
|
||||||
OutputIt out, const std::locale& loc, const S& format_str,
|
OutputIt out, const std::locale& loc, const S& format_str,
|
||||||
format_args_t<type_identity_t<OutputIt>, Char> args) {
|
format_args_t<type_identity_t<OutputIt>, Char> args) {
|
||||||
using range = internal::output_range<OutputIt, Char>;
|
using af = detail::arg_formatter<OutputIt, Char>;
|
||||||
return vformat_to<arg_formatter<range>>(
|
return vformat_to<af>(out, to_string_view(format_str), args,
|
||||||
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
detail::locale_ref(loc));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename S, typename... Args,
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&&
|
||||||
internal::is_string<S>::value)>
|
detail::is_string<S>::value)>
|
||||||
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
||||||
const S& format_str, Args&&... args) {
|
const S& format_str, Args&&... args) {
|
||||||
internal::check_format_string<Args...>(format_str);
|
detail::check_format_string<Args...>(format_str);
|
||||||
using context = format_context_t<OutputIt, char_t<S>>;
|
using context = format_context_t<OutputIt, char_t<S>>;
|
||||||
format_arg_store<context, Args...> as{args...};
|
format_arg_store<context, Args...> as{args...};
|
||||||
return vformat_to(out, loc, to_string_view(format_str),
|
return vformat_to(out, loc, to_string_view(format_str),
|
||||||
|
@ -14,10 +14,10 @@
|
|||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
template <typename CHar> class basic_printf_parse_context;
|
template <typename Char> class basic_printf_parse_context;
|
||||||
template <typename OutputIt, typename Char> class basic_printf_context;
|
template <typename OutputIt, typename Char> class basic_printf_context;
|
||||||
|
|
||||||
namespace internal {
|
namespace detail {
|
||||||
|
|
||||||
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
||||||
private:
|
private:
|
||||||
@ -80,7 +80,7 @@ template <typename T, typename Char> class is_streamable {
|
|||||||
|
|
||||||
// Write the content of buf to os.
|
// Write the content of buf to os.
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||||
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();
|
||||||
@ -101,8 +101,8 @@ void format_value(buffer<Char>& buf, const T& value,
|
|||||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
if (loc) output.imbue(loc.get<std::locale>());
|
if (loc) output.imbue(loc.get<std::locale>());
|
||||||
#endif
|
#endif
|
||||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
|
||||||
output << value;
|
output << value;
|
||||||
|
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||||
buf.resize(buf.size());
|
buf.resize(buf.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +110,8 @@ void format_value(buffer<Char>& buf, const T& value,
|
|||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||||
: private formatter<basic_string_view<Char>, Char> {
|
: private formatter<basic_string_view<Char>, Char> {
|
||||||
auto parse(basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
||||||
|
-> decltype(ctx.begin()) {
|
||||||
return formatter<basic_string_view<Char>, Char>::parse(ctx);
|
return formatter<basic_string_view<Char>, Char>::parse(ctx);
|
||||||
}
|
}
|
||||||
template <typename ParseCtx,
|
template <typename ParseCtx,
|
||||||
@ -136,14 +137,14 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
|||||||
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace internal
|
} // namespace detail
|
||||||
|
|
||||||
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<Char> format_str,
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
internal::vformat_to(buffer, format_str, args);
|
detail::vformat_to(buffer, format_str, args);
|
||||||
internal::write(os, buffer);
|
detail::write_buffer(os, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -156,10 +157,10 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
|||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
||||||
vprint(os, to_string_view(format_str),
|
vprint(os, to_string_view(format_str),
|
||||||
internal::make_args_checked<Args...>(format_str, args...));
|
detail::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
#include "os.h"
|
#include "os.h"
|
||||||
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"
|
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
#include "ostream.h"
|
#include "ostream.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace internal {
|
namespace detail {
|
||||||
|
|
||||||
// Checks if a value fits in int - used to avoid warnings about comparing
|
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||||
// signed and unsigned integers.
|
// signed and unsigned integers.
|
||||||
@ -90,11 +90,11 @@ template <typename T, typename Context> class arg_converter {
|
|||||||
if (const_check(sizeof(target_type) <= sizeof(int))) {
|
if (const_check(sizeof(target_type) <= sizeof(int))) {
|
||||||
// Extra casts are used to silence warnings.
|
// Extra casts are used to silence warnings.
|
||||||
if (is_signed) {
|
if (is_signed) {
|
||||||
arg_ = internal::make_arg<Context>(
|
arg_ = detail::make_arg<Context>(
|
||||||
static_cast<int>(static_cast<target_type>(value)));
|
static_cast<int>(static_cast<target_type>(value)));
|
||||||
} else {
|
} else {
|
||||||
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
|
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
|
||||||
arg_ = internal::make_arg<Context>(
|
arg_ = detail::make_arg<Context>(
|
||||||
static_cast<unsigned>(static_cast<unsigned_type>(value)));
|
static_cast<unsigned>(static_cast<unsigned_type>(value)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -102,9 +102,9 @@ template <typename T, typename Context> class arg_converter {
|
|||||||
// glibc's printf doesn't sign extend arguments of smaller types:
|
// glibc's printf doesn't sign extend arguments of smaller types:
|
||||||
// std::printf("%lld", -42); // prints "4294967254"
|
// std::printf("%lld", -42); // prints "4294967254"
|
||||||
// but we don't have to do the same because it's a UB.
|
// but we don't have to do the same because it's a UB.
|
||||||
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
|
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
|
||||||
} else {
|
} else {
|
||||||
arg_ = internal::make_arg<Context>(
|
arg_ = detail::make_arg<Context>(
|
||||||
static_cast<typename make_unsigned_or_bool<U>::type>(value));
|
static_cast<typename make_unsigned_or_bool<U>::type>(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,7 +133,7 @@ template <typename Context> class char_converter {
|
|||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
void operator()(T value) {
|
void operator()(T value) {
|
||||||
arg_ = internal::make_arg<Context>(
|
arg_ = detail::make_arg<Context>(
|
||||||
static_cast<typename Context::char_type>(value));
|
static_cast<typename Context::char_type>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,6 +141,13 @@ template <typename Context> class char_converter {
|
|||||||
void operator()(T) {} // No conversion needed for non-integral types.
|
void operator()(T) {} // No conversion needed for non-integral types.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// An argument visitor that return a pointer to a C string if argument is a
|
||||||
|
// string or null otherwise.
|
||||||
|
template <typename Char> struct get_cstring {
|
||||||
|
template <typename T> const Char* operator()(T) { return nullptr; }
|
||||||
|
const Char* operator()(const Char* s) { return s; }
|
||||||
|
};
|
||||||
|
|
||||||
// Checks if an argument is a valid printf width specifier and sets
|
// Checks if an argument is a valid printf width specifier and sets
|
||||||
// left alignment if it is negative.
|
// left alignment if it is negative.
|
||||||
template <typename Char> class printf_width_handler {
|
template <typename Char> class printf_width_handler {
|
||||||
@ -155,7 +162,7 @@ template <typename Char> class printf_width_handler {
|
|||||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
unsigned operator()(T value) {
|
unsigned operator()(T value) {
|
||||||
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
|
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
|
||||||
if (internal::is_negative(value)) {
|
if (detail::is_negative(value)) {
|
||||||
specs_.align = align::left;
|
specs_.align = align::left;
|
||||||
width = 0 - width;
|
width = 0 - width;
|
||||||
}
|
}
|
||||||
@ -172,22 +179,20 @@ template <typename Char> class printf_width_handler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename Context>
|
template <typename Char, typename Context>
|
||||||
void printf(buffer<Char>& buf, basic_string_view<Char> format,
|
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||||
basic_format_args<Context> args) {
|
basic_format_args<Context> args) {
|
||||||
Context(std::back_inserter(buf), format, args).format();
|
Context(std::back_inserter(buf), format, args).format();
|
||||||
}
|
}
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
template <typename OutputIt, typename Char, typename Context>
|
// For printing into memory_buffer.
|
||||||
internal::truncating_iterator<OutputIt> printf(
|
template <typename Char, typename Context>
|
||||||
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format,
|
FMT_DEPRECATED void printf(detail::buffer<Char>& buf,
|
||||||
basic_format_args<Context> args) {
|
basic_string_view<Char> format,
|
||||||
return Context(it, format, args).format();
|
basic_format_args<Context> args) {
|
||||||
|
return detail::vprintf(buf, format, args);
|
||||||
}
|
}
|
||||||
} // namespace internal
|
using detail::vprintf;
|
||||||
|
|
||||||
using internal::printf; // For printing into memory_buffer.
|
|
||||||
|
|
||||||
template <typename Range> class printf_arg_formatter;
|
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
class basic_printf_parse_context : public basic_format_parse_context<Char> {
|
class basic_printf_parse_context : public basic_format_parse_context<Char> {
|
||||||
@ -200,15 +205,15 @@ template <typename OutputIt, typename Char> class basic_printf_context;
|
|||||||
The ``printf`` argument formatter.
|
The ``printf`` argument formatter.
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename Range>
|
template <typename OutputIt, typename Char>
|
||||||
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
|
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
|
||||||
public:
|
public:
|
||||||
using iterator = typename Range::iterator;
|
using iterator = OutputIt;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using char_type = typename Range::value_type;
|
using char_type = Char;
|
||||||
using base = internal::arg_formatter_base<Range>;
|
using base = detail::arg_formatter_base<OutputIt, Char>;
|
||||||
using context_type = basic_printf_context<iterator, char_type>;
|
using context_type = basic_printf_context<OutputIt, Char>;
|
||||||
|
|
||||||
context_type& context_;
|
context_type& context_;
|
||||||
|
|
||||||
@ -233,9 +238,9 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
|
|||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
|
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
|
||||||
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
|
: base(iter, &specs, detail::locale_ref()), context_(ctx) {}
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
|
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
|
||||||
iterator operator()(T value) {
|
iterator operator()(T value) {
|
||||||
// MSVC2013 fails to compile separate overloads for bool and char_type so
|
// MSVC2013 fails to compile separate overloads for bool and char_type so
|
||||||
// use std::is_same instead.
|
// use std::is_same instead.
|
||||||
@ -250,7 +255,11 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
|
|||||||
return (*this)(static_cast<int>(value));
|
return (*this)(static_cast<int>(value));
|
||||||
fmt_specs.sign = sign::none;
|
fmt_specs.sign = sign::none;
|
||||||
fmt_specs.alt = false;
|
fmt_specs.alt = false;
|
||||||
fmt_specs.align = align::right;
|
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
|
||||||
|
// align::numeric needs to be overwritten here since the '0' flag is
|
||||||
|
// ignored for non-numeric types
|
||||||
|
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
||||||
|
fmt_specs.align = align::right;
|
||||||
return base::operator()(value);
|
return base::operator()(value);
|
||||||
} else {
|
} else {
|
||||||
return base::operator()(value);
|
return base::operator()(value);
|
||||||
@ -316,12 +325,14 @@ template <typename T> struct printf_formatter {
|
|||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
|
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
internal::format_value(internal::get_container(ctx.out()), value);
|
detail::format_value(detail::get_container(ctx.out()), value);
|
||||||
return ctx.out();
|
return ctx.out();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** This template formats data and writes the output to a writer. */
|
/**
|
||||||
|
This template formats data and writes the output through an output iterator.
|
||||||
|
*/
|
||||||
template <typename OutputIt, typename Char> class basic_printf_context {
|
template <typename OutputIt, typename Char> class basic_printf_context {
|
||||||
public:
|
public:
|
||||||
/** The character type for the output. */
|
/** The character type for the output. */
|
||||||
@ -351,9 +362,8 @@ template <typename OutputIt, typename Char> class basic_printf_context {
|
|||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Constructs a ``printf_context`` object. References to the arguments and
|
Constructs a ``printf_context`` object. References to the arguments are
|
||||||
the writer are stored in the context object so make sure they have
|
stored in the context object so make sure they have appropriate lifetimes.
|
||||||
appropriate lifetimes.
|
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
|
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
|
||||||
@ -363,7 +373,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
|
|||||||
OutputIt out() { return out_; }
|
OutputIt out() { return out_; }
|
||||||
void advance_to(OutputIt it) { out_ = it; }
|
void advance_to(OutputIt it) { out_ = it; }
|
||||||
|
|
||||||
internal::locale_ref locale() { return {}; }
|
detail::locale_ref locale() { return {}; }
|
||||||
|
|
||||||
format_arg arg(int id) const { return args_.get(id); }
|
format_arg arg(int id) const { return args_.get(id); }
|
||||||
|
|
||||||
@ -374,7 +384,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Formats stored arguments and writes the output to the range. */
|
/** Formats stored arguments and writes the output to the range. */
|
||||||
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
|
template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>>
|
||||||
OutputIt format();
|
OutputIt format();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -394,7 +404,9 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
|
|||||||
specs.fill[0] = '0';
|
specs.fill[0] = '0';
|
||||||
break;
|
break;
|
||||||
case ' ':
|
case ' ':
|
||||||
specs.sign = sign::space;
|
if (specs.sign != sign::plus) {
|
||||||
|
specs.sign = sign::space;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case '#':
|
case '#':
|
||||||
specs.alt = true;
|
specs.alt = true;
|
||||||
@ -412,7 +424,7 @@ basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
|
|||||||
arg_index = parse_ctx_.next_arg_id();
|
arg_index = parse_ctx_.next_arg_id();
|
||||||
else
|
else
|
||||||
parse_ctx_.check_arg_id(--arg_index);
|
parse_ctx_.check_arg_id(--arg_index);
|
||||||
return internal::get_arg(*this, arg_index);
|
return detail::get_arg(*this, arg_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
template <typename OutputIt, typename Char>
|
||||||
@ -424,7 +436,7 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
|
|||||||
if (c >= '0' && c <= '9') {
|
if (c >= '0' && c <= '9') {
|
||||||
// Parse an argument index (if followed by '$') or a width possibly
|
// Parse an argument index (if followed by '$') or a width possibly
|
||||||
// preceded with '0' flag(s).
|
// preceded with '0' flag(s).
|
||||||
internal::error_handler eh;
|
detail::error_handler eh;
|
||||||
int value = parse_nonnegative_int(it, end, eh);
|
int value = parse_nonnegative_int(it, end, eh);
|
||||||
if (it != end && *it == '$') { // value is an argument index
|
if (it != end && *it == '$') { // value is an argument index
|
||||||
++it;
|
++it;
|
||||||
@ -443,12 +455,12 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
|
|||||||
// Parse width.
|
// Parse width.
|
||||||
if (it != end) {
|
if (it != end) {
|
||||||
if (*it >= '0' && *it <= '9') {
|
if (*it >= '0' && *it <= '9') {
|
||||||
internal::error_handler eh;
|
detail::error_handler eh;
|
||||||
specs.width = parse_nonnegative_int(it, end, eh);
|
specs.width = parse_nonnegative_int(it, end, eh);
|
||||||
} else if (*it == '*') {
|
} else if (*it == '*') {
|
||||||
++it;
|
++it;
|
||||||
specs.width = static_cast<int>(visit_format_arg(
|
specs.width = static_cast<int>(visit_format_arg(
|
||||||
internal::printf_width_handler<char_type>(specs), get_arg()));
|
detail::printf_width_handler<char_type>(specs), get_arg()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return arg_index;
|
return arg_index;
|
||||||
@ -476,38 +488,52 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
|||||||
|
|
||||||
// Parse argument index, flags and width.
|
// Parse argument index, flags and width.
|
||||||
int arg_index = parse_header(it, end, specs);
|
int arg_index = parse_header(it, end, specs);
|
||||||
if (arg_index == 0) on_error("argument index out of range");
|
if (arg_index == 0) on_error("argument not found");
|
||||||
|
|
||||||
// Parse precision.
|
// Parse precision.
|
||||||
if (it != end && *it == '.') {
|
if (it != end && *it == '.') {
|
||||||
++it;
|
++it;
|
||||||
c = it != end ? *it : 0;
|
c = it != end ? *it : 0;
|
||||||
if ('0' <= c && c <= '9') {
|
if ('0' <= c && c <= '9') {
|
||||||
internal::error_handler eh;
|
detail::error_handler eh;
|
||||||
specs.precision = parse_nonnegative_int(it, end, eh);
|
specs.precision = parse_nonnegative_int(it, end, eh);
|
||||||
} else if (c == '*') {
|
} else if (c == '*') {
|
||||||
++it;
|
++it;
|
||||||
specs.precision = static_cast<int>(
|
specs.precision = static_cast<int>(
|
||||||
visit_format_arg(internal::printf_precision_handler(), get_arg()));
|
visit_format_arg(detail::printf_precision_handler(), get_arg()));
|
||||||
} else {
|
} else {
|
||||||
specs.precision = 0;
|
specs.precision = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
format_arg arg = get_arg(arg_index);
|
format_arg arg = get_arg(arg_index);
|
||||||
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
|
// For d, i, o, u, x, and X conversion specifiers, if a precision is
|
||||||
|
// specified, the '0' flag is ignored
|
||||||
|
if (specs.precision >= 0 && arg.is_integral())
|
||||||
|
specs.fill[0] =
|
||||||
|
' '; // Ignore '0' flag for non-numeric types or if '-' present.
|
||||||
|
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
|
||||||
|
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
|
||||||
|
auto str_end = str + specs.precision;
|
||||||
|
auto nul = std::find(str, str_end, Char());
|
||||||
|
arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>(
|
||||||
|
str,
|
||||||
|
detail::to_unsigned(nul != str_end ? nul - str : specs.precision)));
|
||||||
|
}
|
||||||
|
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
|
||||||
specs.alt = false;
|
specs.alt = false;
|
||||||
if (specs.fill[0] == '0') {
|
if (specs.fill[0] == '0') {
|
||||||
if (arg.is_arithmetic())
|
if (arg.is_arithmetic() && specs.align != align::left)
|
||||||
specs.align = align::numeric;
|
specs.align = align::numeric;
|
||||||
else
|
else
|
||||||
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types.
|
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
|
||||||
|
// flag is also present.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse length and convert the argument to the required type.
|
// Parse length and convert the argument to the required type.
|
||||||
c = it != end ? *it++ : 0;
|
c = it != end ? *it++ : 0;
|
||||||
char_type t = it != end ? *it : 0;
|
char_type t = it != end ? *it : 0;
|
||||||
using internal::convert_arg;
|
using detail::convert_arg;
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
if (t == 'h') {
|
if (t == 'h') {
|
||||||
@ -531,7 +557,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
|||||||
convert_arg<intmax_t>(arg, t);
|
convert_arg<intmax_t>(arg, t);
|
||||||
break;
|
break;
|
||||||
case 'z':
|
case 'z':
|
||||||
convert_arg<std::size_t>(arg, t);
|
convert_arg<size_t>(arg, t);
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
convert_arg<std::ptrdiff_t>(arg, t);
|
convert_arg<std::ptrdiff_t>(arg, t);
|
||||||
@ -556,7 +582,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
|||||||
specs.type = 'd';
|
specs.type = 'd';
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
visit_format_arg(internal::char_converter<basic_printf_context>(arg),
|
visit_format_arg(detail::char_converter<basic_printf_context>(arg),
|
||||||
arg);
|
arg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -565,15 +591,14 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
|||||||
start = it;
|
start = it;
|
||||||
|
|
||||||
// Format argument.
|
// Format argument.
|
||||||
visit_format_arg(ArgFormatter(out, specs, *this), arg);
|
out = visit_format_arg(ArgFormatter(out, specs, *this), arg);
|
||||||
}
|
}
|
||||||
return std::copy(start, it, out);
|
return std::copy(start, it, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
using basic_printf_context_t =
|
using basic_printf_context_t =
|
||||||
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
|
basic_printf_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
|
||||||
Char>;
|
|
||||||
|
|
||||||
using printf_context = basic_printf_context_t<char>;
|
using printf_context = basic_printf_context_t<char>;
|
||||||
using wprintf_context = basic_printf_context_t<wchar_t>;
|
using wprintf_context = basic_printf_context_t<wchar_t>;
|
||||||
@ -610,7 +635,7 @@ inline std::basic_string<Char> vsprintf(
|
|||||||
const S& format,
|
const S& format,
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
printf(buffer, to_string_view(format), args);
|
vprintf(buffer, to_string_view(format), args);
|
||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,7 +649,7 @@ inline std::basic_string<Char> vsprintf(
|
|||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||||
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
|
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
|
||||||
using context = basic_printf_context_t<Char>;
|
using context = basic_printf_context_t<Char>;
|
||||||
return vsprintf(to_string_view(format), make_format_args<context>(args...));
|
return vsprintf(to_string_view(format), make_format_args<context>(args...));
|
||||||
@ -635,8 +660,8 @@ inline int vfprintf(
|
|||||||
std::FILE* f, const S& format,
|
std::FILE* f, const S& format,
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
printf(buffer, to_string_view(format), args);
|
vprintf(buffer, to_string_view(format), args);
|
||||||
std::size_t size = buffer.size();
|
size_t size = buffer.size();
|
||||||
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
||||||
? -1
|
? -1
|
||||||
: static_cast<int>(size);
|
: static_cast<int>(size);
|
||||||
@ -652,7 +677,7 @@ inline int vfprintf(
|
|||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||||
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
|
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
|
||||||
using context = basic_printf_context_t<Char>;
|
using context = basic_printf_context_t<Char>;
|
||||||
return vfprintf(f, to_string_view(format),
|
return vfprintf(f, to_string_view(format),
|
||||||
@ -676,7 +701,7 @@ inline int vprintf(
|
|||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||||
inline int printf(const S& format_str, const Args&... args) {
|
inline int printf(const S& format_str, const Args&... args) {
|
||||||
using context = basic_printf_context_t<char_t<S>>;
|
using context = basic_printf_context_t<char_t<S>>;
|
||||||
return vprintf(to_string_view(format_str),
|
return vprintf(to_string_view(format_str),
|
||||||
@ -688,8 +713,8 @@ inline int vfprintf(
|
|||||||
std::basic_ostream<Char>& os, const S& format,
|
std::basic_ostream<Char>& os, const S& format,
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
printf(buffer, to_string_view(format), args);
|
vprintf(buffer, to_string_view(format), args);
|
||||||
internal::write(os, buffer);
|
detail::write_buffer(os, buffer);
|
||||||
return static_cast<int>(buffer.size());
|
return static_cast<int>(buffer.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -698,7 +723,7 @@ template <typename ArgFormatter, typename Char,
|
|||||||
typename Context =
|
typename Context =
|
||||||
basic_printf_context<typename ArgFormatter::iterator, Char>>
|
basic_printf_context<typename ArgFormatter::iterator, Char>>
|
||||||
typename ArgFormatter::iterator vprintf(
|
typename ArgFormatter::iterator vprintf(
|
||||||
internal::buffer<Char>& out, basic_string_view<Char> format_str,
|
detail::buffer<Char>& out, basic_string_view<Char> format_str,
|
||||||
basic_format_args<type_identity_t<Context>> args) {
|
basic_format_args<type_identity_t<Context>> args) {
|
||||||
typename ArgFormatter::iterator iter(out);
|
typename ArgFormatter::iterator iter(out);
|
||||||
Context(iter, format_str, args).template format<ArgFormatter>();
|
Context(iter, format_str, args).template format<ArgFormatter>();
|
||||||
|
@ -33,7 +33,7 @@ template <typename Char> struct formatting_base {
|
|||||||
|
|
||||||
template <typename Char, typename Enable = void>
|
template <typename Char, typename Enable = void>
|
||||||
struct formatting_range : formatting_base<Char> {
|
struct formatting_range : formatting_base<Char> {
|
||||||
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
|
static FMT_CONSTEXPR_DECL const size_t range_length_limit =
|
||||||
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
||||||
// range.
|
// range.
|
||||||
Char prefix;
|
Char prefix;
|
||||||
@ -54,7 +54,7 @@ struct formatting_tuple : formatting_base<Char> {
|
|||||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace internal {
|
namespace detail {
|
||||||
|
|
||||||
template <typename RangeT, typename OutputIterator>
|
template <typename RangeT, typename OutputIterator>
|
||||||
OutputIterator copy(const RangeT& range, OutputIterator out) {
|
OutputIterator copy(const RangeT& range, OutputIterator out) {
|
||||||
@ -118,26 +118,24 @@ template <typename T> class is_tuple_like_ {
|
|||||||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
||||||
template <typename T, T... N>
|
template <typename T, T... N>
|
||||||
using integer_sequence = std::integer_sequence<T, N...>;
|
using integer_sequence = std::integer_sequence<T, N...>;
|
||||||
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
|
template <size_t... N> using index_sequence = std::index_sequence<N...>;
|
||||||
template <std::size_t N>
|
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
|
||||||
using make_index_sequence = std::make_index_sequence<N>;
|
|
||||||
#else
|
#else
|
||||||
template <typename T, T... N> struct integer_sequence {
|
template <typename T, T... N> struct integer_sequence {
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
|
|
||||||
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
|
static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <std::size_t... N>
|
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
|
||||||
using index_sequence = integer_sequence<std::size_t, N...>;
|
|
||||||
|
|
||||||
template <typename T, std::size_t N, T... Ns>
|
template <typename T, size_t N, T... Ns>
|
||||||
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
|
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
|
||||||
template <typename T, T... Ns>
|
template <typename T, T... Ns>
|
||||||
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
|
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
|
||||||
|
|
||||||
template <std::size_t N>
|
template <size_t N>
|
||||||
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
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>
|
||||||
@ -185,11 +183,11 @@ FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
|||||||
return add_space ? L" '{}'" : L"'{}'";
|
return add_space ? L" '{}'" : L"'{}'";
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace detail
|
||||||
|
|
||||||
template <typename T> struct is_tuple_like {
|
template <typename T> struct is_tuple_like {
|
||||||
static FMT_CONSTEXPR_DECL const bool value =
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
|
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename TupleT, typename Char>
|
template <typename TupleT, typename Char>
|
||||||
@ -202,17 +200,17 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
|||||||
if (formatting.add_prepostfix_space) {
|
if (formatting.add_prepostfix_space) {
|
||||||
*out++ = ' ';
|
*out++ = ' ';
|
||||||
}
|
}
|
||||||
out = internal::copy(formatting.delimiter, out);
|
out = detail::copy(formatting.delimiter, out);
|
||||||
}
|
}
|
||||||
out = format_to(out,
|
out = format_to(out,
|
||||||
internal::format_str_quoted(
|
detail::format_str_quoted(
|
||||||
(formatting.add_delimiter_spaces && i > 0), v),
|
(formatting.add_delimiter_spaces && i > 0), v),
|
||||||
v);
|
v);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
formatting_tuple<Char>& formatting;
|
formatting_tuple<Char>& formatting;
|
||||||
std::size_t& i;
|
size_t& i;
|
||||||
typename std::add_lvalue_reference<decltype(
|
typename std::add_lvalue_reference<decltype(
|
||||||
std::declval<FormatContext>().out())>::type out;
|
std::declval<FormatContext>().out())>::type out;
|
||||||
};
|
};
|
||||||
@ -228,14 +226,14 @@ 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) -> decltype(ctx.out()) {
|
||||||
auto out = ctx.out();
|
auto out = ctx.out();
|
||||||
std::size_t i = 0;
|
size_t i = 0;
|
||||||
internal::copy(formatting.prefix, out);
|
detail::copy(formatting.prefix, out);
|
||||||
|
|
||||||
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||||
if (formatting.add_prepostfix_space) {
|
if (formatting.add_prepostfix_space) {
|
||||||
*out++ = ' ';
|
*out++ = ' ';
|
||||||
}
|
}
|
||||||
internal::copy(formatting.postfix, out);
|
detail::copy(formatting.postfix, out);
|
||||||
|
|
||||||
return ctx.out();
|
return ctx.out();
|
||||||
}
|
}
|
||||||
@ -243,10 +241,9 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
|||||||
|
|
||||||
template <typename T, typename Char> struct is_range {
|
template <typename T, typename Char> struct is_range {
|
||||||
static FMT_CONSTEXPR_DECL const bool value =
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
internal::is_range_<T>::value &&
|
detail::is_range_<T>::value && !detail::is_like_std_string<T>::value &&
|
||||||
!internal::is_like_std_string<T>::value &&
|
|
||||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||||
!std::is_constructible<internal::std_string_view<Char>, T>::value;
|
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename RangeT, typename Char>
|
template <typename RangeT, typename Char>
|
||||||
@ -262,15 +259,17 @@ struct formatter<RangeT, Char,
|
|||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
typename FormatContext::iterator format(const RangeT& values,
|
typename FormatContext::iterator format(const RangeT& values,
|
||||||
FormatContext& ctx) {
|
FormatContext& ctx) {
|
||||||
auto out = internal::copy(formatting.prefix, ctx.out());
|
auto out = detail::copy(formatting.prefix, ctx.out());
|
||||||
std::size_t i = 0;
|
size_t i = 0;
|
||||||
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
|
auto it = values.begin();
|
||||||
|
auto end = values.end();
|
||||||
|
for (; it != end; ++it) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||||
out = internal::copy(formatting.delimiter, out);
|
out = detail::copy(formatting.delimiter, out);
|
||||||
}
|
}
|
||||||
out = format_to(out,
|
out = format_to(out,
|
||||||
internal::format_str_quoted(
|
detail::format_str_quoted(
|
||||||
(formatting.add_delimiter_spaces && i > 0), *it),
|
(formatting.add_delimiter_spaces && i > 0), *it),
|
||||||
*it);
|
*it);
|
||||||
if (++i > formatting.range_length_limit) {
|
if (++i > formatting.range_length_limit) {
|
||||||
@ -279,11 +278,11 @@ struct formatter<RangeT, Char,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||||
return internal::copy(formatting.postfix, out);
|
return detail::copy(formatting.postfix, out);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
|
template <typename Char, typename... T> struct tuple_arg_join : detail::view {
|
||||||
const std::tuple<T...>& tuple;
|
const std::tuple<T...>& tuple;
|
||||||
basic_string_view<Char> sep;
|
basic_string_view<Char> sep;
|
||||||
|
|
||||||
@ -301,14 +300,14 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
|
|||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
typename FormatContext::iterator format(
|
typename FormatContext::iterator format(
|
||||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
|
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
|
||||||
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
|
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename FormatContext, size_t... N>
|
template <typename FormatContext, size_t... N>
|
||||||
typename FormatContext::iterator format(
|
typename FormatContext::iterator format(
|
||||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
||||||
internal::index_sequence<N...>) {
|
detail::index_sequence<N...>) {
|
||||||
return format_args(value, ctx, std::get<N>(value.tuple)...);
|
return format_args(value, ctx, std::get<N>(value.tuple)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,14 +370,14 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
|||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
arg_join<internal::iterator_t<const std::initializer_list<T>>, char> join(
|
arg_join<const T*, const T*, char> join(std::initializer_list<T> list,
|
||||||
std::initializer_list<T> list, string_view sep) {
|
string_view sep) {
|
||||||
return join(std::begin(list), std::end(list), sep);
|
return join(std::begin(list), std::end(list), sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
arg_join<internal::iterator_t<const std::initializer_list<T>>, wchar_t> join(
|
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
|
||||||
std::initializer_list<T> list, wstring_view sep) {
|
wstring_view sep) {
|
||||||
return join(std::begin(list), std::end(list), sep);
|
return join(std::begin(list), std::end(list), sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
168
src/fmt.cpp
168
src/fmt.cpp
@ -10,12 +10,12 @@
|
|||||||
#include <spdlog/fmt/bundled/format-inl.h>
|
#include <spdlog/fmt/bundled/format-inl.h>
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace internal {
|
namespace detail {
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
int format_float(char *buf, std::size_t size, const char *format, int precision, T value)
|
int format_float(char *buf, std::size_t size, const char *format, int precision, T value)
|
||||||
{
|
{
|
||||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
#ifdef FMT_FUZZ
|
||||||
if (precision > 100000)
|
if (precision > 100000)
|
||||||
throw std::runtime_error("fuzz mode - avoid large allocation inside snprintf");
|
throw std::runtime_error("fuzz mode - avoid large allocation inside snprintf");
|
||||||
#endif
|
#endif
|
||||||
@ -23,167 +23,41 @@ int format_float(char *buf, std::size_t size, const char *format, int precision,
|
|||||||
int (*snprintf_ptr)(char *, size_t, const char *, ...) = FMT_SNPRINTF;
|
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);
|
return precision < 0 ? snprintf_ptr(buf, size, format, value) : snprintf_ptr(buf, size, format, precision, value);
|
||||||
}
|
}
|
||||||
struct sprintf_specs
|
} // namespace detail
|
||||||
{
|
|
||||||
int precision;
|
|
||||||
char type;
|
|
||||||
bool alt : 1;
|
|
||||||
|
|
||||||
template<typename Char>
|
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
|
||||||
constexpr sprintf_specs(basic_format_specs<Char> specs)
|
|
||||||
: precision(specs.precision)
|
|
||||||
, type(specs.type)
|
|
||||||
, alt(specs.alt)
|
|
||||||
{}
|
|
||||||
|
|
||||||
constexpr bool has_precision() const
|
|
||||||
{
|
|
||||||
return precision >= 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is deprecated and is kept only to preserve ABI compatibility.
|
|
||||||
template<typename Double>
|
|
||||||
char *sprintf_format(Double value, internal::buffer<char> &buf, sprintf_specs specs)
|
|
||||||
{
|
|
||||||
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
|
|
||||||
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
|
|
||||||
|
|
||||||
// Build format string.
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
max_format_size = 10
|
|
||||||
}; // longest format: %#-*.*Lg
|
|
||||||
char format[max_format_size];
|
|
||||||
char *format_ptr = format;
|
|
||||||
*format_ptr++ = '%';
|
|
||||||
if (specs.alt || !specs.type)
|
|
||||||
*format_ptr++ = '#';
|
|
||||||
if (specs.precision >= 0)
|
|
||||||
{
|
|
||||||
*format_ptr++ = '.';
|
|
||||||
*format_ptr++ = '*';
|
|
||||||
}
|
|
||||||
if (std::is_same<Double, long double>::value)
|
|
||||||
*format_ptr++ = 'L';
|
|
||||||
|
|
||||||
char type = specs.type;
|
|
||||||
|
|
||||||
if (type == '%')
|
|
||||||
type = 'f';
|
|
||||||
else if (type == 0 || type == 'n')
|
|
||||||
type = 'g';
|
|
||||||
#if FMT_MSC_VER
|
|
||||||
if (type == 'F')
|
|
||||||
{
|
|
||||||
// MSVC's printf doesn't support 'F'.
|
|
||||||
type = 'f';
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
*format_ptr++ = type;
|
|
||||||
*format_ptr = '\0';
|
|
||||||
|
|
||||||
// Format using snprintf.
|
|
||||||
char *start = nullptr;
|
|
||||||
char *decimal_point_pos = nullptr;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
std::size_t buffer_size = buf.capacity();
|
|
||||||
start = &buf[0];
|
|
||||||
int result = format_float(start, buffer_size, format, specs.precision, value);
|
|
||||||
if (result >= 0)
|
|
||||||
{
|
|
||||||
unsigned n = internal::to_unsigned(result);
|
|
||||||
if (n < buf.capacity())
|
|
||||||
{
|
|
||||||
// Find the decimal point.
|
|
||||||
auto p = buf.data(), end = p + n;
|
|
||||||
if (*p == '+' || *p == '-')
|
|
||||||
++p;
|
|
||||||
if (specs.type != 'a' && specs.type != 'A')
|
|
||||||
{
|
|
||||||
while (p < end && *p >= '0' && *p <= '9')
|
|
||||||
++p;
|
|
||||||
if (p < end && *p != 'e' && *p != 'E')
|
|
||||||
{
|
|
||||||
decimal_point_pos = p;
|
|
||||||
if (!specs.type)
|
|
||||||
{
|
|
||||||
// Keep only one trailing zero after the decimal point.
|
|
||||||
++p;
|
|
||||||
if (*p == '0')
|
|
||||||
++p;
|
|
||||||
while (p != end && *p >= '1' && *p <= '9')
|
|
||||||
++p;
|
|
||||||
char *where = p;
|
|
||||||
while (p != end && *p == '0')
|
|
||||||
++p;
|
|
||||||
if (p == end || *p < '0' || *p > '9')
|
|
||||||
{
|
|
||||||
if (p != end)
|
|
||||||
std::memmove(where, p, to_unsigned(end - p));
|
|
||||||
n -= static_cast<unsigned>(p - where);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf.resize(n);
|
|
||||||
break; // The buffer is large enough - continue with formatting.
|
|
||||||
}
|
|
||||||
buf.reserve(n + 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If result is negative we ask to increase the capacity by at least 1,
|
|
||||||
// but as std::vector, the buffer grows exponentially.
|
|
||||||
buf.reserve(buf.capacity() + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return decimal_point_pos;
|
|
||||||
}
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
template FMT_API char *internal::sprintf_format(double, internal::buffer<char> &, sprintf_specs);
|
|
||||||
template FMT_API char *internal::sprintf_format(long double, internal::buffer<char> &, sprintf_specs);
|
|
||||||
|
|
||||||
template struct FMT_INSTANTIATION_DEF_API internal::basic_data<void>;
|
|
||||||
|
|
||||||
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
||||||
int (*instantiate_format_float)(double, int, internal::float_specs, internal::buffer<char> &) = internal::format_float;
|
int (*instantiate_format_float)(double, int, detail::float_specs, detail::buffer<char> &) = detail::format_float;
|
||||||
|
|
||||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
template FMT_API internal::locale_ref::locale_ref(const std::locale &loc);
|
template FMT_API detail::locale_ref::locale_ref(const std::locale &loc);
|
||||||
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
|
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Explicit instantiations for char.
|
// Explicit instantiations for char.
|
||||||
|
|
||||||
template FMT_API std::string internal::grouping_impl<char>(locale_ref);
|
template FMT_API std::string detail::grouping_impl<char>(locale_ref);
|
||||||
template FMT_API char internal::thousands_sep_impl(locale_ref);
|
template FMT_API char detail::thousands_sep_impl(locale_ref);
|
||||||
template FMT_API char internal::decimal_point_impl(locale_ref);
|
template FMT_API char detail::decimal_point_impl(locale_ref);
|
||||||
|
|
||||||
template FMT_API void internal::buffer<char>::append(const char *, const char *);
|
template FMT_API void detail::buffer<char>::append(const char *, const char *);
|
||||||
|
|
||||||
template FMT_API void internal::arg_map<format_context>::init(const basic_format_args<format_context> &args);
|
template FMT_API FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to(
|
||||||
|
detail::buffer<char> &, string_view, basic_format_args<FMT_BUFFER_CONTEXT(char)>);
|
||||||
|
|
||||||
template FMT_API std::string internal::vformat<char>(string_view, basic_format_args<format_context>);
|
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 format_context::iterator internal::vformat_to(internal::buffer<char> &, string_view, basic_format_args<format_context>);
|
template FMT_API int detail::format_float(double, int, detail::float_specs, detail::buffer<char> &);
|
||||||
|
template FMT_API int detail::format_float(long double, int, detail::float_specs, detail::buffer<char> &);
|
||||||
template FMT_API int internal::snprintf_float(double, int, internal::float_specs, internal::buffer<char> &);
|
|
||||||
template FMT_API int internal::snprintf_float(long double, int, internal::float_specs, internal::buffer<char> &);
|
|
||||||
template FMT_API int internal::format_float(double, int, internal::float_specs, internal::buffer<char> &);
|
|
||||||
template FMT_API int internal::format_float(long double, int, internal::float_specs, internal::buffer<char> &);
|
|
||||||
|
|
||||||
// Explicit instantiations for wchar_t.
|
// Explicit instantiations for wchar_t.
|
||||||
|
|
||||||
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
|
template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref);
|
||||||
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
|
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
|
||||||
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
|
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
|
||||||
|
|
||||||
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t *, const wchar_t *);
|
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t *, const wchar_t *);
|
||||||
|
|
||||||
template FMT_API std::wstring internal::vformat<wchar_t>(wstring_view, basic_format_args<wformat_context>);
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // !SPDLOG_FMT_EXTERNAL
|
#endif // !SPDLOG_FMT_EXTERNAL
|
Loading…
Reference in New Issue
Block a user