a7f9129f18
c4ee72653 Update version fa2eb2d2e Bump version 35f72bf21 Bump version d22f00d7e Update changelog 4e8d21560 Update changelog 84eecb656 Prune CI configs 55727e3b2 More compile-time checks 1010b7f14 Update docs 2ac51fc44 Update changelog 831132293 Workaround for Microsoft Visual Studio 2022 Internal compiler error. 115e00e0b Replace __cplusplus with FMT_CPLUSPLUS. 94114b05c New CI: Microsoft Visual Studio 2022. d2a232082 Fix partial specialization problem for filesystem for Visual Studio (#2957) 0c06c81da Deprecated implicit conversion of enums to ints for consistency with scoped enums c12b4c0cf New CI: GCC-8 C++17, Clang-8 C++17. 99bb5b1d1 Fix std::variant, std::filesystem::path tests on GCC-8, Clang-7,8. e29c2bc60 Update docs c65e4286b Update changelog 69c24e47e Update changelog 6a775e956 Add support for 'std::variant' in C++17 (#2941) 51535866d Update docs 3ef5caa9f Update docs dccd3e674 Fix docs 9cb02aaaa Fix UDLs e6d478f8e Update changelog and docs 2d931b149 Add fmt::streamed 0506a5733 Update changelog e8bd2a804 Fix enable_ifs for map formatter (#2944) 7c56e11ec Update changelog 69a20db08 Update changelog and fix an apidoc comment 7a2a97c88 Update changelog 568233889 Fix is_formattable for tuple-like types. (#2940) f0de12844 Remove /source-charset:utf-8 compile option. eaa8efb95 Fix ofstream handling in msvc fb991e9d3 Update changelog 8e47cfd1c fix -Wsign-conversion warning 247187586 Make the tests pass on a CHERI system. b135f1c01 Refactor handling of argument types f61a1e813 Add format_arg_types 48b7e3daf Added a FMT_STRING wrapper for system_error() call. 4bb3af7a6 Improve compile-time checks d02c582b9 Fix 'duplicate symbol' error. b59d8c3a2 Make std::filesystem::path formatter utf-8 compatible. 232e21d51 Add utf-8 test for std::filesystem::path formatter. 864465419 Docs: add comment about empty format context range ba50c19e8 use qualified call to avoid ADL conflict with std::format_to 9d6039595 Fix compilation on ppc64 a2681aabc Debug ppc failure bfc576736 Add support for std.h in Bazel build 798d09bb7 Debug ppc failure 8c7cf5139 Cleanup cdfacb434 Cleanup parse_format_string 926ddd063 Move compile string to detail cb682f36f Move to_string_view to detail 156744ad4 Simplify fmt::runtime d9c7166cf bi_iterator -> base 11316b29a chore: Set permissions for GitHub actions fe6eb792d Cleanup check_format_string 054b1d980 Remove unused include e927149f8 Cleanup macros 1761e2666 Remove FMT_CONSTEXPR_DECL d6b568a6c Cleanup string_view checks c83a5d42b FMT_MSC_VER -> FMT_MSC_VERSION 27cd68c30 Cleanup macros 08be4abb3 Remove FMT_NVCOMPILER_VERSION 661b19254 Remove FMT_HEADER_ONLY_CONSTEXPR20 d1026fa5d Remove extern format_float 7e63b600b Make to_string work with __float128 b2ea212cd Update README.rst c2fcdc54e Move format_float to format.h for __float128 2b9037a19 Move basic_fp to format.h for compile-time formatting 542785ccb Get rid of detail::bits 65dd2ea52 Use write_escaped_string to std::filesystem::path. 9860f67cd Improve xchar support for std formatters. 03b1b2838 Improve std::filesystem::path formatter. 4f9311e68 Fix definition of error_handler::on_error 652fea45a Visual Studio 2022: fmt/format.h(1526,27): warning C4127: conditional expression is constant #2908 1f9eae7e3 Add xchar support for write_escaped_string. 90b68783f Skip cmake targets inclusion if fmt::fmt already exists (#2907) ce246aaf7 Remove deprecated APIs edeb3d809 Remove deprecated APIs 496aff7c3 Remove deprecated APIs f5cdf7cb0 Simplify snprintf_float 440512f08 Remove deprecated APIs 621eb80bb Remove deprecated APIs 5c7d315de Remove locale.h c6324009b Add initial double-double support 147e8ca58 Fix Windows max mix-up (#2903) 6bf039d75 Add std:🧵:id formatter 9730fb015 Fix path formatter f0903ad9d Add a path formatter 8833f386e Merge branch 'master' of github.com:fmtlib/fmt 5ab9d3925 Namespace-qualify format_to to avoid conflict with std::format_to af5644c27 Update README.rst 3e28dc021 VS2022 17.2: C4189: 'zero': local variable is initialized but not referenced #2891 (#2892) f6f920a1a Tweak a comment and apply clang-format ae963e444 Implement constexpr isfinite to avoid producing NaN 358f5a7e5 Make precision computation consistent with width f63afd161 Fixed all clang -Wsigned-enum-bitfield warnings (#2882) 7e4ad4017 Add initial support for double-double ffb5e6a73 Suppress a -Wliteral-range warning on Apple M1 (#2861) 5d804ee7f Fix handling of subnormals in exotic FP 86e27ccb4 Suppress a warning 192f79aaa Fix handling of locale separators in FP formatting 395cf0f03 Fix detection of unformattable pointers fc429d18b Avoid overhead on sensible platforms ce7ecdb7a Replace conditional compilation with SFINAE 8751a03a0 Fix Unicode handling when writing to an ostream c55175a58 Add an issue template a935ac3e6 MSVC CMake generation optimization (#2852) 22d31b31f Add a __float128 test f607e3e97 Add __float128 support 686de5888 Implement 128-bit constant mul in bigint 02eb215f2 Replace uint128_wrapper with uint128_fallback b4dc7a1d3 Add 128-bit operations to bigint ef54f9aa3 Suppress -Wfloat-equal 288c3b928 Remove dead code in ostream.h format_value 96930161f Implement 128-bit operator+= for uint128_fallback b41890c1e Make arg_mapper SFINAE-friendly again e2408f37c Check if formatter is not defined if there is format_as db5b8993a Fix formatting of std::byte via format_as 1c83eaf75 Fix incompatible between Jinja2 >= 3.1 and sphinx 3.3.0 5379063b5 Fixed clang -Wreserved-identifier warings b591fc87d Fixed all clang -Wreserved-id-macro warnings (on macOS at least) 17dda5839 constexpr -> const for portability 7ffe87c0b Fix docs 3c4273dd0 Simplify UDL 36d95c9fc Fix docs 44abd1f48 Update signatures in docs and ostream.h db745986f Workaround broken std::numeric_limits 8271e43e5 Improve __float128 support and use constexpr 3f9b7433a Improve __float128 support 71778e8b9 Specialize float_info for __float128 f024565c3 Improve exponent handling in Dragon e7f31f5cd Cleanup format_dragon 3c61799fb Cleanup fuzzing mode 4e39e1308 Remove xchar.h include from ostream.h ac0d9d5fe Issue #2816: also strip named-arg for the fallback formatter 4ad90578f Fix #2818: diagnose unformattable arguments in unpacked case 17ba99c1d Fix #2817: add compile-time checking to ostream overloads of fmt::print 3d19be282 Fix #2816: strip named argument wrappers for compile-time checking c076a54a4 Move snprintf_float to format.h 0419d2388 Add FMT_USE_FLOAT128 69396347a Update color.h (#2815) c51604a0e Reduce the number of configs 587dc9946 Remove windows-2016 env no longer suppported by GA 1f3d44b85 Update std::tm/chrono docs bc654faf8 Add is_floating_point that works with __float128 26bffce66 Simplify basic_memory_buffer ed18ca3ea Implement isnan a204b8dde Add initial __float128 support b6b003b07 Cleanup test f2543b0a9 Add initial support for 128-bit floats 72f487562 Simplify float_info f91f61cd1 Reuse num_significand_bits 9a1beab57 Workaround Windows API garbage a8fe8becf Fix compilation error for ranges with ADL `begin`/`end` (#2807) f6bcb25e1 Remove extra dot b4a4189d0 Fix handling of implicit bit 32d477e5f Add `styled` in documentation (#2805) 0b7c045a2 Simplify _cf c10fffecd Make _cf visible in the doc build dcfbe4a77 Document output_file default behavior correctly (#2803) 8c9bc070f Implement styled arguments (#2793) 5bc39d363 Eliminate intel compiler warnings (#2802) e3d688e79 Fix warning C4251: class fmt::v8::file needs to have dll-interface (#2797) 8d4f3e91b Update docs 0cef1f819 Fixing formatting of certain kinds of ranges of ranges. (#2787) 5c0d65640 Fix apt install d416a995e Update README.rst 3f67a1247 Update README.rst cc57e3597 Update godbolt link in the readme (#2789) 86477f7ec Fix size computation 0742606f1 Fix Conversion Warning (#2782) 1ba69fb5a Remove snprintf FP fallback ea6f0bf0e Minor cleanup 1a18a2f3d Fixing "C4127: conditional expression is constant" Visual Studio 2022 warning in pedantic mode (#2783) 4fcacea35 Parameterized fp on significand type cf940ae82 Simplify to_decimal 70dc3de05 Update format.h cbc59ca89 Clear moved from memory buffer ea3d326c6 Fix clang -Wliteral-range warning (#2779) aad44f283 Add fmt::enums::format_as 1319719a5 Add underlying_t af5d8004f Limit Dragonbox to supported FP formats 7b9642096 Remove unused include a0b43bfae Add support for 96-bit long double 2c8cd2db3 Fix handling of zero precision b6d56170f Remove unnecessary inline 05432e570 Use consistent indentation 47da218cc Remove uintptr_fallback 4ddab8901 Merge accumulator into int128_fallback d38f72aff Refactor fallback ints 15c2a3bac int128_t -> int128_opt 532a69a63 Fix handling of 96-bit long double with -m32 d8e1dd4ab improve installing headers ae25f7968 add ability to build Apple framework using CMAKE_FRAMEWORK ce93a66df Implement a fallback uint128_t 6a1346405 Include 128-bit with other signed integers in specifier check 70de324aa Apply 2746 fix for NVidia compiler also (#2770) a1ea3e015 Move built-in formatter specialization to core 161059dd9 Add support for extended precision FP c4c6b42de Bump version 21785040c Fix markup 2b6f7fc7a Add partial support for extended precision FP 0a24a0714 Clz builtin may be not constexpr (Issue #2761) (#2762) ba6f89c76 Update .bazelversion (#2766) 5594edaf6 Address https://github.com/fmtlib/fmt/issues/2763 (#2765) 10e3b83a7 Replace ``make_args_checked`` with ``make_format_args`` (#2760) c48353cb7 Update docs 083510f0f Add FMT_CONSTEXPR to rotr instead dba99bc86 Revert adding constexpr to rotr to satisfy C++11 compilers c04af4bfc Simplify remove_trailing_zeros b348caa9e Remove some C-style casts for consistency c8bd1e646 Simplify remove_trailing_zeros 9b23e9dcb Fix wrong comment/refer to a correct reference 69f2c550a Remove std:: infront of uint32_t/64_t & add constexpr to rotr 9b62310f0 Fix some conversion issues 08d12f31d Fix typo dbddb1d06 Remove literal separator to satisfy some compilers 7dbe3dcde Recover log10_2_significand 10642e608 Optimize remove_trailing_zeros 7b4323e1e Add rotr f1bd6f773 Check r < deltai first, because that is the major branch chosen for short inputs 5d8eb6a1a Reflect the new paper - Change constants appearing in log & division computations - Rename beta_minus_1 to beta 8e2e4d403 Suppress a gcc warning a44716f58 Workaround to Intel compiler (#2758) c71b07016 Add missing const qualifier (#2755) ecd6022c2 Update docs afbcf1e8e Remove legacy C locale wrapper 90325d097 Fix stored type detection e2ba01fcb Fix overload ambiguity in print 17b362f78 Simplify ostream opt-in API a5a7e3a26 Update docs f055ebbd2 Make ostream operators opt in to reduce the risk of ODR violations 8a21e328b Remove problematic constructibility check 31e743d06 Don't use ostream for types convertible to string_view 35c0286cd Simplify byte handling c7173a36a Drop :: and fix formatting 3e8372b96 qualify unqualified calls to format in compile.h (#2742) a34a97cc1 Supporting ? as a string presentation type (#2674) ae1aaaee5 Fix access mode of files created (#2530) (#2733) 1557ab764 Add format_as for enums b00a1eac7 Fixes NVIDIA HPC compiler and Intel ICC compatibility (#2732) a7aecbfca Remove an old mingw workaround dfcc730cb Making target_compile_options PRIVATE, fix #2726, fix #2507 f7a809be6 Clarify the choice of magic numbers and compute the most magic one 09fde7f4b Add fmt::underlying for enum classes 0014024a2 Don't rely on transitive includes c28500556 FMT_NOEXCEPT -> noexcept 6240d0201 Improve comments 925b744ae Improve comments 22b14ff25 Simplify cache recovery 3dc26b44d Make a fallback path more compiler-friendly 2e4038bf5 Simplify lines with __builtin_addcll and friends 76336b4f6 Replace noexcept with FMT_NOEXCEPT 918198348 Fix syntax errors 74097a149 Remove now-unused stuffs 21a1c5338 Fix typo 04eea0f0a Remove now-unused stuffs 35a468ed3 Simplify integer checks 1882a7a2c Replace Dragonbox cache which allows simpler cache recovery & integer checks f4dd1b1b8 Simplify Dragonbox Step 3. 70561ed13 Minimize the usage of built-in 128-bit ints It usually generates slower code than manual handling. cdf1a3b53 Fix codecvt warning (#2408) (#2725) b8b037e93 Fix -Wconversion warning (#2724) 5985f0a7d Fix overflow for chrono durations (#2722) 8f8a1a02d Fix handling of formattable types implicitly convertible to pointers b02e5af52 fmt::join support FMT_COMPILE (#2720) 58fb78239 Improve docs 4fe6129d6 Fix FMT_NOEXCEPT definition c056a009d Docs: Fix link to "Compile-time Format String Checks" section (#2712) 7c12118c1 Deprecate buffered_file::fileno 2a09d468d Use noexcept unconditionally a126b4d88 Check if right shift is arithmetic 9ff91b18c Simplify write_fractional_seconds d9f045fba Fix a UB in chrono c06bef727 Adding comments for range formatting. (#2706) 3c98f1a4c Comment style 6e0f1399d Supporting nested format specs for ranges. (#2673) 0102101ac Make colored print handle UTF-8 (#2701) 4ac5269b4 Update ChangeLog.rst git-subtree-dir: externals/fmt git-subtree-split: c4ee726532178e556d923372f29163bd206d7732
640 lines
20 KiB
C++
640 lines
20 KiB
C++
// Formatting library for C++ - legacy printf implementation
|
|
//
|
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
|
// All rights reserved.
|
|
//
|
|
// For the license information refer to format.h.
|
|
|
|
#ifndef FMT_PRINTF_H_
|
|
#define FMT_PRINTF_H_
|
|
|
|
#include <algorithm> // std::max
|
|
#include <limits> // std::numeric_limits
|
|
|
|
#include "format.h"
|
|
|
|
FMT_BEGIN_NAMESPACE
|
|
FMT_MODULE_EXPORT_BEGIN
|
|
|
|
template <typename T> struct printf_formatter { printf_formatter() = delete; };
|
|
|
|
template <typename Char>
|
|
class basic_printf_parse_context : public basic_format_parse_context<Char> {
|
|
using basic_format_parse_context<Char>::basic_format_parse_context;
|
|
};
|
|
|
|
template <typename OutputIt, typename Char> class basic_printf_context {
|
|
private:
|
|
OutputIt out_;
|
|
basic_format_args<basic_printf_context> args_;
|
|
|
|
public:
|
|
using char_type = Char;
|
|
using format_arg = basic_format_arg<basic_printf_context>;
|
|
using parse_context_type = basic_printf_parse_context<Char>;
|
|
template <typename T> using formatter_type = printf_formatter<T>;
|
|
|
|
/**
|
|
\rst
|
|
Constructs a ``printf_context`` object. References to the arguments are
|
|
stored in the context object so make sure they have appropriate lifetimes.
|
|
\endrst
|
|
*/
|
|
basic_printf_context(OutputIt out,
|
|
basic_format_args<basic_printf_context> args)
|
|
: out_(out), args_(args) {}
|
|
|
|
OutputIt out() { return out_; }
|
|
void advance_to(OutputIt it) { out_ = it; }
|
|
|
|
detail::locale_ref locale() { return {}; }
|
|
|
|
format_arg arg(int id) const { return args_.get(id); }
|
|
|
|
FMT_CONSTEXPR void on_error(const char* message) {
|
|
detail::error_handler().on_error(message);
|
|
}
|
|
};
|
|
|
|
FMT_BEGIN_DETAIL_NAMESPACE
|
|
|
|
// Checks if a value fits in int - used to avoid warnings about comparing
|
|
// signed and unsigned integers.
|
|
template <bool IsSigned> struct int_checker {
|
|
template <typename T> static bool fits_in_int(T value) {
|
|
unsigned max = max_value<int>();
|
|
return value <= max;
|
|
}
|
|
static bool fits_in_int(bool) { return true; }
|
|
};
|
|
|
|
template <> struct int_checker<true> {
|
|
template <typename T> static bool fits_in_int(T value) {
|
|
return value >= (std::numeric_limits<int>::min)() &&
|
|
value <= max_value<int>();
|
|
}
|
|
static bool fits_in_int(int) { return true; }
|
|
};
|
|
|
|
class printf_precision_handler {
|
|
public:
|
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
|
int operator()(T value) {
|
|
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
|
FMT_THROW(format_error("number is too big"));
|
|
return (std::max)(static_cast<int>(value), 0);
|
|
}
|
|
|
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
|
int operator()(T) {
|
|
FMT_THROW(format_error("precision is not integer"));
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
// An argument visitor that returns true iff arg is a zero integer.
|
|
class is_zero_int {
|
|
public:
|
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
|
bool operator()(T value) {
|
|
return value == 0;
|
|
}
|
|
|
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
|
bool operator()(T) {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
|
|
|
|
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
|
|
|
|
template <typename T, typename Context> class arg_converter {
|
|
private:
|
|
using char_type = typename Context::char_type;
|
|
|
|
basic_format_arg<Context>& arg_;
|
|
char_type type_;
|
|
|
|
public:
|
|
arg_converter(basic_format_arg<Context>& arg, char_type type)
|
|
: arg_(arg), type_(type) {}
|
|
|
|
void operator()(bool value) {
|
|
if (type_ != 's') operator()<bool>(value);
|
|
}
|
|
|
|
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
|
|
void operator()(U value) {
|
|
bool is_signed = type_ == 'd' || type_ == 'i';
|
|
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
|
|
if (const_check(sizeof(target_type) <= sizeof(int))) {
|
|
// Extra casts are used to silence warnings.
|
|
if (is_signed) {
|
|
arg_ = detail::make_arg<Context>(
|
|
static_cast<int>(static_cast<target_type>(value)));
|
|
} else {
|
|
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
|
|
arg_ = detail::make_arg<Context>(
|
|
static_cast<unsigned>(static_cast<unsigned_type>(value)));
|
|
}
|
|
} else {
|
|
if (is_signed) {
|
|
// glibc's printf doesn't sign extend arguments of smaller types:
|
|
// std::printf("%lld", -42); // prints "4294967254"
|
|
// but we don't have to do the same because it's a UB.
|
|
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
|
|
} else {
|
|
arg_ = detail::make_arg<Context>(
|
|
static_cast<typename make_unsigned_or_bool<U>::type>(value));
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
|
|
void operator()(U) {} // No conversion needed for non-integral types.
|
|
};
|
|
|
|
// Converts an integer argument to T for printf, if T is an integral type.
|
|
// If T is void, the argument is converted to corresponding signed or unsigned
|
|
// type depending on the type specifier: 'd' and 'i' - signed, other -
|
|
// unsigned).
|
|
template <typename T, typename Context, typename Char>
|
|
void convert_arg(basic_format_arg<Context>& arg, Char type) {
|
|
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
|
|
}
|
|
|
|
// Converts an integer argument to char for printf.
|
|
template <typename Context> class char_converter {
|
|
private:
|
|
basic_format_arg<Context>& arg_;
|
|
|
|
public:
|
|
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
|
|
|
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
|
void operator()(T value) {
|
|
arg_ = detail::make_arg<Context>(
|
|
static_cast<typename Context::char_type>(value));
|
|
}
|
|
|
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
|
void operator()(T) {} // No conversion needed for non-integral types.
|
|
};
|
|
|
|
// An argument visitor that return a pointer to a C string if argument is a
|
|
// string or null otherwise.
|
|
template <typename Char> struct get_cstring {
|
|
template <typename T> const Char* operator()(T) { return nullptr; }
|
|
const Char* operator()(const Char* s) { return s; }
|
|
};
|
|
|
|
// Checks if an argument is a valid printf width specifier and sets
|
|
// left alignment if it is negative.
|
|
template <typename Char> class printf_width_handler {
|
|
private:
|
|
using format_specs = basic_format_specs<Char>;
|
|
|
|
format_specs& specs_;
|
|
|
|
public:
|
|
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
|
|
|
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
|
unsigned operator()(T value) {
|
|
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
|
|
if (detail::is_negative(value)) {
|
|
specs_.align = align::left;
|
|
width = 0 - width;
|
|
}
|
|
unsigned int_max = max_value<int>();
|
|
if (width > int_max) FMT_THROW(format_error("number is too big"));
|
|
return static_cast<unsigned>(width);
|
|
}
|
|
|
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
|
unsigned operator()(T) {
|
|
FMT_THROW(format_error("width is not integer"));
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
// The ``printf`` argument formatter.
|
|
template <typename OutputIt, typename Char>
|
|
class printf_arg_formatter : public arg_formatter<Char> {
|
|
private:
|
|
using base = arg_formatter<Char>;
|
|
using context_type = basic_printf_context<OutputIt, Char>;
|
|
using format_specs = basic_format_specs<Char>;
|
|
|
|
context_type& context_;
|
|
|
|
OutputIt write_null_pointer(bool is_string = false) {
|
|
auto s = this->specs;
|
|
s.type = presentation_type::none;
|
|
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
|
|
}
|
|
|
|
public:
|
|
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
|
|
: base{iter, s, locale_ref()}, context_(ctx) {}
|
|
|
|
OutputIt operator()(monostate value) { return base::operator()(value); }
|
|
|
|
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
|
|
OutputIt operator()(T value) {
|
|
// MSVC2013 fails to compile separate overloads for bool and Char so use
|
|
// std::is_same instead.
|
|
if (std::is_same<T, Char>::value) {
|
|
format_specs fmt_specs = this->specs;
|
|
if (fmt_specs.type != presentation_type::none &&
|
|
fmt_specs.type != presentation_type::chr) {
|
|
return (*this)(static_cast<int>(value));
|
|
}
|
|
fmt_specs.sign = sign::none;
|
|
fmt_specs.alt = false;
|
|
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
|
|
// align::numeric needs to be overwritten here since the '0' flag is
|
|
// ignored for non-numeric types
|
|
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
|
fmt_specs.align = align::right;
|
|
return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
|
|
}
|
|
return base::operator()(value);
|
|
}
|
|
|
|
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
|
OutputIt operator()(T value) {
|
|
return base::operator()(value);
|
|
}
|
|
|
|
/** Formats a null-terminated C string. */
|
|
OutputIt operator()(const char* value) {
|
|
if (value) return base::operator()(value);
|
|
return write_null_pointer(this->specs.type != presentation_type::pointer);
|
|
}
|
|
|
|
/** Formats a null-terminated wide C string. */
|
|
OutputIt operator()(const wchar_t* value) {
|
|
if (value) return base::operator()(value);
|
|
return write_null_pointer(this->specs.type != presentation_type::pointer);
|
|
}
|
|
|
|
OutputIt operator()(basic_string_view<Char> value) {
|
|
return base::operator()(value);
|
|
}
|
|
|
|
/** Formats a pointer. */
|
|
OutputIt operator()(const void* value) {
|
|
return value ? base::operator()(value) : write_null_pointer();
|
|
}
|
|
|
|
/** Formats an argument of a custom (user-defined) type. */
|
|
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
|
|
auto parse_ctx =
|
|
basic_printf_parse_context<Char>(basic_string_view<Char>());
|
|
handle.format(parse_ctx, context_);
|
|
return this->out;
|
|
}
|
|
};
|
|
|
|
template <typename Char>
|
|
void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
|
|
const Char* end) {
|
|
for (; it != end; ++it) {
|
|
switch (*it) {
|
|
case '-':
|
|
specs.align = align::left;
|
|
break;
|
|
case '+':
|
|
specs.sign = sign::plus;
|
|
break;
|
|
case '0':
|
|
specs.fill[0] = '0';
|
|
break;
|
|
case ' ':
|
|
if (specs.sign != sign::plus) {
|
|
specs.sign = sign::space;
|
|
}
|
|
break;
|
|
case '#':
|
|
specs.alt = true;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename Char, typename GetArg>
|
|
int parse_header(const Char*& it, const Char* end,
|
|
basic_format_specs<Char>& specs, GetArg get_arg) {
|
|
int arg_index = -1;
|
|
Char c = *it;
|
|
if (c >= '0' && c <= '9') {
|
|
// Parse an argument index (if followed by '$') or a width possibly
|
|
// preceded with '0' flag(s).
|
|
int value = parse_nonnegative_int(it, end, -1);
|
|
if (it != end && *it == '$') { // value is an argument index
|
|
++it;
|
|
arg_index = value != -1 ? value : max_value<int>();
|
|
} else {
|
|
if (c == '0') specs.fill[0] = '0';
|
|
if (value != 0) {
|
|
// Nonzero value means that we parsed width and don't need to
|
|
// parse it or flags again, so return now.
|
|
if (value == -1) FMT_THROW(format_error("number is too big"));
|
|
specs.width = value;
|
|
return arg_index;
|
|
}
|
|
}
|
|
}
|
|
parse_flags(specs, it, end);
|
|
// Parse width.
|
|
if (it != end) {
|
|
if (*it >= '0' && *it <= '9') {
|
|
specs.width = parse_nonnegative_int(it, end, -1);
|
|
if (specs.width == -1) FMT_THROW(format_error("number is too big"));
|
|
} else if (*it == '*') {
|
|
++it;
|
|
specs.width = static_cast<int>(visit_format_arg(
|
|
detail::printf_width_handler<Char>(specs), get_arg(-1)));
|
|
}
|
|
}
|
|
return arg_index;
|
|
}
|
|
|
|
template <typename Char, typename Context>
|
|
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
|
basic_format_args<Context> args) {
|
|
using OutputIt = buffer_appender<Char>;
|
|
auto out = OutputIt(buf);
|
|
auto context = basic_printf_context<OutputIt, Char>(out, args);
|
|
auto parse_ctx = basic_printf_parse_context<Char>(format);
|
|
|
|
// Returns the argument with specified index or, if arg_index is -1, the next
|
|
// argument.
|
|
auto get_arg = [&](int arg_index) {
|
|
if (arg_index < 0)
|
|
arg_index = parse_ctx.next_arg_id();
|
|
else
|
|
parse_ctx.check_arg_id(--arg_index);
|
|
return detail::get_arg(context, arg_index);
|
|
};
|
|
|
|
const Char* start = parse_ctx.begin();
|
|
const Char* end = parse_ctx.end();
|
|
auto it = start;
|
|
while (it != end) {
|
|
if (!detail::find<false, Char>(it, end, '%', it)) {
|
|
it = end; // detail::find leaves it == nullptr if it doesn't find '%'
|
|
break;
|
|
}
|
|
Char c = *it++;
|
|
if (it != end && *it == c) {
|
|
out = detail::write(
|
|
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
|
|
start = ++it;
|
|
continue;
|
|
}
|
|
out = detail::write(out, basic_string_view<Char>(
|
|
start, detail::to_unsigned(it - 1 - start)));
|
|
|
|
basic_format_specs<Char> specs;
|
|
specs.align = align::right;
|
|
|
|
// Parse argument index, flags and width.
|
|
int arg_index = parse_header(it, end, specs, get_arg);
|
|
if (arg_index == 0) parse_ctx.on_error("argument not found");
|
|
|
|
// Parse precision.
|
|
if (it != end && *it == '.') {
|
|
++it;
|
|
c = it != end ? *it : 0;
|
|
if ('0' <= c && c <= '9') {
|
|
specs.precision = parse_nonnegative_int(it, end, 0);
|
|
} else if (c == '*') {
|
|
++it;
|
|
specs.precision = static_cast<int>(
|
|
visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
|
|
} else {
|
|
specs.precision = 0;
|
|
}
|
|
}
|
|
|
|
auto arg = get_arg(arg_index);
|
|
// For d, i, o, u, x, and X conversion specifiers, if a precision is
|
|
// specified, the '0' flag is ignored
|
|
if (specs.precision >= 0 && arg.is_integral())
|
|
specs.fill[0] =
|
|
' '; // Ignore '0' flag for non-numeric types or if '-' present.
|
|
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
|
|
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
|
|
auto str_end = str + specs.precision;
|
|
auto nul = std::find(str, str_end, Char());
|
|
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
|
|
basic_string_view<Char>(
|
|
str, detail::to_unsigned(nul != str_end ? nul - str
|
|
: specs.precision)));
|
|
}
|
|
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
|
|
specs.alt = false;
|
|
if (specs.fill[0] == '0') {
|
|
if (arg.is_arithmetic() && specs.align != align::left)
|
|
specs.align = align::numeric;
|
|
else
|
|
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
|
|
// flag is also present.
|
|
}
|
|
|
|
// Parse length and convert the argument to the required type.
|
|
c = it != end ? *it++ : 0;
|
|
Char t = it != end ? *it : 0;
|
|
using detail::convert_arg;
|
|
switch (c) {
|
|
case 'h':
|
|
if (t == 'h') {
|
|
++it;
|
|
t = it != end ? *it : 0;
|
|
convert_arg<signed char>(arg, t);
|
|
} else {
|
|
convert_arg<short>(arg, t);
|
|
}
|
|
break;
|
|
case 'l':
|
|
if (t == 'l') {
|
|
++it;
|
|
t = it != end ? *it : 0;
|
|
convert_arg<long long>(arg, t);
|
|
} else {
|
|
convert_arg<long>(arg, t);
|
|
}
|
|
break;
|
|
case 'j':
|
|
convert_arg<intmax_t>(arg, t);
|
|
break;
|
|
case 'z':
|
|
convert_arg<size_t>(arg, t);
|
|
break;
|
|
case 't':
|
|
convert_arg<std::ptrdiff_t>(arg, t);
|
|
break;
|
|
case 'L':
|
|
// printf produces garbage when 'L' is omitted for long double, no
|
|
// need to do the same.
|
|
break;
|
|
default:
|
|
--it;
|
|
convert_arg<void>(arg, c);
|
|
}
|
|
|
|
// Parse type.
|
|
if (it == end) FMT_THROW(format_error("invalid format string"));
|
|
char type = static_cast<char>(*it++);
|
|
if (arg.is_integral()) {
|
|
// Normalize type.
|
|
switch (type) {
|
|
case 'i':
|
|
case 'u':
|
|
type = 'd';
|
|
break;
|
|
case 'c':
|
|
visit_format_arg(
|
|
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
|
|
arg);
|
|
break;
|
|
}
|
|
}
|
|
specs.type = parse_presentation_type(type);
|
|
if (specs.type == presentation_type::none)
|
|
parse_ctx.on_error("invalid type specifier");
|
|
|
|
start = it;
|
|
|
|
// Format argument.
|
|
out = visit_format_arg(
|
|
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
|
|
}
|
|
detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
|
|
}
|
|
FMT_END_DETAIL_NAMESPACE
|
|
|
|
template <typename Char>
|
|
using basic_printf_context_t =
|
|
basic_printf_context<detail::buffer_appender<Char>, Char>;
|
|
|
|
using printf_context = basic_printf_context_t<char>;
|
|
using wprintf_context = basic_printf_context_t<wchar_t>;
|
|
|
|
using printf_args = basic_format_args<printf_context>;
|
|
using wprintf_args = basic_format_args<wprintf_context>;
|
|
|
|
/**
|
|
\rst
|
|
Constructs an `~fmt::format_arg_store` object that contains references to
|
|
arguments and can be implicitly converted to `~fmt::printf_args`.
|
|
\endrst
|
|
*/
|
|
template <typename... T>
|
|
inline auto make_printf_args(const T&... args)
|
|
-> format_arg_store<printf_context, T...> {
|
|
return {args...};
|
|
}
|
|
|
|
/**
|
|
\rst
|
|
Constructs an `~fmt::format_arg_store` object that contains references to
|
|
arguments and can be implicitly converted to `~fmt::wprintf_args`.
|
|
\endrst
|
|
*/
|
|
template <typename... T>
|
|
inline auto make_wprintf_args(const T&... args)
|
|
-> format_arg_store<wprintf_context, T...> {
|
|
return {args...};
|
|
}
|
|
|
|
template <typename S, typename Char = char_t<S>>
|
|
inline auto vsprintf(
|
|
const S& fmt,
|
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
|
-> std::basic_string<Char> {
|
|
basic_memory_buffer<Char> buffer;
|
|
vprintf(buffer, detail::to_string_view(fmt), args);
|
|
return to_string(buffer);
|
|
}
|
|
|
|
/**
|
|
\rst
|
|
Formats arguments and returns the result as a string.
|
|
|
|
**Example**::
|
|
|
|
std::string message = fmt::sprintf("The answer is %d", 42);
|
|
\endrst
|
|
*/
|
|
template <typename S, typename... T,
|
|
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
|
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
|
|
using context = basic_printf_context_t<Char>;
|
|
return vsprintf(detail::to_string_view(fmt),
|
|
fmt::make_format_args<context>(args...));
|
|
}
|
|
|
|
template <typename S, typename Char = char_t<S>>
|
|
inline auto vfprintf(
|
|
std::FILE* f, const S& fmt,
|
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
|
-> int {
|
|
basic_memory_buffer<Char> buffer;
|
|
vprintf(buffer, detail::to_string_view(fmt), args);
|
|
size_t size = buffer.size();
|
|
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
|
? -1
|
|
: static_cast<int>(size);
|
|
}
|
|
|
|
/**
|
|
\rst
|
|
Prints formatted data to the file *f*.
|
|
|
|
**Example**::
|
|
|
|
fmt::fprintf(stderr, "Don't %s!", "panic");
|
|
\endrst
|
|
*/
|
|
template <typename S, typename... T, typename Char = char_t<S>>
|
|
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
|
|
using context = basic_printf_context_t<Char>;
|
|
return vfprintf(f, detail::to_string_view(fmt),
|
|
fmt::make_format_args<context>(args...));
|
|
}
|
|
|
|
template <typename S, typename Char = char_t<S>>
|
|
inline auto vprintf(
|
|
const S& fmt,
|
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
|
-> int {
|
|
return vfprintf(stdout, detail::to_string_view(fmt), args);
|
|
}
|
|
|
|
/**
|
|
\rst
|
|
Prints formatted data to ``stdout``.
|
|
|
|
**Example**::
|
|
|
|
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
|
\endrst
|
|
*/
|
|
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
|
|
inline auto printf(const S& fmt, const T&... args) -> int {
|
|
return vprintf(
|
|
detail::to_string_view(fmt),
|
|
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
|
|
}
|
|
|
|
FMT_MODULE_EXPORT_END
|
|
FMT_END_NAMESPACE
|
|
|
|
#endif // FMT_PRINTF_H_
|