Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into fuzz
Browse files Browse the repository at this point in the history
# Conflicts:
#	include/fmt/chrono.h
  • Loading branch information
pauldreik committed May 30, 2019
2 parents 492a204 + 30bce6c commit b6a5927
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 37 deletions.
20 changes: 20 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,26 @@ matrix:
- ubuntu-toolchain-r-test
packages:
- g++-6
# g++ 8 on Linux with C++17
- env: COMPILER=g++-8 BUILD=Debug STANDARD=17
compiler: gcc
addons:
apt:
update: true
sources:
- ubuntu-toolchain-r-test
packages:
- g++-8
- env: COMPILER=g++-8 BUILD=Release STANDARD=17
compiler: gcc
addons:
apt:
update: true
sources:
- ubuntu-toolchain-r-test
packages:
- g++-8

# Apple clang on OS X with C++14
- env: BUILD=Debug STANDARD=14
compiler: clang
Expand Down
8 changes: 4 additions & 4 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,6 @@ You can also write a formatter for a hierarchy of classes::
fmt::print("{}", a); // prints "B"
}

This section shows how to define a custom format function for a user-defined
type. The next section describes how to get ``fmt`` to use a conventional stream
output ``operator<<`` when one is defined for a user-defined type.

Output Iterator Support
-----------------------

Expand Down Expand Up @@ -214,6 +210,10 @@ Utilities

.. doxygenfunction:: fmt::to_string_view(basic_string_view<Char>)

.. doxygenfunction:: fmt::join(const Range&, string_view)

.. doxygenfunction:: fmt::join(It, It, string_view)

.. doxygenclass:: fmt::basic_memory_buffer
:protected-members:
:members:
Expand Down
38 changes: 22 additions & 16 deletions include/fmt/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -465,13 +465,10 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
}

template <typename Rep, typename OutputIt>
OutputIt static format_chrono_duration_value(OutputIt out, Rep val,
int precision) {
if (precision < 0) {
return format_to(out, std::is_floating_point<Rep>::value ? "{:g}" : "{}",
val);
}
return format_to(out, "{:.{}f}", val, precision);
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}" : "{}",
val);
}

template <typename Period, typename OutputIt>
Expand All @@ -487,21 +484,22 @@ struct chrono_formatter {
FormatContext& context;
OutputIt out;
int precision;
Rep val;
typedef std::chrono::duration<Rep> seconds;
typedef typename std::conditional<std::is_integral<Rep>::value &&
sizeof(Rep) < sizeof(int),
int, Rep>::type rep;
rep val;
typedef std::chrono::duration<rep> seconds;
seconds s;
typedef std::chrono::duration<Rep, std::milli> milliseconds;
typedef std::chrono::duration<rep, std::milli> milliseconds;

typedef typename FormatContext::char_type char_type;

explicit chrono_formatter(FormatContext& ctx, OutputIt o,
std::chrono::duration<Rep, Period> d)
: context(ctx), out(o), val(d.count()) {
if (d.count() < 0) {
// hmm, what happens if this is INT_MIN?
d = -d;
*out++ = '-';
}
// hmm, what happens if this is INT_MIN?
if (d.count() < 0) d = -d;

// this may overflow and/or the result may not fit in the
// target type.
#ifdef FMT_SAFE_DURATION_CAST
Expand Down Expand Up @@ -551,7 +549,15 @@ struct chrono_formatter {
return time;
}

void write_sign() {
if (val < 0) {
*out++ = '-';
val = -val;
}
}

void write(Rep value, int width) {
write_sign();
if (isnan(value)) return write_nan();
typedef typename int_traits<int>::main_type main_type;
main_type n = to_unsigned(to_int(value));
Expand Down Expand Up @@ -689,7 +695,7 @@ struct chrono_formatter {
if (handle_nan_inf()) {
return;
}

write_sign();
out = format_chrono_duration_value(out, val, precision);
}

Expand Down
4 changes: 3 additions & 1 deletion include/fmt/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -1416,8 +1416,9 @@ typename buffer_context<Char>::type::iterator vformat_to(
fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23));
\endrst
*/
template <typename S, typename T, FMT_ENABLE_IF(internal::is_string<S>::value)>
template <typename S, typename T>
inline internal::named_arg<T, FMT_CHAR(S)> arg(const S& name, const T& arg) {
static_assert(internal::is_string<S>::value, "");
return {name, arg};
}

Expand Down Expand Up @@ -1472,6 +1473,7 @@ inline std::basic_string<Char> vformat(
std::string message = fmt::format("The answer is {}", 42);
\endrst
*/
// Use SFINAE instead of static_assert because of color overload of fmt::format.
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
inline std::basic_string<FMT_CHAR(S)> format(const S& format_str,
Expand Down
40 changes: 27 additions & 13 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,6 @@ FMT_END_NAMESPACE
# endif
#endif

#if FMT_HAS_GXX_CXX11 || FMT_HAS_FEATURE(cxx_trailing_return) || \
FMT_MSC_VER >= 1600
# define FMT_USE_TRAILING_RETURN 1
#else
# define FMT_USE_TRAILING_RETURN 0
#endif

#ifdef FMT_USE_INT128
// Do nothing.
#elif defined(__SIZEOF_INT128__)
Expand Down Expand Up @@ -291,6 +284,12 @@ FMT_CONSTEXPR T* end(T (&array)[N]) FMT_NOEXCEPT {
return array + N;
}

// An implementation of iterator_t for pre-C++20 compilers such as gcc 4.
template <typename T>
struct iterator_t {
typedef decltype(internal::begin(internal::declval<const T&>())) type;
};

// For std::result_of in gcc 4.4.
template <typename Result> struct function {
template <typename T> struct result { typedef Result type; };
Expand Down Expand Up @@ -3313,6 +3312,10 @@ struct formatter<arg_join<It, Char>, Char>
}
};

/**
Returns an object that formats the iterator range `[begin, end)` with elements
separated by `sep`.
*/
template <typename It>
arg_join<It, char> join(It begin, It end, string_view sep) {
return arg_join<It, char>(begin, end, sep);
Expand All @@ -3323,17 +3326,28 @@ arg_join<It, wchar_t> join(It begin, It end, wstring_view sep) {
return arg_join<It, wchar_t>(begin, end, sep);
}

// The following causes ICE in gcc 4.4.
#if FMT_USE_TRAILING_RETURN && (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 405)
// gcc 4.4 on join: internal compiler error: in tsubst_copy, at cp/pt.c:10122
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 405
/**
\rst
Returns an object that formats `range` with elements separated by `sep`.
**Example**::
std::vector<int> v = {1, 2, 3};
fmt::print("{}", fmt::join(v, ", "));
// Output: "1, 2, 3"
\endrst
*/
template <typename Range>
auto join(const Range& range, string_view sep)
-> arg_join<decltype(internal::begin(range)), char> {
arg_join<typename internal::iterator_t<Range>::type, char>
join(const Range& range, string_view sep) {
return join(internal::begin(range), internal::end(range), sep);
}

template <typename Range>
auto join(const Range& range, wstring_view sep)
-> arg_join<decltype(internal::begin(range)), wchar_t> {
arg_join<typename internal::iterator_t<Range>::type, wchar_t>
join(const Range& range, wstring_view sep) {
return join(internal::begin(range), internal::end(range), sep);
}
#endif
Expand Down
18 changes: 16 additions & 2 deletions test/chrono-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -323,12 +323,22 @@ TEST(ChronoTest, InvalidColons) {
fmt::format_error);
}

TEST(ChronoTest, NegativeDuration) {
EXPECT_EQ("-12345", fmt::format("{:%Q}", std::chrono::seconds(-12345)));
EXPECT_EQ("-03:25:45",
fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345)));
EXPECT_EQ("-00:01",
fmt::format("{:%M:%S}", std::chrono::duration<double>(-1)));
EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(-12345)));
EXPECT_EQ("-00.127",
fmt::format("{:%S}",
std::chrono::duration<signed char, std::milli>{-127}));
}

TEST(ChronoTest, SpecialDurations) {
EXPECT_EQ(
"40.",
fmt::format("{:%S}", std::chrono::duration<double>(1e20)).substr(0, 3));
EXPECT_EQ("-00:01",
fmt::format("{:%M:%S}", std::chrono::duration<double>(-1)));
auto nan = std::numeric_limits<double>::quiet_NaN();
EXPECT_EQ(
"nan nan nan nan.nan nan:nan nan",
Expand All @@ -339,6 +349,10 @@ TEST(ChronoTest, SpecialDurations) {
"1Es");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::atto>(1)),
"1as");
EXPECT_EQ(fmt::format("{:%R}", std::chrono::duration<char, std::mega>{2}),
"03:33");
EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration<char, std::mega>{2}),
"03:33:20");
}
/*
TEST(ChronoTest, DurationIsFloatNaN) {
Expand Down
2 changes: 1 addition & 1 deletion test/format-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1793,7 +1793,7 @@ TEST(FormatTest, JoinArg) {
EXPECT_EQ(format("{}, {}", v3[0], v3[1]),
format("{}", join(v3, v3 + 2, ", ")));

#if FMT_USE_TRAILING_RETURN && (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 405)
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 405
EXPECT_EQ("(1, 2, 3)", format("({})", join(v1, ", ")));
EXPECT_EQ("(+01.20, +03.40)", format("({:+06.2f})", join(v2, ", ")));
#endif
Expand Down

0 comments on commit b6a5927

Please sign in to comment.