date/chrono_io.h
2016-10-08 15:03:33 -04:00

669 lines
12 KiB
C++

#ifndef CHRONO_IO_H
#define CHRONO_IO_H
// The MIT License (MIT)
//
// Copyright (c) 2016 Howard Hinnant
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Our apologies. When the previous paragraph was written, lowercase had not yet
// been invented (that woud involve another several millennia of evolution).
// We did not mean to shout.
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <iosfwd>
#include <ratio>
#include <string>
#include <type_traits>
namespace date
{
namespace detail
{
#if __cplusplus >= 201402
template <class CharT, std::size_t N>
class string_literal
{
CharT p_[N];
public:
using const_iterator = const CharT*;
string_literal(string_literal const&) = default;
string_literal& operator=(string_literal const&) = delete;
template <std::size_t N1 = 2,
class = std::enable_if_t<N1 == N>>
constexpr string_literal(CharT c) noexcept
: p_{c}
{
}
constexpr string_literal(const CharT(&a)[N]) noexcept
: p_{}
{
for (std::size_t i = 0; i < N; ++i)
p_[i] = a[i];
}
template <class U = CharT, class = std::enable_if_t<1 < sizeof(U)>>
constexpr string_literal(const char(&a)[N]) noexcept
: p_{}
{
for (std::size_t i = 0; i < N; ++i)
p_[i] = a[i];
}
template <class CharT2, class = std::enable_if_t<!std::is_same<CharT2, CharT>{}>>
constexpr string_literal(string_literal<CharT2, N> const& a) noexcept
: p_{}
{
for (std::size_t i = 0; i < N; ++i)
p_[i] = a[i];
}
template <std::size_t N1, std::size_t N2,
class = std::enable_if_t<N1 + N2 - 1 == N>>
constexpr string_literal(const string_literal<CharT, N1>& x,
const string_literal<CharT, N2>& y) noexcept
: p_{}
{
std::size_t i = 0;
for (; i < N1-1; ++i)
p_[i] = x[i];
for (std::size_t j = 0; j < N2; ++j, ++i)
p_[i] = y[j];
}
constexpr const CharT* data() const noexcept {return p_;}
constexpr std::size_t size() const noexcept {return N-1;}
constexpr const_iterator begin() const noexcept {return p_;}
constexpr const_iterator end() const noexcept {return p_ + N-1;}
constexpr CharT const& operator[](std::size_t n) const noexcept
{
return p_[n];
}
template <class Traits>
friend
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const string_literal& s)
{
return os << s.p_;
}
};
template <class CharT1, class CharT2, std::size_t N1, std::size_t N2>
constexpr
inline
string_literal<std::conditional_t<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>,
N1 + N2 - 1>
operator+(const string_literal<CharT1, N1>& x, const string_literal<CharT2, N2>& y) noexcept
{
using CharT = std::conditional_t<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>;
return string_literal<CharT, N1 + N2 - 1>{string_literal<CharT, N1>{x},
string_literal<CharT, N2>{y}};
}
template <class CharT, std::size_t N>
constexpr
inline
string_literal<CharT, N>
msl(const CharT(&a)[N]) noexcept
{
return string_literal<CharT, N>{a};
}
template <class CharT,
class = std::enable_if_t<std::is_same<CharT, char>{} ||
std::is_same<CharT, wchar_t>{} ||
std::is_same<CharT, char16_t>{} ||
std::is_same<CharT, char32_t>{}>>
constexpr
inline
string_literal<CharT, 2>
msl(CharT c) noexcept
{
return string_literal<CharT, 2>{c};
}
constexpr
std::size_t
to_string_len(std::intmax_t i)
{
std::size_t r = 0;
do
{
i /= 10;
++r;
} while (i > 0);
return r;
}
template <std::intmax_t N>
constexpr
inline
std::enable_if_t
<
N < 10,
string_literal<char, to_string_len(N)+1>
>
msl() noexcept
{
return msl(char(N % 10 + '0'));
}
template <std::intmax_t N>
constexpr
inline
std::enable_if_t
<
10 <= N,
string_literal<char, to_string_len(N)+1>
>
msl() noexcept
{
return msl<N/10>() + msl(char(N % 10 + '0'));
}
template <class CharT, std::intmax_t N, std::intmax_t D>
constexpr
inline
std::enable_if_t
<
std::ratio<N, D>::type::den != 1,
string_literal<CharT, to_string_len(std::ratio<N, D>::type::num) +
to_string_len(std::ratio<N, D>::type::den) + 4>
>
msl(std::ratio<N, D>) noexcept
{
using R = typename std::ratio<N, D>::type;
return msl(CharT{'['}) + msl<R::num>() + msl(CharT{'/'}) +
msl<R::den>() + msl(CharT{']'});
}
template <class CharT, std::intmax_t N, std::intmax_t D>
constexpr
inline
std::enable_if_t
<
std::ratio<N, D>::type::den == 1,
string_literal<CharT, to_string_len(std::ratio<N, D>::type::num) + 3>
>
msl(std::ratio<N, D>) noexcept
{
using R = typename std::ratio<N, D>::type;
return msl(CharT{'['}) + msl<R::num>() + msl(CharT{']'});
}
template <class CharT>
constexpr
inline
auto
msl(std::atto) noexcept
{
return msl(CharT{'a'});
}
template <class CharT>
constexpr
inline
auto
msl(std::femto) noexcept
{
return msl(CharT{'f'});
}
template <class CharT>
constexpr
inline
auto
msl(std::pico) noexcept
{
return msl(CharT{'p'});
}
template <class CharT>
constexpr
inline
auto
msl(std::nano) noexcept
{
return msl(CharT{'n'});
}
template <class CharT>
constexpr
inline
std::enable_if_t
<
std::is_same<CharT, char>{},
string_literal<char, 3>
>
msl(std::micro) noexcept
{
return string_literal<char, 3>{"\xC2\xB5"};
}
template <class CharT>
constexpr
inline
std::enable_if_t
<
!std::is_same<CharT, char>{},
string_literal<CharT, 2>
>
msl(std::micro) noexcept
{
return string_literal<CharT, 2>{CharT{static_cast<unsigned char>('\xB5')}};
}
template <class CharT>
constexpr
inline
auto
msl(std::milli) noexcept
{
return msl(CharT{'m'});
}
template <class CharT>
constexpr
inline
auto
msl(std::centi) noexcept
{
return msl(CharT{'c'});
}
template <class CharT>
constexpr
inline
auto
msl(std::deci) noexcept
{
return msl(CharT{'d'});
}
template <class CharT>
constexpr
inline
auto
msl(std::deca) noexcept
{
return string_literal<CharT, 3>{"da"};
}
template <class CharT>
constexpr
inline
auto
msl(std::hecto) noexcept
{
return msl(CharT{'h'});
}
template <class CharT>
constexpr
inline
auto
msl(std::kilo) noexcept
{
return msl(CharT{'k'});
}
template <class CharT>
constexpr
inline
auto
msl(std::mega) noexcept
{
return msl(CharT{'M'});
}
template <class CharT>
constexpr
inline
auto
msl(std::giga) noexcept
{
return msl(CharT{'G'});
}
template <class CharT>
constexpr
inline
auto
msl(std::tera) noexcept
{
return msl(CharT{'T'});
}
template <class CharT>
constexpr
inline
auto
msl(std::peta) noexcept
{
return msl(CharT{'P'});
}
template <class CharT>
constexpr
inline
auto
msl(std::exa) noexcept
{
return msl(CharT{'E'});
}
template <class CharT, class Period>
constexpr
auto
get_units(Period p)
{
return msl<CharT>(p) + string_literal<CharT, 2>{"s"};
}
template <class CharT>
constexpr
auto
get_units(std::ratio<1>)
{
return string_literal<CharT, 2>{"s"};
}
template <class CharT>
constexpr
auto
get_units(std::ratio<60>)
{
return string_literal<CharT, 4>{"min"};
}
template <class CharT>
constexpr
auto
get_units(std::ratio<3600>)
{
return string_literal<CharT, 2>{"h"};
}
#else // __cplusplus < 201402
inline
std::string
to_string(std::uint64_t x)
{
return std::to_string(x);
}
template <class CharT>
std::basic_string<CharT>
to_string(std::uint64_t x)
{
auto y = std::to_string(x);
return std::basic_string<CharT>(y.begin(), y.end());
}
template <class CharT, std::intmax_t N, std::intmax_t D>
constexpr
inline
typename std::enable_if
<
std::ratio<N, D>::type::den != 1,
std::basic_string<CharT>
>::type
msl(std::ratio<N, D>) noexcept
{
using R = typename std::ratio<N, D>::type;
return std::basic_string<CharT>(1, '[') + to_string<CharT>(R::num) + CharT{'/'} +
to_string<CharT>(R::den) + CharT{']'};
}
template <class CharT, std::intmax_t N, std::intmax_t D>
constexpr
inline
typename std::enable_if
<
std::ratio<N, D>::type::den == 1,
std::basic_string<CharT>
>::type
msl(std::ratio<N, D>) noexcept
{
using R = typename std::ratio<N, D>::type;
return std::basic_string<CharT>(1, '[') + to_string<CharT>(R::num) + CharT{']'};
}
template <class CharT>
constexpr
inline
std::basic_string<CharT>
msl(std::atto) noexcept
{
return {'a'};
}
template <class CharT>
constexpr
inline
std::basic_string<CharT>
msl(std::femto) noexcept
{
return {'f'};
}
template <class CharT>
constexpr
inline
std::basic_string<CharT>
msl(std::pico) noexcept
{
return {'p'};
}
template <class CharT>
constexpr
inline
std::basic_string<CharT>
msl(std::nano) noexcept
{
return {'n'};
}
template <class CharT>
constexpr
inline
typename std::enable_if
<
std::is_same<CharT, char>::value,
std::string
>::type
msl(std::micro) noexcept
{
return "\xC2\xB5";
}
template <class CharT>
constexpr
inline
typename std::enable_if
<
!std::is_same<CharT, char>::value,
std::basic_string<CharT>
>::type
msl(std::micro) noexcept
{
return {CharT(static_cast<unsigned char>('\xB5'))};
}
template <class CharT>
constexpr
inline
std::basic_string<CharT>
msl(std::milli) noexcept
{
return {'m'};
}
template <class CharT>
constexpr
inline
std::basic_string<CharT>
msl(std::centi) noexcept
{
return {'c'};
}
template <class CharT>
constexpr
inline
std::basic_string<CharT>
msl(std::deci) noexcept
{
return {'d'};
}
template <class CharT>
constexpr
inline
std::basic_string<CharT>
msl(std::deca) noexcept
{
return {'d', 'a'};
}
template <class CharT>
constexpr
inline
std::basic_string<CharT>
msl(std::hecto) noexcept
{
return {'h'};
}
template <class CharT>
constexpr
inline
std::basic_string<CharT>
msl(std::kilo) noexcept
{
return {'k'};
}
template <class CharT>
constexpr
inline
std::basic_string<CharT>
msl(std::mega) noexcept
{
return {'M'};
}
template <class CharT>
constexpr
inline
std::basic_string<CharT>
msl(std::giga) noexcept
{
return {'G'};
}
template <class CharT>
constexpr
inline
std::basic_string<CharT>
msl(std::tera) noexcept
{
return {'T'};
}
template <class CharT>
constexpr
inline
std::basic_string<CharT>
msl(std::peta) noexcept
{
return {'P'};
}
template <class CharT>
constexpr
inline
std::basic_string<CharT>
msl(std::exa) noexcept
{
return {'E'};
}
template <class CharT, class Period>
std::basic_string<CharT>
get_units(Period p)
{
return msl<CharT>(p) + CharT{'s'};
}
template <class CharT>
std::basic_string<CharT>
get_units(std::ratio<1>)
{
return {'s'};
}
template <class CharT>
std::basic_string<CharT>
get_units(std::ratio<60>)
{
return {'m', 'i', 'n'};
}
template <class CharT>
std::basic_string<CharT>
get_units(std::ratio<3600>)
{
return {'h'};
}
#endif // __cplusplus >= 201402
} // namespace detail
template <class CharT, class Traits, class Rep, class Period>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os,
const std::chrono::duration<Rep, Period>& d)
{
using namespace std::chrono;
return os << d.count()
<< detail::get_units<CharT>(typename Period::type{});
}
} // namespace date
#endif // CHRONO_IO_H