#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š 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 #include "Magnum/Magnum.h" #include "Magnum/visibility.h" #include "Magnum/Math/Unit.h" namespace Magnum { namespace Math { namespace Implementation { template struct NanosecondsConverter; template 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 @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" @ref Nanoseconds | ← | @ref std::chrono::time_point @ref Nanoseconds | ⇆ | @ref std::chrono::time_point "std::chrono::time_point>" Example: @snippet Math-stl.cpp Nanoseconds-usage @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 Nanoseconds: public Unit { public: /** * @brief Minimal representable value * * Returns @cpp -0x8000000000000000_nsec @ce. */ constexpr static Nanoseconds min(); /** * @brief Maximal representable value * * Returns @cpp 0x7fffffffffffffff_nsec @ce. */ constexpr static Nanoseconds 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 instead of a Nanoseconds template */ constexpr /*implicit*/ Nanoseconds() noexcept: Unit{ZeroInit} {} /** @brief Construct a zero time */ constexpr explicit Nanoseconds(ZeroInitT) noexcept: Unit{ZeroInit} {} /** @brief Construct without initializing the contents */ explicit Nanoseconds(Magnum::NoInitT) noexcept: Unit{Magnum::NoInit} {} /** @brief Explicit constructor from a unitless type */ constexpr explicit Nanoseconds(T value) noexcept: Unit{value} {} /** @brief Copy constructor */ /* Needed in order to make arithmetic operations (which have a Unit return type) convertible to Nanoseconds */ constexpr /*implicit*/ Nanoseconds(Unit other) noexcept: Unit(other) {} /** * @brief Construct nanoseconds from seconds * * The floating-point value is multiplied by a billion and rounded. */ template constexpr /*implicit*/ Nanoseconds(Unit value) noexcept; /** @brief Construct nanoseconds from external representation */ template::from(std::declval()))> constexpr explicit Nanoseconds(const U& other) noexcept: Nanoseconds{Implementation::NanosecondsConverter::from(other)} {} /** @brief Convert nanoseconds to external representation */ template::to(std::declval>()))> constexpr explicit operator U() const { return Implementation::NanosecondsConverter::to(*this); } }; /* Doxygen can't match these to the class, meh */ #ifndef DOXYGEN_GENERATING_OUTPUT template<> constexpr Nanoseconds Nanoseconds::min() { /* There's apparently no way to say -0x8000000000000000ll. C++, LOL. */ return Nanoseconds{Long(0x8000000000000000ull)}; } template<> constexpr Nanoseconds Nanoseconds::max() { return Nanoseconds{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 Seconds: public Unit { 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 instead of a Seconds template */ constexpr /*implicit*/ Seconds() noexcept: Unit{ZeroInit} {} /** @brief Construct a zero time */ constexpr explicit Seconds(ZeroInitT) noexcept: Unit{ZeroInit} {} /** @brief Construct without initializing the contents */ explicit Seconds(Magnum::NoInitT) noexcept: Unit{Magnum::NoInit} {} /** @brief Explicit constructor from a unitless type */ constexpr explicit Seconds(T value) noexcept: Unit{value} {} /** @brief Copy constructor */ /* Needed in order to make arithmetic operations (which have a Unit return type) convertible to Seconds */ constexpr /*implicit*/ Seconds(Unit other) noexcept: Unit(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 constexpr /*implicit*/ Seconds(Unit value) noexcept; /** @brief Construct seconds from external representation */ template::from(std::declval()))> constexpr explicit Seconds(const U& other) noexcept: Seconds{Implementation::SecondsConverter::from(other)} {} /** @brief Convert seconds to external representation */ template::to(std::declval>()))> constexpr explicit operator U() const { return Implementation::SecondsConverter::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 operator "" _nsec(unsigned long long value) { return Nanoseconds{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 operator "" _usec(long double value) { return Nanoseconds{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 operator "" _msec(long double value) { return Nanoseconds{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 operator "" _sec(long double value) { return Nanoseconds{Long(value*1000000000.0l)}; } }} template template constexpr Nanoseconds::Nanoseconds(Unit value) noexcept: Unit{T(static_cast(U(value))*1000000000.0l)} {} template template constexpr Seconds::Seconds(Unit value) noexcept: Unit{T(static_cast(U(value))/1000000000.0l)} {} /** * @debugoperator{Nanoseconds} * @m_since_latest */ MAGNUM_EXPORT Utility::Debug& operator<<(Utility::Debug& debug, const Unit& value); /** * @debugoperator{Seconds} * @m_since_latest */ MAGNUM_EXPORT Utility::Debug& operator<<(Utility::Debug& debug, const Unit& value); }} #endif