mirror of https://github.com/mosra/magnum.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
398 lines
17 KiB
398 lines
17 KiB
#ifndef Magnum_Math_Time_h |
|
#define Magnum_Math_Time_h |
|
/* |
|
This file is part of Magnum. |
|
|
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
|
2020, 2021, 2022, 2023 Vladimír Vondruš <mosra@centrum.cz> |
|
|
|
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. |
|
*/ |
|
|
|
/** @file |
|
* @brief Class @ref Magnum::Math::Nanoseconds, @ref Magnum::Math::Seconds, literal @link Magnum::Math::Literals::TimeLiterals::operator""_nsec() @endlink, @link Magnum::Math::Literals::TimeLiterals::operator""_usec() @endlink, @link Magnum::Math::Literals::TimeLiterals::operator""_msec() @endlink, @link Magnum::Math::Literals::TimeLiterals::operator""_sec() @endlink |
|
* @m_since_latest |
|
*/ |
|
|
|
#include <Corrade/Utility/Utility.h> |
|
|
|
#include "Magnum/Magnum.h" |
|
#include "Magnum/visibility.h" |
|
#include "Magnum/Math/Unit.h" |
|
|
|
namespace Magnum { namespace Math { |
|
|
|
namespace Implementation { |
|
template<class, class> struct NanosecondsConverter; |
|
template<class, class> struct SecondsConverter; |
|
} |
|
|
|
/** |
|
@brief Nanoseconds |
|
@m_since_latest |
|
|
|
Along with @ref Seconds provides convenience classes to make time specification |
|
and conversion less error-prone. As there's little need to represent fractions |
|
of nanoseconds, the @ref Magnum::Nanoseconds typedef uses a 64-bit signed |
|
integer, which covers a span of ±292 years. In scenarios where nanosecond |
|
precision or a large range isn't needed, the @ref Magnum::Seconds typedef, |
|
which is a 32-bit floating-point type, may be sufficient. |
|
|
|
@section Math-Nanoseconds-usage Usage |
|
|
|
You can create the value by using one of the time literals. For all of them the |
|
result type is @ref Nanoseconds for preserving maximum precision, but you can |
|
directly convert the literal value @link Seconds @endlink: |
|
|
|
@snippet Math.cpp Nanoseconds-usage |
|
|
|
Or by explicitly converting a unitless value (such as an output from some |
|
function) to either nanoseconds or seconds. And same can be done in the other direction: |
|
|
|
@snippet Math-stl.cpp Nanoseconds-usage-convert |
|
|
|
The classes support all arithmetic operations, such as addition, subtraction |
|
or multiplication/division by a unitless number: |
|
|
|
@snippet Math.cpp Nanoseconds-usage-operations |
|
|
|
It is also possible to compare time values with all comparison operators. As |
|
the literals are all producing @ref Nanoseconds, it's most convenient to |
|
compare to nanosecond values. Comparison of @ref Nanoseconds and @ref Seconds |
|
is not possible without conversion to a common type first. |
|
|
|
@snippet Math.cpp Nanoseconds-usage-comparison |
|
|
|
@section Math-Nanoseconds-stl STL compatibility |
|
|
|
Instances of @ref Nanoseconds are explicitly convertible from and to |
|
@ref std::chrono::duration and @ref std::chrono::time_point types if you |
|
include @ref Magnum/Math/TimeStl.h. The conversion is provided in a separate |
|
header to avoid unconditional @cpp #include <chrono> @ce, which can |
|
significantly affect compile times. The following table lists allowed |
|
conversions, conversions in certain directions aren't allowed as they cause a |
|
precision loss: |
|
|
|
Magnum type | ↭ | STL type |
|
-----------------| - | --------------------- |
|
@ref Nanoseconds | ⇆ | @ref std::chrono::nanoseconds |
|
@ref Nanoseconds | ← | @ref std::chrono::microseconds |
|
@ref Nanoseconds | ← | @ref std::chrono::milliseconds |
|
@ref Nanoseconds | ← | @ref std::chrono::seconds |
|
@ref Nanoseconds | ← | @ref std::chrono::minutes |
|
@ref Nanoseconds | ← | @ref std::chrono::hours |
|
@ref Nanoseconds | ← | @ref std::chrono::duration |
|
@ref Nanoseconds | ⇆ | @ref std::chrono::duration "std::chrono::duration<Rep, std::nano>" |
|
@ref Nanoseconds | ← | @ref std::chrono::time_point |
|
@ref Nanoseconds | ⇆ | @ref std::chrono::time_point "std::chrono::time_point<Clock, std::chrono::duration<Rep, std::nano>>" |
|
|
|
Example: |
|
|
|
@snippet Math-stl.cpp Nanoseconds-usage |
|
|
|
<b></b> |
|
|
|
@m_class{m-block m-warning} |
|
|
|
@par Conversion from and to std::time_t |
|
Even though @ref std::time_t may look like an implementation-defined strong |
|
type, it's actually just an alias to an integer type, which in turn means |
|
it's not possible to provide safe conversion for it. Thus a simple |
|
conversion, while it may compile, won't do the right thing: |
|
@par |
|
@snippet Math-stl.cpp Nanoseconds-usage-time |
|
|
|
@see @link Literals::TimeLiterals::operator""_nsec() @endlink, |
|
@link Literals::TimeLiterals::operator""_usec() @endlink, |
|
@link Literals::TimeLiterals::operator""_msec() @endlink, |
|
@link Literals::TimeLiterals::operator""_sec() @endlink |
|
*/ |
|
template<class T> class Nanoseconds: public Unit<Nanoseconds, T> { |
|
public: |
|
/** |
|
* @brief Minimal representable value |
|
* |
|
* Returns @cpp -0x8000000000000000_nsec @ce. |
|
*/ |
|
constexpr static Nanoseconds<T> min(); |
|
|
|
/** |
|
* @brief Maximal representable value |
|
* |
|
* Returns @cpp 0x7fffffffffffffff_nsec @ce. |
|
*/ |
|
constexpr static Nanoseconds<T> max(); |
|
|
|
/** |
|
* @brief Default constructor |
|
* |
|
* Equivalent to @ref Nanoseconds(ZeroInitT). |
|
*/ |
|
/* Needs to be Math::Nanoseconds here and in all other places because |
|
older Clang and both MSVC 2015 and 2017 treat it as a template |
|
instance Nanoseconds<T> instead of a Nanoseconds template */ |
|
constexpr /*implicit*/ Nanoseconds() noexcept: Unit<Math::Nanoseconds, T>{ZeroInit} {} |
|
|
|
/** @brief Construct a zero time */ |
|
constexpr explicit Nanoseconds(ZeroInitT) noexcept: Unit<Math::Nanoseconds, T>{ZeroInit} {} |
|
|
|
/** @brief Construct without initializing the contents */ |
|
explicit Nanoseconds(Magnum::NoInitT) noexcept: Unit<Math::Nanoseconds, T>{Magnum::NoInit} {} |
|
|
|
/** @brief Explicit constructor from a unitless type */ |
|
constexpr explicit Nanoseconds(T value) noexcept: Unit<Math::Nanoseconds, T>{value} {} |
|
|
|
/** @brief Copy constructor */ |
|
/* Needed in order to make arithmetic operations (which have a Unit |
|
return type) convertible to Nanoseconds */ |
|
constexpr /*implicit*/ Nanoseconds(Unit<Math::Nanoseconds, T> other) noexcept: Unit<Math::Nanoseconds, T>(other) {} |
|
|
|
/** |
|
* @brief Construct nanoseconds from seconds |
|
* |
|
* The floating-point value is multiplied by a billion and rounded. |
|
*/ |
|
template<class U> constexpr /*implicit*/ Nanoseconds(Unit<Seconds, U> value) noexcept; |
|
|
|
/** @brief Construct nanoseconds from external representation */ |
|
template<class U, class V = decltype(Implementation::NanosecondsConverter<T, U>::from(std::declval<U>()))> constexpr explicit Nanoseconds(const U& other) noexcept: Nanoseconds{Implementation::NanosecondsConverter<T, U>::from(other)} {} |
|
|
|
/** @brief Convert nanoseconds to external representation */ |
|
template<class U, class V = decltype(Implementation::NanosecondsConverter<T, U>::to(std::declval<Nanoseconds<T>>()))> constexpr explicit operator U() const { |
|
return Implementation::NanosecondsConverter<T, U>::to(*this); |
|
} |
|
}; |
|
|
|
/* Doxygen can't match these to the class, meh */ |
|
#ifndef DOXYGEN_GENERATING_OUTPUT |
|
template<> constexpr Nanoseconds<Long> Nanoseconds<Long>::min() { |
|
/* There's apparently no way to say -0x8000000000000000ll. C++, LOL. */ |
|
return Nanoseconds<Long>{Long(0x8000000000000000ull)}; |
|
} |
|
template<> constexpr Nanoseconds<Long> Nanoseconds<Long>::max() { |
|
return Nanoseconds<Long>{0x7fffffffffffffffll}; |
|
} |
|
#endif |
|
|
|
/** |
|
@brief Seconds |
|
@m_since_latest |
|
|
|
Represents a floating-point second value. Compared to @ref Nanoseconds, the |
|
@ref Magnum::Seconds typedef uses a 32-bit float which offers a |
|
microsecond-level precision and a reasonable range for scenarios where storing |
|
a full 64-bit nanosecond value isn't needed. See @ref Nanoseconds for more |
|
information and usage examples. |
|
*/ |
|
template<class T> class Seconds: public Unit<Seconds, T> { |
|
public: |
|
/** |
|
* @brief Default constructor |
|
* |
|
* Equivalent to @ref Seconds(ZeroInitT). |
|
*/ |
|
/* Needs to be Math::Seconds here and in all other places because |
|
older Clang and both MSVC 2015 and 2017 treat it as a template |
|
instance Seconds<T> instead of a Seconds template */ |
|
constexpr /*implicit*/ Seconds() noexcept: Unit<Math::Seconds, T>{ZeroInit} {} |
|
|
|
/** @brief Construct a zero time */ |
|
constexpr explicit Seconds(ZeroInitT) noexcept: Unit<Math::Seconds, T>{ZeroInit} {} |
|
|
|
/** @brief Construct without initializing the contents */ |
|
explicit Seconds(Magnum::NoInitT) noexcept: Unit<Math::Seconds, T>{Magnum::NoInit} {} |
|
|
|
/** @brief Explicit constructor from a unitless type */ |
|
constexpr explicit Seconds(T value) noexcept: Unit<Math::Seconds, T>{value} {} |
|
|
|
/** @brief Copy constructor */ |
|
/* Needed in order to make arithmetic operations (which have a Unit |
|
return type) convertible to Seconds */ |
|
constexpr /*implicit*/ Seconds(Unit<Math::Seconds, T> other) noexcept: Unit<Math::Seconds, T>(other) {} |
|
|
|
/** |
|
* @brief Construct seconds from nanoseconds |
|
* |
|
* A floating-point value can accurately only represent microseconds |
|
* and only in a limited range, so the conversion may result in some |
|
* precision loss. |
|
*/ |
|
template<class U> constexpr /*implicit*/ Seconds(Unit<Nanoseconds, U> value) noexcept; |
|
|
|
/** @brief Construct seconds from external representation */ |
|
template<class U, class V = decltype(Implementation::SecondsConverter<T, U>::from(std::declval<U>()))> constexpr explicit Seconds(const U& other) noexcept: Seconds{Implementation::SecondsConverter<T, U>::from(other)} {} |
|
|
|
/** @brief Convert seconds to external representation */ |
|
template<class U, class V = decltype(Implementation::SecondsConverter<T, U>::to(std::declval<Seconds<T>>()))> constexpr explicit operator U() const { |
|
return Implementation::SecondsConverter<T, U>::to(*this); |
|
} |
|
}; |
|
|
|
/* Unlike STL, where there's e.g. std::literals::string_literals with both |
|
being inline, here's just the second inline because making both would cause |
|
the literals to be implicitly available to all code in Math. Which isn't |
|
great if there are eventually going to be conflicts. In case of STL the |
|
expected use case was that literals are available to anybody who does |
|
`using namespace std;`, that doesn't apply here as most APIs are in |
|
subnamespaces that *should not* be pulled in via `using` as a whole. */ |
|
namespace Literals { |
|
/** @todoc The inline causes "error: non-const getClassDef() called on |
|
aliased member. Please report as a bug." on Doxygen 1.8.18, plus the |
|
fork I have doesn't even mark them as inline in the XML output yet. And |
|
it also duplicates the literal reference to parent namespace, adding |
|
extra noise. Revisit once upgrading to a newer version. */ |
|
#ifndef DOXYGEN_GENERATING_OUTPUT |
|
inline |
|
#endif |
|
namespace TimeLiterals { |
|
|
|
/* Note on literal naming: while the STL in C++14 uses `ns`, `us`, `s` and |
|
https://en.cppreference.com/w/cpp/chrono/operator%22%22s claims that having |
|
`s` for both a string literal, taking const char*, and a second literal, |
|
taking long double, "just works", at least on GCC and Clang it's only true |
|
if both are defined next to each other IN THE SAME NAMESPACE. Which |
|
completely breaks any library composability or encapsulation, as it means I |
|
can not have a StringView _s literal defined in Corrade::Containers and a |
|
Nanosecond _s literal defined in Magnum::Math. The only workaround I found |
|
was to do something along the lines of the following, which was NOT NICE at |
|
all (and no, `using namespace Containers::Literals` wasn't enough). |
|
|
|
namespace Magnum { namespace Math { namespace Literals { |
|
using Containers::Literals::operator""_s; |
|
}}} |
|
|
|
Thus I'm choosing a different name to prevent the conflict from even |
|
happening. On the other hand, that's probably a better solution even without |
|
the above design issue in the C++ spec, because it prevents potential |
|
conflicts with _s / _us being eventually used for Short and UnsignedShort |
|
literals, or _h conflicting between half-float and hour literals (now it'd |
|
be _hr). Even C++14 picked `min` for minutes instead of `m` because it |
|
apparently seemed to become problematic once/if distance literals for meters |
|
and such get introduced. So why not go with the more clear name for |
|
everything already. Seeing 15.0_sec in unfamiliar code doesn't feel |
|
ambiguous, seeing 127_s or 0.5_h definitely does. */ |
|
|
|
/** @relatesalso Magnum::Math::Nanoseconds |
|
@brief Nanosecond value literal |
|
@m_since_latest |
|
|
|
Compared to the microsecond, millisecond and second literals, this literal is |
|
an integer value and not a floating-point, as it's not possible to represent |
|
fractions of nanoseconds. Usage example: |
|
|
|
@snippet Math.cpp _nsec |
|
|
|
@see @link operator""_usec() @endlink, @link operator""_msec() @endlink, |
|
@link operator""_sec() @endlink |
|
@m_keywords{_nsec nsec} |
|
*/ |
|
constexpr Nanoseconds<Long> operator "" _nsec(unsigned long long value) { |
|
return Nanoseconds<Long>{Long(value)}; |
|
} |
|
|
|
/** @relatesalso Magnum::Math::Nanoseconds |
|
@brief Microsecond value literal |
|
@m_since_latest |
|
|
|
As the value is converted to whole nanoseconds, everything after thousandths is |
|
truncated. Additionally, up to thousandths the conversion is without precision |
|
loss only on systems with a 80-bit @cpp long double @ce (which has a 63-bit |
|
mantissa). If you need to ensure nanosecond-level precision on systems that |
|
have a 64-bit @cpp long double @ce, use @link operator""_nsec() @endlink |
|
instead. On the other hand, if nanosecond-level precision isn't needed, it's |
|
possible to convert directly to @ref Seconds that offer a microsecond-level |
|
precision on a range of roughly ±8 seconds. For example: |
|
|
|
@snippet Math.cpp _usec |
|
|
|
@see @link operator""_msec() @endlink, @link operator""_sec() @endlink, |
|
@ref CORRADE_LONG_DOUBLE_SAME_AS_DOUBLE |
|
@m_keywords{_usec usec} |
|
*/ |
|
constexpr Nanoseconds<Long> operator "" _usec(long double value) { |
|
return Nanoseconds<Long>{Long(value*1000.0l)}; |
|
} |
|
|
|
/** @relatesalso Magnum::Math::Nanoseconds |
|
@brief Millisecond value literal |
|
@m_since_latest |
|
|
|
As the value is converted to whole nanoseconds, everything after millionths is |
|
truncated. Additionally, up to millionths the conversion is without precision |
|
loss only on systems with a 80-bit @cpp long double @ce (which has a 63-bit |
|
mantissa). If you need to ensure nanosecond-level precision on systems that |
|
have a 64-bit @cpp long double @ce, use @link operator""_nsec() @endlink |
|
instead. On the other hand, if nanosecond-level precision isn't needed, it's |
|
possible to convert directly to @ref Seconds that offer a millisecond-level |
|
precision on a range of roughly ±2 hours. For example: |
|
|
|
@snippet Math.cpp _msec |
|
|
|
@see @link operator""_usec() @endlink, @link operator""_sec() @endlink, |
|
@ref CORRADE_LONG_DOUBLE_SAME_AS_DOUBLE |
|
@m_keywords{_msec msec} |
|
*/ |
|
constexpr Nanoseconds<Long> operator "" _msec(long double value) { |
|
return Nanoseconds<Long>{Long(value*1000000.0l)}; |
|
} |
|
|
|
/** @relatesalso Magnum::Math::Nanoseconds |
|
@brief Second value literal |
|
@m_since_latest |
|
|
|
As the value is converted to whole nanoseconds, everything after billionths is |
|
truncated. Additionally, up to billionths the conversion is without precision |
|
loss only on systems with a 80-bit @cpp long double @ce (which has a 63-bit |
|
mantissa). If you need to ensure nanosecond-level precision on systems that |
|
have a 64-bit @cpp long double @ce, use @link operator""_nsec() @endlink |
|
instead. On the other hand, if nanosecond-level precision isn't needed, it's |
|
possible to convert directly to @ref Seconds that offer a millisecond-level |
|
precision on a range of roughly ±2 hours. For example: |
|
|
|
@snippet Math.cpp _sec |
|
|
|
@see @link operator""_usec() @endlink, @link operator""_msec() @endlink, |
|
@ref CORRADE_LONG_DOUBLE_SAME_AS_DOUBLE |
|
@m_keywords{_sec sec} |
|
*/ |
|
constexpr Nanoseconds<Long> operator "" _sec(long double value) { |
|
return Nanoseconds<Long>{Long(value*1000000000.0l)}; |
|
} |
|
|
|
}} |
|
|
|
template<class T> template<class U> constexpr Nanoseconds<T>::Nanoseconds(Unit<Seconds, U> value) noexcept: Unit<Math::Nanoseconds, T>{T(static_cast<long double>(U(value))*1000000000.0l)} {} |
|
|
|
template<class T> template<class U> constexpr Seconds<T>::Seconds(Unit<Nanoseconds, U> value) noexcept: Unit<Math::Seconds, T>{T(static_cast<long double>(U(value))/1000000000.0l)} {} |
|
|
|
/** |
|
* @debugoperator{Nanoseconds} |
|
* @m_since_latest |
|
*/ |
|
MAGNUM_EXPORT Utility::Debug& operator<<(Utility::Debug& debug, const Unit<Nanoseconds, Long>& value); |
|
|
|
/** |
|
* @debugoperator{Seconds} |
|
* @m_since_latest |
|
*/ |
|
MAGNUM_EXPORT Utility::Debug& operator<<(Utility::Debug& debug, const Unit<Seconds, Float>& value); |
|
|
|
}} |
|
|
|
#endif
|
|
|