mirror of https://github.com/mosra/magnum.git
Browse Source
Like the Deg / Rad classes, these are for strongly-typed representation of time. Because the current way, either with untyped and imprecise Float, or the insanely-hard-to-use and bloated std::chrono::nanoseconds, was just too crappy. This is just the types alone, corresponding typedefs in the root namespace, and conversion from std::chrono. Using these in the Animation library, in Timeline, in DebugTools::FrameProfiler, GL::TimeQuery etc., will eventually and gradually follow.pull/638/head
15 changed files with 1420 additions and 9 deletions
@ -0,0 +1,246 @@ |
|||||||
|
/*
|
||||||
|
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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Corrade/TestSuite/Tester.h> |
||||||
|
|
||||||
|
#include "Magnum/Math/TimeStl.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Math { namespace Test { namespace { |
||||||
|
|
||||||
|
struct TimeStlTest: TestSuite::Tester { |
||||||
|
explicit TimeStlTest(); |
||||||
|
|
||||||
|
void chronoDurationTypedefs(); |
||||||
|
void chronoDurationFloatingPoint(); |
||||||
|
void chronoTimePoint(); |
||||||
|
}; |
||||||
|
|
||||||
|
TimeStlTest::TimeStlTest() { |
||||||
|
addTests({&TimeStlTest::chronoDurationTypedefs, |
||||||
|
&TimeStlTest::chronoDurationFloatingPoint, |
||||||
|
&TimeStlTest::chronoTimePoint}); |
||||||
|
} |
||||||
|
|
||||||
|
using Magnum::Nanoseconds; |
||||||
|
|
||||||
|
using namespace Math::Literals; |
||||||
|
|
||||||
|
void TimeStlTest::chronoDurationTypedefs() { |
||||||
|
/* Negative values should work as well */ |
||||||
|
std::chrono::nanoseconds a1{1234567891234567890ll}; |
||||||
|
std::chrono::nanoseconds a2{-1234567891234567890ll}; |
||||||
|
/* The rest is implemented in a generic way, so no need to test both
|
||||||
|
variants of every */ |
||||||
|
std::chrono::microseconds b1{4567891234567890ll}; |
||||||
|
std::chrono::milliseconds c1{-7891234567890ll}; |
||||||
|
std::chrono::seconds d1{1234567890ll}; |
||||||
|
std::chrono::minutes e1{-34567890ll}; |
||||||
|
std::chrono::hours f1{567890ll}; |
||||||
|
Nanoseconds a3{a1}; |
||||||
|
Nanoseconds a4{a2}; |
||||||
|
CORRADE_COMPARE(a3, 1234567891234567890_nsec); |
||||||
|
CORRADE_COMPARE(a4, -1234567891234567890_nsec); |
||||||
|
/* Using the _nsec literal to circumvent potential rounding errors when
|
||||||
|
using the _usec etc literals on platforms without an 80-bit long |
||||||
|
double */ |
||||||
|
CORRADE_COMPARE(Nanoseconds{b1}, 4567891234567890000_nsec); |
||||||
|
CORRADE_COMPARE(Nanoseconds{c1}, -7891234567890000000_nsec); |
||||||
|
CORRADE_COMPARE(Nanoseconds{d1}, 1234567890000000000_nsec); |
||||||
|
CORRADE_COMPARE(Nanoseconds{e1}, 60*-34567890000000000_nsec); |
||||||
|
CORRADE_COMPARE(Nanoseconds{f1}, 60*60*567890000000000_nsec); |
||||||
|
|
||||||
|
/* Only nanoseconds can be converted back */ |
||||||
|
std::chrono::nanoseconds a5(a3); |
||||||
|
std::chrono::nanoseconds a6(a4); |
||||||
|
CORRADE_COMPARE(a5.count(), 1234567891234567890ll); |
||||||
|
CORRADE_COMPARE(a6.count(), -1234567891234567890ll); |
||||||
|
|
||||||
|
constexpr std::chrono::nanoseconds ca1{1234567891234567890ll}; |
||||||
|
constexpr std::chrono::nanoseconds ca2{-1234567891234567890ll}; |
||||||
|
constexpr std::chrono::microseconds cb1{4567891234567890ll}; |
||||||
|
constexpr std::chrono::milliseconds cc1{-7891234567890ll}; |
||||||
|
constexpr std::chrono::seconds cd1{1234567890ll}; |
||||||
|
constexpr std::chrono::minutes ce1{-34567890ll}; |
||||||
|
constexpr std::chrono::hours cf1{567890ll}; |
||||||
|
constexpr Nanoseconds ca3{ca1}; |
||||||
|
constexpr Nanoseconds ca4{ca2}; |
||||||
|
constexpr Nanoseconds cb2{cb1}; |
||||||
|
constexpr Nanoseconds cc2{cc1}; |
||||||
|
constexpr Nanoseconds cd2{cd1}; |
||||||
|
constexpr Nanoseconds ce2{ce1}; |
||||||
|
constexpr Nanoseconds cf2{cf1}; |
||||||
|
CORRADE_COMPARE(ca3, 1234567891234567890_nsec); |
||||||
|
CORRADE_COMPARE(ca4, -1234567891234567890_nsec); |
||||||
|
CORRADE_COMPARE(cb2, 4567891234567890000_nsec); |
||||||
|
CORRADE_COMPARE(cc2, -7891234567890000000_nsec); |
||||||
|
CORRADE_COMPARE(cd2, 1234567890000000000_nsec); |
||||||
|
CORRADE_COMPARE(ce2, 60*-34567890000000000_nsec); |
||||||
|
CORRADE_COMPARE(cf2, 60*60*567890000000000_nsec); |
||||||
|
|
||||||
|
constexpr std::chrono::nanoseconds ca5(ca3); |
||||||
|
constexpr std::chrono::nanoseconds ca6(ca4); |
||||||
|
CORRADE_COMPARE(ca5.count(), 1234567891234567890ll); |
||||||
|
CORRADE_COMPARE(ca6.count(), -1234567891234567890ll); |
||||||
|
} |
||||||
|
|
||||||
|
void TimeStlTest::chronoDurationFloatingPoint() { |
||||||
|
/* Same as chronoDurationTypedefs(), except that this is using a
|
||||||
|
floating-point type, for which the precision shouldn't be lost for the |
||||||
|
fractional part. In C++14 with std::chrono_literals this would be |
||||||
|
9087654321987654321.0ns, -9087654321987.654321ms etc. */ |
||||||
|
std::chrono::duration<long double, std::nano> a1{9087654321987654321.0l}; |
||||||
|
std::chrono::duration<long double, std::nano> a2{-9087654321987654321.0l}; |
||||||
|
/* Again, everything except nanoseconds is implemented in a generic way, so
|
||||||
|
no need to test both variants of every */ |
||||||
|
std::chrono::duration<long double, std::micro> b1{9087654321987654.321l}; |
||||||
|
std::chrono::duration<long double, std::milli> c1{-9087654321987.654321l}; |
||||||
|
std::chrono::duration<long double> d1{9087654321.987654321l}; |
||||||
|
Nanoseconds a3{a1}; |
||||||
|
Nanoseconds a4{a2}; |
||||||
|
/* Not sure what Emscripten does here, but it behaves as if long double was
|
||||||
|
actually the full precision. Similar to the logic in |
||||||
|
TimeTest::literals(). */ |
||||||
|
#if !defined(CORRADE_LONG_DOUBLE_SAME_AS_DOUBLE) || defined(CORRADE_TARGET_EMSCRIPTEN) |
||||||
|
CORRADE_COMPARE(a3, 9087654321987654321_nsec); |
||||||
|
CORRADE_COMPARE(a4, -9087654321987654321_nsec); |
||||||
|
CORRADE_COMPARE(Nanoseconds{b1}, 9087654321987654321_nsec); |
||||||
|
/* The conversion suffers from the same minor precision loss as with
|
||||||
|
the floating-point Nanosecond literals themselves. Again see |
||||||
|
TimeTest::literals() for details. */ |
||||||
|
#if (defined(CORRADE_TARGET_ARM) && !defined(CORRADE_TARGET_32BIT)) || defined(CORRADE_TARGET_EMSCRIPTEN) |
||||||
|
CORRADE_COMPARE(Nanoseconds{c1}, -9087654321987654321_nsec); |
||||||
|
CORRADE_COMPARE(Nanoseconds{d1}, 9087654321987654320_nsec); |
||||||
|
#else |
||||||
|
CORRADE_COMPARE(Nanoseconds{c1}, -9087654321987654320_nsec); |
||||||
|
CORRADE_COMPARE(Nanoseconds{d1}, 9087654321987654321_nsec); |
||||||
|
#endif |
||||||
|
#else |
||||||
|
CORRADE_COMPARE(a3, 9087654321987654656_nsec); |
||||||
|
CORRADE_COMPARE(a4, -9087654321987654656_nsec); |
||||||
|
CORRADE_COMPARE(Nanoseconds{b1}, 9087654321987653632_nsec); |
||||||
|
CORRADE_COMPARE(Nanoseconds{c1}, -9087654321987654656_nsec); |
||||||
|
CORRADE_COMPARE(Nanoseconds{d1}, 9087654321987653632_nsec); |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Only nanoseconds can be converted back */ |
||||||
|
/** @todo enable conversion in the other direction for all ratios if using
|
||||||
|
a floating-point representation */ |
||||||
|
std::chrono::duration<long double, std::nano> a5(a3); |
||||||
|
std::chrono::duration<long double, std::nano> a6(a4); |
||||||
|
CORRADE_COMPARE(a5.count(), 9087654321987654321.0l); |
||||||
|
CORRADE_COMPARE(a6.count(), -9087654321987654321.0l); |
||||||
|
|
||||||
|
constexpr std::chrono::duration<long double, std::nano> ca1{9087654321987654321.0l}; |
||||||
|
constexpr std::chrono::duration<long double, std::nano> ca2{-9087654321987654321.0l}; |
||||||
|
constexpr std::chrono::duration<long double, std::micro> cb1{9087654321987654.321l}; |
||||||
|
constexpr std::chrono::duration<long double, std::milli> cc1{-9087654321987.654321l}; |
||||||
|
constexpr std::chrono::duration<long double> cd1{9087654321.987654321l}; |
||||||
|
constexpr Nanoseconds ca3{ca1}; |
||||||
|
constexpr Nanoseconds ca4{ca2}; |
||||||
|
constexpr Nanoseconds cb2{cb1}; |
||||||
|
constexpr Nanoseconds cc2{cc1}; |
||||||
|
constexpr Nanoseconds cd2{cd1}; |
||||||
|
#if !defined(CORRADE_LONG_DOUBLE_SAME_AS_DOUBLE) || defined(CORRADE_TARGET_EMSCRIPTEN) |
||||||
|
CORRADE_COMPARE(ca3, 9087654321987654321_nsec); |
||||||
|
CORRADE_COMPARE(ca4, -9087654321987654321_nsec); |
||||||
|
CORRADE_COMPARE(cb2, 9087654321987654321_nsec); |
||||||
|
#if (defined(CORRADE_TARGET_ARM) && !defined(CORRADE_TARGET_32BIT)) || defined(CORRADE_TARGET_EMSCRIPTEN) |
||||||
|
CORRADE_COMPARE(cc2, -9087654321987654321_nsec); |
||||||
|
CORRADE_COMPARE(cd2, 9087654321987654320_nsec); |
||||||
|
#else |
||||||
|
CORRADE_COMPARE(cc2, -9087654321987654320_nsec); |
||||||
|
CORRADE_COMPARE(cd2, 9087654321987654321_nsec); |
||||||
|
#endif |
||||||
|
#else |
||||||
|
CORRADE_COMPARE(ca3, 9087654321987654656_nsec); |
||||||
|
CORRADE_COMPARE(ca4, -9087654321987654656_nsec); |
||||||
|
CORRADE_COMPARE(cb2, 9087654321987653632_nsec); |
||||||
|
CORRADE_COMPARE(cc2, -9087654321987654656_nsec); |
||||||
|
CORRADE_COMPARE(cd2, 9087654321987653632_nsec); |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Only nanoseconds can be converted back */ |
||||||
|
/** @todo enable conversion in the other direction for all ratios if using
|
||||||
|
a floating-point representation */ |
||||||
|
constexpr std::chrono::duration<long double, std::nano> ca5(ca3); |
||||||
|
constexpr std::chrono::duration<long double, std::nano> ca6(ca4); |
||||||
|
CORRADE_COMPARE(ca5.count(), 9087654321987654321.0l); |
||||||
|
CORRADE_COMPARE(ca6.count(), -9087654321987654321.0l); |
||||||
|
} |
||||||
|
|
||||||
|
void TimeStlTest::chronoTimePoint() { |
||||||
|
std::chrono::system_clock::time_point systemNow = std::chrono::system_clock::now(); |
||||||
|
std::chrono::steady_clock::time_point steadyNow = std::chrono::steady_clock::now(); |
||||||
|
std::chrono::high_resolution_clock::time_point highNow = std::chrono::high_resolution_clock::now(); |
||||||
|
|
||||||
|
Nanoseconds system{systemNow}; |
||||||
|
Nanoseconds steady{steadyNow}; |
||||||
|
Nanoseconds high{highNow}; |
||||||
|
CORRADE_COMPARE(Long(system), std::chrono::duration_cast<std::chrono::nanoseconds>(systemNow.time_since_epoch()).count()); |
||||||
|
CORRADE_COMPARE(Long(steady), std::chrono::duration_cast<std::chrono::nanoseconds>(steadyNow.time_since_epoch()).count()); |
||||||
|
CORRADE_COMPARE(Long(high), std::chrono::duration_cast<std::chrono::nanoseconds>(highNow.time_since_epoch()).count()); |
||||||
|
|
||||||
|
/* Conversion back is possible only if the STL clock is in nanoseconds.
|
||||||
|
That's the case for all three in libstdc++. In libc++ the system_clock |
||||||
|
has only a microsecond precision: |
||||||
|
https://github.com/llvm/llvm-project/blob/44d85c5b15bbf6226f442126735b764d81cbf6e3/libcxx/include/__chrono/system_clock.h#L28
|
||||||
|
In MSVC STL system_clock has a 100-nanosecond resolution: |
||||||
|
https://github.com/microsoft/STL/blob/192a84008a59ac4d2e55681e1ffac73535788674/stl/inc/__msvc_chrono.hpp#L636.
|
||||||
|
|
||||||
|
std::ostream operators for std::chrono::duration are only available |
||||||
|
since C++20 so the comparison is this insane. */ |
||||||
|
#if !defined(CORRADE_TARGET_LIBCXX) && !defined(CORRADE_TARGET_DINKUMWARE) |
||||||
|
std::chrono::system_clock::time_point systemAgain(system); |
||||||
|
#endif |
||||||
|
std::chrono::steady_clock::time_point steadyAgain(steady); |
||||||
|
std::chrono::high_resolution_clock::time_point highAgain(high); |
||||||
|
#if !defined(CORRADE_TARGET_LIBCXX) && !defined(CORRADE_TARGET_DINKUMWARE) |
||||||
|
CORRADE_COMPARE(systemAgain.time_since_epoch().count(), systemNow.time_since_epoch().count()); |
||||||
|
#endif |
||||||
|
CORRADE_COMPARE(steadyAgain.time_since_epoch().count(), steadyNow.time_since_epoch().count()); |
||||||
|
CORRADE_COMPARE(highAgain.time_since_epoch().count(), highNow.time_since_epoch().count()); |
||||||
|
|
||||||
|
/* Constexpr variants with a custom value. Spec says the time_point
|
||||||
|
constructor is constexpr since C++14. Libstdc++ and MSVC STL has them |
||||||
|
since C++11 already, libc++ explicitly only since C++14. */ |
||||||
|
#ifndef CORRADE_TARGET_LIBCXX |
||||||
|
constexpr |
||||||
|
#endif |
||||||
|
std::chrono::high_resolution_clock::time_point ca{std::chrono::nanoseconds{1234567891234567890ll}}; |
||||||
|
#ifndef CORRADE_TARGET_LIBCXX |
||||||
|
constexpr |
||||||
|
#endif |
||||||
|
Nanoseconds cb{ca}; |
||||||
|
#ifndef CORRADE_TARGET_LIBCXX |
||||||
|
constexpr |
||||||
|
#endif |
||||||
|
std::chrono::high_resolution_clock::time_point cc{cb}; |
||||||
|
CORRADE_COMPARE(Long(cb),std::chrono::duration_cast<std::chrono::nanoseconds>(ca.time_since_epoch()).count()); |
||||||
|
CORRADE_COMPARE(cc.time_since_epoch().count(), ca.time_since_epoch().count()); |
||||||
|
} |
||||||
|
|
||||||
|
}}}} |
||||||
|
|
||||||
|
CORRADE_TEST_MAIN(Magnum::Math::Test::TimeStlTest) |
||||||
@ -0,0 +1,442 @@ |
|||||||
|
/*
|
||||||
|
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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <new> |
||||||
|
#include <sstream> /** @todo remove once Debug is STL-free */ |
||||||
|
#include <Corrade/TestSuite/Tester.h> |
||||||
|
#include <Corrade/Utility/DebugStl.h> /** @todo remove once Debug is STL-free */ |
||||||
|
|
||||||
|
#include "Magnum/Math/Time.h" |
||||||
|
|
||||||
|
struct Time { |
||||||
|
unsigned secondsSinceEpoch; |
||||||
|
}; |
||||||
|
|
||||||
|
struct Keyframe { |
||||||
|
float duration; |
||||||
|
}; |
||||||
|
|
||||||
|
namespace Magnum { namespace Math { |
||||||
|
|
||||||
|
namespace Implementation { |
||||||
|
|
||||||
|
template<> struct NanosecondsConverter<Long, Time> { |
||||||
|
constexpr static Nanoseconds<Long> from(Time other) { |
||||||
|
return Nanoseconds<Long>{Long(other.secondsSinceEpoch)*1000000000ll}; |
||||||
|
} |
||||||
|
constexpr static Time to(Nanoseconds<Long> other) { |
||||||
|
return {unsigned(Long(other)/1000000000ll)}; |
||||||
|
} |
||||||
|
}; |
||||||
|
template<> struct SecondsConverter<Float, Keyframe> { |
||||||
|
constexpr static Seconds<Float> from(Keyframe other) { |
||||||
|
return Seconds<Float>{other.duration}; |
||||||
|
} |
||||||
|
constexpr static Keyframe to(Seconds<Float> other) { |
||||||
|
return {Float(other)}; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
namespace Test { namespace { |
||||||
|
|
||||||
|
struct TimeTest: TestSuite::Tester { |
||||||
|
explicit TimeTest(); |
||||||
|
|
||||||
|
void limits(); |
||||||
|
|
||||||
|
void construct(); |
||||||
|
void constructDefault(); |
||||||
|
void constructNoInit(); |
||||||
|
void constructCopy(); |
||||||
|
void constructFromBase(); |
||||||
|
void convert(); |
||||||
|
|
||||||
|
void literals(); |
||||||
|
void conversion(); |
||||||
|
void nanosecondFloatScaling(); |
||||||
|
|
||||||
|
void debugNanoseconds(); |
||||||
|
void debugNanosecondsPacked(); |
||||||
|
void debugSeconds(); |
||||||
|
void debugSecondsPacked(); |
||||||
|
}; |
||||||
|
|
||||||
|
using Magnum::Nanoseconds; |
||||||
|
using Magnum::Seconds; |
||||||
|
|
||||||
|
using namespace Math::Literals; |
||||||
|
|
||||||
|
TimeTest::TimeTest() { |
||||||
|
addTests({&TimeTest::limits, |
||||||
|
|
||||||
|
&TimeTest::construct, |
||||||
|
&TimeTest::constructDefault, |
||||||
|
&TimeTest::constructNoInit, |
||||||
|
&TimeTest::constructCopy, |
||||||
|
&TimeTest::constructFromBase, |
||||||
|
&TimeTest::convert, |
||||||
|
|
||||||
|
&TimeTest::literals, |
||||||
|
&TimeTest::conversion, |
||||||
|
&TimeTest::nanosecondFloatScaling, |
||||||
|
|
||||||
|
&TimeTest::debugNanoseconds, |
||||||
|
&TimeTest::debugNanosecondsPacked, |
||||||
|
&TimeTest::debugSeconds, |
||||||
|
&TimeTest::debugSecondsPacked}); |
||||||
|
} |
||||||
|
|
||||||
|
void TimeTest::limits() { |
||||||
|
/* There's apparently no way to say -0x8000000000000000ll, so there's also
|
||||||
|
no non-error-prone way to verify the values are correct. */ |
||||||
|
|
||||||
|
/* It should be all 64 bits (so 16 nibbles) being set */ |
||||||
|
CORRADE_COMPARE(UnsignedLong(Long(Nanoseconds::min()))| |
||||||
|
UnsignedLong(Long(Nanoseconds::max())), |
||||||
|
/* 0123456789abcdef */ |
||||||
|
0xffffffffffffffffull); |
||||||
|
|
||||||
|
/* Assuming signed integer overflow is defined sanely, which it should */ |
||||||
|
CORRADE_COMPARE(Nanoseconds::min() - 1_nsec, Nanoseconds::max()); |
||||||
|
CORRADE_COMPARE(Nanoseconds::max() + 1_nsec, Nanoseconds::min()); |
||||||
|
|
||||||
|
/* This should also hold */ |
||||||
|
CORRADE_COMPARE(Nanoseconds::min() + Nanoseconds::max(), -1_nsec); |
||||||
|
CORRADE_COMPARE(Nanoseconds::max() - Nanoseconds::min(), -1_nsec); |
||||||
|
} |
||||||
|
|
||||||
|
void TimeTest::construct() { |
||||||
|
Nanoseconds a{-123456789123456789ll}; |
||||||
|
Seconds b{123.45f}; |
||||||
|
CORRADE_COMPARE(Long(a), -123456789123456789ll); |
||||||
|
CORRADE_COMPARE(Float(b), 123.45f); |
||||||
|
|
||||||
|
constexpr Nanoseconds ca{-123456789123456789ll}; |
||||||
|
constexpr Seconds cb{123.45f}; |
||||||
|
CORRADE_COMPARE(Long(ca), -123456789123456789ll); |
||||||
|
CORRADE_COMPARE(Float(cb), 123.45f); |
||||||
|
|
||||||
|
/* Implicit conversion is not allowed */ |
||||||
|
CORRADE_VERIFY(!std::is_convertible<Long, Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(!std::is_convertible<Float, Seconds>::value); |
||||||
|
|
||||||
|
CORRADE_VERIFY(std::is_nothrow_constructible<Nanoseconds, Long>::value); |
||||||
|
CORRADE_VERIFY(std::is_nothrow_constructible<Seconds, Float>::value); |
||||||
|
} |
||||||
|
|
||||||
|
void TimeTest::constructDefault() { |
||||||
|
Nanoseconds a1; |
||||||
|
Nanoseconds a2{ZeroInit}; |
||||||
|
Seconds b1; |
||||||
|
Seconds b2{ZeroInit}; |
||||||
|
CORRADE_COMPARE(Long(a1), 0ll); |
||||||
|
CORRADE_COMPARE(Long(a2), 0ll); |
||||||
|
CORRADE_COMPARE(Float(b1), 0.0f); |
||||||
|
CORRADE_COMPARE(Float(b2), 0.0f); |
||||||
|
|
||||||
|
constexpr Nanoseconds ca1; |
||||||
|
constexpr Nanoseconds ca2{ZeroInit}; |
||||||
|
constexpr Seconds cb1; |
||||||
|
constexpr Seconds cb2{ZeroInit}; |
||||||
|
CORRADE_COMPARE(Long(ca1), 0ll); |
||||||
|
CORRADE_COMPARE(Long(ca2), 0ll); |
||||||
|
CORRADE_COMPARE(Float(cb1), 0.0f); |
||||||
|
CORRADE_COMPARE(Float(cb2), 0.0f); |
||||||
|
|
||||||
|
CORRADE_VERIFY(std::is_nothrow_default_constructible<Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_nothrow_default_constructible<Seconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_nothrow_constructible<Nanoseconds, ZeroInitT>::value); |
||||||
|
CORRADE_VERIFY(std::is_nothrow_constructible<Seconds, ZeroInitT>::value); |
||||||
|
|
||||||
|
/* Implicit construction is not allowed */ |
||||||
|
CORRADE_VERIFY(!std::is_convertible<ZeroInitT, Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(!std::is_convertible<ZeroInitT, Seconds>::value); |
||||||
|
} |
||||||
|
|
||||||
|
void TimeTest::constructNoInit() { |
||||||
|
Nanoseconds a{123456789123456789ll}; |
||||||
|
Seconds b{123.45f}; |
||||||
|
new(&a) Nanoseconds{Magnum::NoInit}; |
||||||
|
new(&b) Seconds{Magnum::NoInit}; |
||||||
|
{ |
||||||
|
/* Explicitly check we're not on Clang because certain Clang-based IDEs
|
||||||
|
inherit __GNUC__ if GCC is used instead of leaving it at 4 like |
||||||
|
Clang itself does */ |
||||||
|
#if defined(CORRADE_TARGET_GCC) && !defined(CORRADE_TARGET_CLANG) && __GNUC__*100 + __GNUC_MINOR__ >= 601 |
||||||
|
/* The warning is reported for both debug and release build */ |
||||||
|
#pragma GCC diagnostic push |
||||||
|
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" |
||||||
|
/* On GCC 13 it's -Wuninitialized now, the compiler is now EVEN MORE
|
||||||
|
DEFINITELY RIGHT that I'm doing something wrong */ |
||||||
|
#if __GNUC__ >= 13 |
||||||
|
#pragma GCC diagnostic ignored "-Wuninitialized" |
||||||
|
#endif |
||||||
|
#ifdef __OPTIMIZE__ |
||||||
|
CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
CORRADE_COMPARE(Long(a), 123456789123456789ll); |
||||||
|
CORRADE_COMPARE(Float(b), 123.45f); |
||||||
|
#if defined(CORRADE_TARGET_GCC) && !defined(CORRADE_TARGET_CLANG) && __GNUC__*100 + __GNUC_MINOR__ >= 601 |
||||||
|
#pragma GCC diagnostic pop |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
CORRADE_VERIFY(std::is_nothrow_constructible<Nanoseconds, Magnum::NoInitT>::value); |
||||||
|
CORRADE_VERIFY(std::is_nothrow_constructible<Seconds, Magnum::NoInitT>::value); |
||||||
|
|
||||||
|
/* Implicit construction is not allowed */ |
||||||
|
CORRADE_VERIFY(!std::is_convertible<Magnum::NoInitT, Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(!std::is_convertible<Magnum::NoInitT, Seconds>::value); |
||||||
|
} |
||||||
|
|
||||||
|
void TimeTest::constructCopy() { |
||||||
|
Nanoseconds a{-987654321987654321ll}; |
||||||
|
Nanoseconds b{a}; |
||||||
|
Seconds c{-543.21f}; |
||||||
|
Seconds d{c}; |
||||||
|
CORRADE_COMPARE(Long(b), -987654321987654321ll); |
||||||
|
CORRADE_COMPARE(Float(d), -543.21f); |
||||||
|
|
||||||
|
constexpr Nanoseconds ca{-987654321987654321ll}; |
||||||
|
constexpr Nanoseconds cb{ca}; |
||||||
|
constexpr Seconds cc{-543.21f}; |
||||||
|
constexpr Seconds cd{cc}; |
||||||
|
CORRADE_COMPARE(Long(cb), -987654321987654321ll); |
||||||
|
CORRADE_COMPARE(Float(cd), -543.21f); |
||||||
|
|
||||||
|
#ifndef CORRADE_NO_STD_IS_TRIVIALLY_TRAITS |
||||||
|
CORRADE_VERIFY(std::is_trivially_copy_constructible<Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_trivially_copy_constructible<Seconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_trivially_copy_assignable<Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_trivially_copy_assignable<Seconds>::value); |
||||||
|
#endif |
||||||
|
CORRADE_VERIFY(std::is_nothrow_copy_constructible<Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_nothrow_copy_constructible<Seconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_nothrow_copy_assignable<Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_nothrow_copy_assignable<Seconds>::value); |
||||||
|
} |
||||||
|
|
||||||
|
void TimeTest::constructFromBase() { |
||||||
|
/* The operation returns Unit instead of the leaf type, so this can work
|
||||||
|
only if the base class has a "copy constructor" from the base type */ |
||||||
|
Nanoseconds a = 15.0_usec + 3.5_msec; |
||||||
|
Seconds b = Seconds{15.0_msec} + Seconds{3.5_sec}; |
||||||
|
CORRADE_COMPARE(a, 3.515_msec); |
||||||
|
/* Comparing as seconds because precision loss involved */ |
||||||
|
CORRADE_COMPARE_AS(b, 3.515_sec, Seconds); |
||||||
|
} |
||||||
|
|
||||||
|
void TimeTest::convert() { |
||||||
|
/* From external type */ |
||||||
|
Time a0{1707678819}; |
||||||
|
Keyframe b0{56.72f}; |
||||||
|
Nanoseconds a1{a0}; |
||||||
|
Seconds b1{b0}; |
||||||
|
CORRADE_COMPARE(a1, 1707678819.0_sec); |
||||||
|
CORRADE_COMPARE_AS(b1, 56.72_sec, Seconds); |
||||||
|
|
||||||
|
constexpr Time ca0{1707678819}; |
||||||
|
constexpr Keyframe cb0{56.72f}; |
||||||
|
constexpr Nanoseconds ca1{ca0}; |
||||||
|
constexpr Seconds cb1{cb0}; |
||||||
|
CORRADE_COMPARE(ca1, 1707678819.0_sec); |
||||||
|
CORRADE_COMPARE_AS(cb1, 56.72_sec, Seconds); |
||||||
|
|
||||||
|
/* To external type */ |
||||||
|
Nanoseconds c0 = 1707678819.0_sec; |
||||||
|
Seconds d0 = 56.72_sec; |
||||||
|
Time c1(c0); |
||||||
|
Keyframe d1(d0); |
||||||
|
CORRADE_COMPARE(c1.secondsSinceEpoch, 1707678819); |
||||||
|
CORRADE_COMPARE(d1.duration, 56.72f); |
||||||
|
|
||||||
|
constexpr Nanoseconds cc0 = 1707678819.0_sec; |
||||||
|
constexpr Seconds cd0 = 56.72_sec; |
||||||
|
constexpr Time cc1(cc0); |
||||||
|
constexpr Keyframe cd1(cd0); |
||||||
|
CORRADE_COMPARE(cc1.secondsSinceEpoch, 1707678819); |
||||||
|
CORRADE_COMPARE(cd1.duration, 56.72f); |
||||||
|
|
||||||
|
/* It should not be possible to convert in a direction that may result in a
|
||||||
|
precision loss, i.e. Seconds with a NanosecondsConverter */ |
||||||
|
CORRADE_VERIFY(std::is_constructible<Seconds, Keyframe>::value); |
||||||
|
CORRADE_VERIFY(!std::is_constructible<Seconds, Time>::value); |
||||||
|
CORRADE_VERIFY(std::is_constructible<Nanoseconds, Time>::value); |
||||||
|
CORRADE_VERIFY(!std::is_constructible<Nanoseconds, Keyframe>::value); |
||||||
|
CORRADE_VERIFY(std::is_constructible<Time, Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(!std::is_constructible<Time, Seconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_constructible<Keyframe, Seconds>::value); |
||||||
|
CORRADE_VERIFY(!std::is_constructible<Keyframe, Nanoseconds>::value); |
||||||
|
|
||||||
|
/* Implicit conversion is not allowed */ |
||||||
|
CORRADE_VERIFY(!std::is_convertible<Keyframe, Seconds>::value); |
||||||
|
CORRADE_VERIFY(!std::is_convertible<Time, Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(!std::is_convertible<Nanoseconds, Time>::value); |
||||||
|
CORRADE_VERIFY(!std::is_convertible<Seconds, Keyframe>::value); |
||||||
|
} |
||||||
|
|
||||||
|
void TimeTest::literals() { |
||||||
|
/* Testing the full precision, 19 digits. Max representable 63-bit value is
|
||||||
|
9223372036854775807. */ |
||||||
|
auto a = 9087654321987654321_nsec; |
||||||
|
auto b = 9087654321987654.321_usec; |
||||||
|
auto c = 9087654321987.654321_msec; |
||||||
|
auto d = 9087654321.987654321_sec; |
||||||
|
CORRADE_VERIFY(std::is_same<decltype(a), Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_same<decltype(b), Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_same<decltype(c), Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_same<decltype(d), Nanoseconds>::value); |
||||||
|
CORRADE_COMPARE(Long(a), 9087654321987654321ll); |
||||||
|
/* Not sure what Emscripten does here, but it behaves as if long double was
|
||||||
|
actually the full precision */ |
||||||
|
#if !defined(CORRADE_LONG_DOUBLE_SAME_AS_DOUBLE) || defined(CORRADE_TARGET_EMSCRIPTEN) |
||||||
|
/* 80-bit long double has a 63-bit mantissa, which means this is converted
|
||||||
|
without any precision loss. Otherwise the precision is just 52 bits. */ |
||||||
|
CORRADE_COMPARE(Long(b), 9087654321987654321ll); |
||||||
|
/* Well, almost. On x86 this conversion has a slight imprecision in the
|
||||||
|
lowest bit for the _ms variant, on ARM64 and Emscripten for the _s |
||||||
|
variant. */ |
||||||
|
#if (defined(CORRADE_TARGET_ARM) && !defined(CORRADE_TARGET_32BIT)) || defined(CORRADE_TARGET_EMSCRIPTEN) |
||||||
|
CORRADE_COMPARE(Long(c), 9087654321987654321ll); |
||||||
|
CORRADE_COMPARE(Long(d), 9087654321987654320ll); |
||||||
|
#else |
||||||
|
CORRADE_COMPARE(Long(c), 9087654321987654320ll); |
||||||
|
CORRADE_COMPARE(Long(d), 9087654321987654321ll); |
||||||
|
#endif |
||||||
|
#else |
||||||
|
CORRADE_COMPARE(Long(b), 9087654321987653632ll); |
||||||
|
CORRADE_COMPARE(Long(c), 9087654321987654656ll); |
||||||
|
CORRADE_COMPARE(Long(d), 9087654321987653632ll); |
||||||
|
#endif |
||||||
|
|
||||||
|
constexpr auto ca = 9087654321987654321_nsec; |
||||||
|
constexpr auto cb = 9087654321987654.321_usec; |
||||||
|
constexpr auto cc = 9087654321987.654321_msec; |
||||||
|
constexpr auto cd = 9087654321.987654321_sec; |
||||||
|
CORRADE_VERIFY(std::is_same<decltype(ca), const Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_same<decltype(cb), const Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_same<decltype(cc), const Nanoseconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_same<decltype(cd), const Nanoseconds>::value); |
||||||
|
CORRADE_COMPARE(Long(ca), 9087654321987654321ll); |
||||||
|
#if !defined(CORRADE_LONG_DOUBLE_SAME_AS_DOUBLE) || defined(CORRADE_TARGET_EMSCRIPTEN) |
||||||
|
CORRADE_COMPARE(Long(cb), 9087654321987654321ll); |
||||||
|
#if (defined(CORRADE_TARGET_ARM) && !defined(CORRADE_TARGET_32BIT)) || defined(CORRADE_TARGET_EMSCRIPTEN) |
||||||
|
CORRADE_COMPARE(Long(cc), 9087654321987654321ll); |
||||||
|
CORRADE_COMPARE(Long(cd), 9087654321987654320ll); |
||||||
|
#else |
||||||
|
CORRADE_COMPARE(Long(cc), 9087654321987654320ll); |
||||||
|
CORRADE_COMPARE(Long(cd), 9087654321987654321ll); |
||||||
|
#endif |
||||||
|
#else |
||||||
|
CORRADE_COMPARE(Long(cb), 9087654321987653632ll); |
||||||
|
CORRADE_COMPARE(Long(cc), 9087654321987654656ll); |
||||||
|
CORRADE_COMPARE(Long(cd), 9087654321987653632ll); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
void TimeTest::conversion() { |
||||||
|
/* Implicit conversion should be allowed. Again testing (almost) the full
|
||||||
|
nanosecond precision, although not much of it is left when converting |
||||||
|
to a 32-bit float. */ |
||||||
|
Nanoseconds a = Seconds{-987654321.987654321f}; |
||||||
|
Seconds b = 987654321987654321_nsec; |
||||||
|
CORRADE_COMPARE(Long(a), -987654336000000000ll); |
||||||
|
CORRADE_COMPARE(Float(b), 987654336.0f); |
||||||
|
|
||||||
|
constexpr Nanoseconds ca = Seconds{987654321.987654321f}; |
||||||
|
constexpr Seconds cb = -987654321987654321_nsec; |
||||||
|
CORRADE_COMPARE(Long(ca), 987654336000000000ll); |
||||||
|
CORRADE_COMPARE(Float(cb), -987654336.0f); |
||||||
|
|
||||||
|
CORRADE_VERIFY(std::is_nothrow_constructible<Nanoseconds, Seconds>::value); |
||||||
|
CORRADE_VERIFY(std::is_nothrow_constructible<Seconds, Nanoseconds>::value); |
||||||
|
} |
||||||
|
|
||||||
|
void TimeTest::nanosecondFloatScaling() { |
||||||
|
/* Nanoseconds is an integer type, but multiplying it with float should
|
||||||
|
give a reasonable output. The actual logic is in the Unit class, just |
||||||
|
verify that it works from the high level perspective here. */ |
||||||
|
|
||||||
|
CORRADE_COMPARE(1000000000_nsec*1.25, 1250000000_nsec); |
||||||
|
CORRADE_COMPARE(1000000000_nsec*1.25f, 1250000000_nsec); |
||||||
|
CORRADE_COMPARE(1000000000_nsec/0.8, 1250000000_nsec); |
||||||
|
CORRADE_COMPARE(1000000000_nsec/0.8f, 1250000000_nsec); |
||||||
|
|
||||||
|
/* Compared to above this looks like it should "obviously work", although
|
||||||
|
internally both cases are the same, operating on Nanoseconds */ |
||||||
|
CORRADE_COMPARE(1.0_sec*1.25, 1.25_sec); |
||||||
|
CORRADE_COMPARE(1.0_sec*1.25f, 1.25_sec); |
||||||
|
CORRADE_COMPARE(1.0_sec/0.8, 1.25_sec); |
||||||
|
CORRADE_COMPARE(1.0_sec/0.8f, 1.25_sec); |
||||||
|
|
||||||
|
/* This would be nice if it worked, but so far it doesn't, as it's
|
||||||
|
calculated as an integer value */ |
||||||
|
CORRADE_COMPARE(1.0f/0.018f, 55.5556f); |
||||||
|
{ |
||||||
|
CORRADE_EXPECT_FAIL("This doesn't work correctly."); |
||||||
|
CORRADE_COMPARE(1.0_sec/18.0_msec, 55.5556f); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void TimeTest::debugNanoseconds() { |
||||||
|
std::ostringstream out; |
||||||
|
|
||||||
|
/* Also verify that the second expression compiles (it's the Unit type,
|
||||||
|
not Nanoseconds) */ |
||||||
|
Debug{&out} << 987654321987654321_nsec << 15.0_sec - 7.5_sec; |
||||||
|
CORRADE_COMPARE(out.str(), "Nanoseconds(987654321987654321) Nanoseconds(7500000000)\n"); |
||||||
|
} |
||||||
|
|
||||||
|
void TimeTest::debugNanosecondsPacked() { |
||||||
|
std::ostringstream out; |
||||||
|
|
||||||
|
/* Second is not packed, the first should not make any flags persistent */ |
||||||
|
Debug{&out} << Debug::packed << 15.0_sec << 45.0_sec; |
||||||
|
CORRADE_COMPARE(out.str(), "15000000000 Nanoseconds(45000000000)\n"); |
||||||
|
} |
||||||
|
|
||||||
|
void TimeTest::debugSeconds() { |
||||||
|
std::ostringstream out; |
||||||
|
|
||||||
|
/* Also verify that the second expression compiles (it's the Unit type,
|
||||||
|
not Nanoseconds) */ |
||||||
|
Debug{&out} << Seconds{123.45_sec} << Seconds{15.0_sec} - Seconds{7.5_sec}; |
||||||
|
CORRADE_COMPARE(out.str(), "Seconds(123.45) Seconds(7.5)\n"); |
||||||
|
} |
||||||
|
|
||||||
|
void TimeTest::debugSecondsPacked() { |
||||||
|
std::ostringstream out; |
||||||
|
|
||||||
|
/* Second is not packed, the first should not make any flags persistent */ |
||||||
|
Debug{&out} << Debug::packed << Seconds{123.45_sec} << Seconds{45.0_sec}; |
||||||
|
CORRADE_COMPARE(out.str(), "123.45 Seconds(45)\n"); |
||||||
|
} |
||||||
|
|
||||||
|
}}}} |
||||||
|
|
||||||
|
CORRADE_TEST_MAIN(Magnum::Math::Test::TimeTest) |
||||||
@ -0,0 +1,44 @@ |
|||||||
|
/*
|
||||||
|
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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Time.h" |
||||||
|
|
||||||
|
#include <Corrade/Utility/Debug.h> |
||||||
|
|
||||||
|
namespace Magnum { namespace Math { |
||||||
|
|
||||||
|
Utility::Debug& operator<<(Utility::Debug& debug, const Unit<Nanoseconds, Long>& value) { |
||||||
|
if(debug.immediateFlags() >= Utility::Debug::Flag::Packed) |
||||||
|
return debug << Long(value); |
||||||
|
return debug << "Nanoseconds(" << Utility::Debug::nospace << Long(value) << Utility::Debug::nospace << ")"; |
||||||
|
} |
||||||
|
|
||||||
|
Utility::Debug& operator<<(Utility::Debug& debug, const Unit<Seconds, Float>& value) { |
||||||
|
if(debug.immediateFlags() >= Utility::Debug::Flag::Packed) |
||||||
|
return debug << Float(value); |
||||||
|
return debug << "Seconds(" << Utility::Debug::nospace << Float(value) << Utility::Debug::nospace << ")"; |
||||||
|
} |
||||||
|
|
||||||
|
}} |
||||||
@ -0,0 +1,398 @@ |
|||||||
|
#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 MagnumMath.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 MagnumMath-stl.cpp Nanoseconds-usage-convert |
||||||
|
|
||||||
|
The classes support all arithmetic operations, such as addition, subtraction |
||||||
|
or multiplication/division by a unitless number: |
||||||
|
|
||||||
|
@snippet MagnumMath.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 MagnumMath.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 MagnumMath-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 MagnumMath-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 MagnumMath.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 MagnumMath.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 MagnumMath.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 MagnumMath.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 |
||||||
@ -0,0 +1,87 @@ |
|||||||
|
#ifndef Magnum_Math_TimeStl_h |
||||||
|
#define Magnum_Math_TimeStl_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 STL @ref std::chrono compatibility for @ref Magnum::Math::Nanoseconds |
||||||
|
@m_since_latest |
||||||
|
|
||||||
|
Including this header allows you to convert a |
||||||
|
@ref Magnum::Math::Nanoseconds from and to @ref std::chrono::duration and |
||||||
|
@ref std::chrono::time_point. See @ref Math-Nanoseconds-stl for more |
||||||
|
information. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <chrono> |
||||||
|
|
||||||
|
#include "Magnum/Math/Time.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Math { namespace Implementation { |
||||||
|
|
||||||
|
/* There's no NanosecondsConverter<Long, std::time_t> because this is a typedef
|
||||||
|
to some integral type, which when simply picks the Nanoseconds(Long) |
||||||
|
constructor and not a NanosecondsConverter. C types yay. */ |
||||||
|
|
||||||
|
template<class Rep, std::intmax_t num, std::intmax_t denom> struct NanosecondsConverter<Long, std::chrono::duration<Rep, std::ratio<num, denom>>> { |
||||||
|
constexpr static Nanoseconds<Long> from(std::chrono::duration<Rep, std::ratio<num, denom>> other) { |
||||||
|
/* The Rep can be floating-point, truncate just the integral part but
|
||||||
|
only after converting to nanoseconds */ |
||||||
|
return Nanoseconds<Long>{Long(other.count()*num*(1000000000ll/denom))}; |
||||||
|
} |
||||||
|
/* No to() because it can be an integer type, losing precision */ |
||||||
|
/** @todo add a floating-point-only to() once desirable */ |
||||||
|
}; |
||||||
|
template<class Rep> struct NanosecondsConverter<Long, std::chrono::duration<Rep, std::nano>> { |
||||||
|
constexpr static Nanoseconds<Long> from(std::chrono::duration<Rep, std::nano> other) { |
||||||
|
/* The Rep can be floating-point, truncate just the integral part --
|
||||||
|
we don't have anything for sub-nanosecond precision */ |
||||||
|
return Nanoseconds<Long>{Long(other.count())}; |
||||||
|
} |
||||||
|
constexpr static std::chrono::duration<Rep, std::nano> to(Nanoseconds<Long> other) { |
||||||
|
return std::chrono::duration<Rep, std::nano>{Long(other)}; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
template<class Clock, class Rep, std::intmax_t num, std::intmax_t denom> struct NanosecondsConverter<Long, std::chrono::time_point<Clock, std::chrono::duration<Rep, std::ratio<num, denom>>>> { |
||||||
|
constexpr static Nanoseconds<Long> from(std::chrono::time_point<Clock, std::chrono::duration<Rep, std::ratio<num, denom>>> other) { |
||||||
|
return Nanoseconds<Long>{other.time_since_epoch().count()*num*(1000000000ll/denom)}; |
||||||
|
} |
||||||
|
/* No to() because it's an integer type, losing precision */ |
||||||
|
/** @todo is there even any std::chrono::time_point that would use a FP
|
||||||
|
duration type? */ |
||||||
|
}; |
||||||
|
template<class Clock, class Rep> struct NanosecondsConverter<Long, std::chrono::time_point<Clock, std::chrono::duration<Rep, std::nano>>> { |
||||||
|
constexpr static Nanoseconds<Long> from(std::chrono::time_point<Clock, std::chrono::duration<Rep, std::nano>> other) { |
||||||
|
return Nanoseconds<Long>{other.time_since_epoch().count()}; |
||||||
|
} |
||||||
|
constexpr static std::chrono::time_point<Clock, std::chrono::duration<Rep, std::nano>> to(Nanoseconds<Long> other) { |
||||||
|
return std::chrono::time_point<Clock, std::chrono::duration<Rep, std::nano>>{std::chrono::duration<Rep, std::nano>{Long(other)}}; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
}}} |
||||||
|
|
||||||
|
#endif |
||||||
Loading…
Reference in new issue