Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rebase master. #1

Merged
merged 31 commits into from
Jan 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
65ac626
Improve join docs
vitaut Jan 12, 2020
77165fd
Use FMT_NOEXCEPT instead of noexcept directly
tohammer Jan 13, 2020
ae3ea15
Fix for older versions of intel compiler
gsjaardema Jan 14, 2020
55b6130
Use C++11-compatible operations
gsjaardema Jan 14, 2020
4bbe57c
Work-around for nvcc
gsjaardema Jan 14, 2020
c8dd9cc
Use type_identity to block unnecessary template argument deduction (t…
vitaut Jan 15, 2020
40638a7
Use C++11 compatible std::is_same operations
gsjaardema Jan 14, 2020
4ccbe4b
Avoid namespace clash for fmt
lefticus Jan 15, 2020
1f11070
Remove redundant braces
vitaut Jan 15, 2020
0f0e5dd
Add vcpkg installation instructions
Jan 16, 2020
ffd5f34
Correct display format
Jan 16, 2020
b124e3e
fix url link
Jan 16, 2020
11cc290
re-fix url link
Jan 16, 2020
1bd4f54
update format
Jan 17, 2020
06e437f
Move docs to the proper place
vitaut Jan 18, 2020
bd5f903
Add a locale example
vitaut Jan 18, 2020
9bd9738
Remove static and simplify names
vitaut Jan 18, 2020
75765bf
Avoid unnecessary unsigned overflows (#1515)
vitaut Jan 18, 2020
e5f2f8c
Add variable-width fill support (#1109)
vitaut Jan 19, 2020
8a3a817
Bump version
vitaut Jan 19, 2020
b4218aa
Test invalid fill
vitaut Jan 19, 2020
7800173
Update fill docs
vitaut Jan 20, 2020
47d3968
Add more examples
vitaut Jan 20, 2020
a844d7a
Add namespaces
vitaut Jan 20, 2020
fd1cabe
Workaround a bogus MSVC warning
vitaut Jan 20, 2020
0b2eb65
Add locale example
vitaut Jan 20, 2020
25d6916
Fix so can work without locale defined
gsjaardema Jan 21, 2020
9fc4161
fix interal compiler error when building with mingw
Jan 22, 2020
419db8b
Fix length computation of constexpr C strings
vitaut Jan 23, 2020
09a1324
Disallow passing non-string-literals to FMT_STRING
vitaut Jan 23, 2020
1acb73f
Fix formatting std::chrono::duration types to wide strings (#1533)
Jan 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,19 @@ Compatibility
.. doxygentypedef:: fmt::string_view
.. doxygentypedef:: fmt::wstring_view

Locale
------

All formatting is locale-independent by default. Use the ``'n'`` format
specifier to insert the appropriate number separator characters from the
locale::

#include <fmt/core.h>
#include <locale>

std::locale::global(std::locale("en_US.UTF-8"));
auto s = fmt::format("{:n}", 1000000); // s == "1,000,000"

.. _format-api:

Format API
Expand Down
84 changes: 53 additions & 31 deletions doc/syntax.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,19 @@ The general form of a *standard format specifier* is:

.. productionlist:: sf
format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`][`type`]
fill: <a character other than '{', '}' or '\0'>
fill: <a character other than '{' or '}'>
align: "<" | ">" | "^"
sign: "+" | "-" | " "
width: `integer` | "{" `arg_id` "}"
precision: `integer` | "{" `arg_id` "}"
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s"
int_type: "b" | "B" | "d" | "n" | "o" | "x" | "X"

The *fill* character can be any character other than '{', '}' or '\\0'. The
presence of a fill character is signaled by the character following it, which
must be one of the alignment options. If the second character of *format_spec*
is not a valid alignment option, then it is assumed that both the fill character
and the alignment option are absent.
The *fill* character can be any Unicode code point other than ``'{'`` or
``'}'``. The presence of a fill character is signaled by the character following
it, which must be one of the alignment options. If the second character of
*format_spec* is not a valid alignment option, then it is assumed that both the
fill character and the alignment option are absent.

The meaning of the various alignment options is as follows:

Expand Down Expand Up @@ -320,71 +320,93 @@ following examples.

Accessing arguments by position::

format("{0}, {1}, {2}", 'a', 'b', 'c');
fmt::format("{0}, {1}, {2}", 'a', 'b', 'c');
// Result: "a, b, c"
format("{}, {}, {}", 'a', 'b', 'c');
fmt::format("{}, {}, {}", 'a', 'b', 'c');
// Result: "a, b, c"
format("{2}, {1}, {0}", 'a', 'b', 'c');
fmt::format("{2}, {1}, {0}", 'a', 'b', 'c');
// Result: "c, b, a"
format("{0}{1}{0}", "abra", "cad"); // arguments' indices can be repeated
fmt::format("{0}{1}{0}", "abra", "cad"); // arguments' indices can be repeated
// Result: "abracadabra"

Aligning the text and specifying a width::

format("{:<30}", "left aligned");
fmt::format("{:<30}", "left aligned");
// Result: "left aligned "
format("{:>30}", "right aligned");
fmt::format("{:>30}", "right aligned");
// Result: " right aligned"
format("{:^30}", "centered");
fmt::format("{:^30}", "centered");
// Result: " centered "
format("{:*^30}", "centered"); // use '*' as a fill char
fmt::format("{:*^30}", "centered"); // use '*' as a fill char
// Result: "***********centered***********"

Dynamic width::

format("{:<{}}", "left aligned", 30);
fmt::format("{:<{}}", "left aligned", 30);
// Result: "left aligned "

Dynamic precision::

format("{:.{}f}", 3.14, 1);
fmt::format("{:.{}f}", 3.14, 1);
// Result: "3.1"

Replacing ``%+f``, ``%-f``, and ``% f`` and specifying a sign::

format("{:+f}; {:+f}", 3.14, -3.14); // show it always
fmt::format("{:+f}; {:+f}", 3.14, -3.14); // show it always
// Result: "+3.140000; -3.140000"
format("{: f}; {: f}", 3.14, -3.14); // show a space for positive numbers
fmt::format("{: f}; {: f}", 3.14, -3.14); // show a space for positive numbers
// Result: " 3.140000; -3.140000"
format("{:-f}; {:-f}", 3.14, -3.14); // show only the minus -- same as '{:f}; {:f}'
fmt::format("{:-f}; {:-f}", 3.14, -3.14); // show only the minus -- same as '{:f}; {:f}'
// Result: "3.140000; -3.140000"

Replacing ``%x`` and ``%o`` and converting the value to different bases::

format("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
fmt::format("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
// Result: "int: 42; hex: 2a; oct: 52; bin: 101010"
// with 0x or 0 or 0b as prefix:
format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42);
fmt::format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42);
// Result: "int: 42; hex: 0x2a; oct: 052; bin: 0b101010"

Padded hex byte with prefix and always prints both hex characters::

format("{:#04x}", 0);
fmt::format("{:#04x}", 0);
// Result: "0x00"

.. ifconfig:: False
Box drawing using Unicode fill::

fmt::print(
"┌{0:─^{2}}┐\n"
"│{1: ^{2}}│\n"
"└{0:─^{2}}┘\n", "", "Hello, world!", 20);

prints::

┌────────────────────┐
│ Hello, world! │
└────────────────────┘

Using the comma as a thousands separator::
Using type-specific formatting::

format("{:,}", 1234567890);
'1,234,567,890'
#include <fmt/chrono.h>

Using type-specific formatting::
auto t = tm();
t.tm_year = 2010 - 1900;
t.tm_mon = 6;
t.tm_mday = 4;
t.tm_hour = 12;
t.tm_min = 15;
t.tm_sec = 58;
fmt::print("{:%Y-%m-%d %H:%M:%S}", t);
// Prints: 2010-08-04 12:15:58

>>> import datetime
>>> d = datetime.datetime(2010, 7, 4, 12, 15, 58)
Format("{:%Y-%m-%d %H:%M:%S}") << d)
'2010-07-04 12:15:58'
Using the comma as a thousands separator::

#include <fmt/locale.h>

auto s = fmt::format(std::locale("en_US.UTF-8"), "{:n}", 1234567890);
// s == "1,234,567,890"

.. ifconfig:: False

Nesting arguments and more complex examples::

Expand Down
16 changes: 16 additions & 0 deletions doc/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,22 @@ fmt can be installed on Linux, macOS and Windows with

conda install -c conda-forge fmt

Vcpkg
=====

You can download and install fmt using the `vcpkg
<https://github.com/Microsoft/vcpkg>`__ dependency manager::

git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install fmt

The fmt port in vcpkg is kept up to date by Microsoft team members and community
contributors. If the version is out of date, please `create an issue or pull
request <https://github.com/Microsoft/vcpkg>`__ on the vcpkg repository.

Android NDK
===========

Expand Down
69 changes: 41 additions & 28 deletions include/fmt/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
#ifndef FMT_CHRONO_H_
#define FMT_CHRONO_H_

#include "format.h"
#include "locale.h"

#include <chrono>
#include <ctime>
#include <locale>
#include <sstream>

#include "format.h"
#include "locale.h"

FMT_BEGIN_NAMESPACE

// Enable safe chrono durations, unless explicitly disabled.
Expand Down Expand Up @@ -495,12 +495,12 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
handler.on_text(ptr - 1, ptr);
break;
case 'n': {
const char newline[] = "\n";
const Char newline[]{'\n', 0};
handler.on_text(newline, newline + 1);
break;
}
case 't': {
const char tab[] = "\t";
const Char tab[]{'\t', 0};
handler.on_text(tab, tab + 1);
break;
}
Expand Down Expand Up @@ -759,18 +759,30 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms));
}

template <typename Rep, typename OutputIt>
OutputIt format_chrono_duration_value(OutputIt out, Rep val, int precision) {
if (precision >= 0) return format_to(out, "{:.{}f}", val, precision);
return format_to(out, std::is_floating_point<Rep>::value ? "{:g}" : "{}",
template <typename Char, typename Rep, typename OutputIt>
OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
const Char pr_f[]{'{', ':', '.', '{', '}', 'f', '}', 0};
if (precision >= 0) return format_to(out, pr_f, val, precision);
const Char fp_f[]{'{', ':', 'g', '}', 0};
const Char format[]{'{', '}', 0};
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
val);
}

template <typename Period, typename OutputIt>
static OutputIt format_chrono_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>()) return format_to(out, "{}", unit);
if (Period::den == 1) return format_to(out, "[{}]s", Period::num);
return format_to(out, "[{}/{}]s", Period::num, Period::den);
template <typename Char, typename Period, typename OutputIt>
OutputIt format_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>()) {
string_view s(unit);
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};
if (Period::den == 1) return format_to(out, num_f, Period::num);
const Char num_def_f[]{'[', '{', '}', '/', '{', '}', ']', 's', 0};
return format_to(out, num_def_f, Period::num, Period::den);
}

template <typename FormatContext, typename OutputIt, typename Rep,
Expand Down Expand Up @@ -871,13 +883,13 @@ struct chrono_formatter {
void write_pinf() { std::copy_n("inf", 3, out); }
void write_ninf() { std::copy_n("-inf", 4, out); }

void format_localized(const tm& time, const char* format) {
void format_localized(const tm& time, char format, char modifier = 0) {
if (isnan(val)) return write_nan();
auto locale = context.locale().template get<std::locale>();
auto& facet = std::use_facet<std::time_put<char_type>>(locale);
std::basic_ostringstream<char_type> os;
os.imbue(locale);
facet.put(os, os, ' ', &time, format, format + std::strlen(format));
facet.put(os, os, ' ', &time, format, modifier);
auto str = os.str();
std::copy(str.begin(), str.end(), out);
}
Expand Down Expand Up @@ -907,7 +919,7 @@ struct chrono_formatter {
if (ns == numeric_system::standard) return write(hour(), 2);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour(), 24);
format_localized(time, "%OH");
format_localized(time, 'H', 'O');
}

void on_12_hour(numeric_system ns) {
Expand All @@ -916,7 +928,7 @@ struct chrono_formatter {
if (ns == numeric_system::standard) return write(hour12(), 2);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour12(), 12);
format_localized(time, "%OI");
format_localized(time, 'I', 'O');
}

void on_minute(numeric_system ns) {
Expand All @@ -925,7 +937,7 @@ struct chrono_formatter {
if (ns == numeric_system::standard) return write(minute(), 2);
auto time = tm();
time.tm_min = to_nonnegative_int(minute(), 60);
format_localized(time, "%OM");
format_localized(time, 'M', 'O');
}

void on_second(numeric_system ns) {
Expand All @@ -950,13 +962,12 @@ struct chrono_formatter {
}
auto time = tm();
time.tm_sec = to_nonnegative_int(second(), 60);
format_localized(time, "%OS");
format_localized(time, 'S', 'O');
}

void on_12_hour_time() {
if (handle_nan_inf()) return;

format_localized(time(), "%r");
format_localized(time(), 'r');
}

void on_24_hour_time() {
Expand All @@ -980,16 +991,18 @@ struct chrono_formatter {

void on_am_pm() {
if (handle_nan_inf()) return;
format_localized(time(), "%p");
format_localized(time(), 'p');
}

void on_duration_value() {
if (handle_nan_inf()) return;
write_sign();
out = format_chrono_duration_value(out, val, precision);
out = format_duration_value<char_type>(out, val, precision);
}

void on_duration_unit() { out = format_chrono_duration_unit<Period>(out); }
void on_duration_unit() {
out = format_duration_unit<char_type, Period>(out);
}
};
} // namespace internal

Expand Down Expand Up @@ -1024,7 +1037,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
}

void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
void on_fill(Char fill) { f.specs.fill[0] = fill; }
void on_fill(basic_string_view<Char> fill) { f.specs.fill = fill; }
void on_align(align_t align) { f.specs.align = align; }
void on_width(int width) { f.specs.width = width; }
void on_precision(int _precision) { f.precision = _precision; }
Expand Down Expand Up @@ -1088,8 +1101,8 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx);
if (begin == end || *begin == '}') {
out = internal::format_chrono_duration_value(out, d.count(), precision);
internal::format_chrono_duration_unit<Period>(out);
out = internal::format_duration_value<Char>(out, d.count(), precision);
internal::format_duration_unit<Char, Period>(out);
} else {
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
ctx, out, d);
Expand Down
10 changes: 4 additions & 6 deletions include/fmt/color.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,10 +491,8 @@ void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
internal::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
vformat_to(buf, format_str, args);
if (has_style) {
internal::reset_color<Char>(buf);
}
internal::vformat_to(buf, format_str, args);
if (has_style) internal::reset_color<Char>(buf);
}
} // namespace internal

Expand Down Expand Up @@ -540,7 +538,7 @@ void print(const text_style& ts, const S& format_str, const Args&... args) {
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<Char>> args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
Expand All @@ -562,7 +560,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,
const Args&... args) {
return vformat(ts, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
internal::make_args_checked<Args...>(format_str, args...));
}

FMT_END_NAMESPACE
Expand Down
Loading