SFML icon indicating copy to clipboard operation
SFML copied to clipboard

Improve `sf::Time` interoperability with `<chrono>`

Open ChrisThrasher opened this issue 2 years ago • 17 comments

Description

This PR implements the ideas outlined here. 100% code coverage is maintained for sf::Time.

One important design note is how the constructor from std::chrono::duration does not perform a duration_cast. This is intentional because I don't want to make it too easy for users to provide a duration type that requires a lossy conversion. The onus is on the user to do that lossy conversion.

ChrisThrasher avatar Jun 17 '22 00:06 ChrisThrasher

Codecov Report

Merging #2133 (9b4efc7) into master (72d8803) will increase coverage by 0.04%. The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2133      +/-   ##
==========================================
+ Coverage   13.90%   13.94%   +0.04%     
==========================================
  Files         189      189              
  Lines       15813    15821       +8     
  Branches     4169     4179      +10     
==========================================
+ Hits         2199     2207       +8     
  Misses      13460    13460              
  Partials      154      154              
Impacted Files Coverage Δ
include/SFML/System/Time.inl 100.00% <100.00%> (ø)
src/SFML/System/Clock.cpp 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 72d8803...9b4efc7. Read the comment docs.

codecov[bot] avatar Jun 17 '22 00:06 codecov[bot]

@kimci86

The private constructor Time(Int64 microseconds) could be removed as it is used only in microseconds function which could use the new constructor instead.

Good point. I'll remove that.

Also, the class documentation could mention that conversion to/from duration is supported.

Right I forgot that. I'll add some extra information to that section describing the new functionality.

@Bromeon

I'm not sure if we should change the member variable though. What do we effectively gain? Code becomes quite a bit more verbose (mainly because duration_cast is such an ugly API).

It makes it easier to remove magic numbers from the implementation and gives us the added assurance that <chrono>'s type safety brings.

Does sizeof(Time) also increase?

No I don't believe so. std::chrono::microseconds will use a signed integer with at least 55 bits which means any sensible implementation is using a std::int64_t which is definitionally the same as sf::Int64.

ChrisThrasher avatar Jun 17 '22 14:06 ChrisThrasher

I squashed some commits too since I thought 4 or 5 commits was excessive for a change of this scale.

ChrisThrasher avatar Jun 17 '22 14:06 ChrisThrasher

What do we think about a user-defined conversion function? This would do the exact opposite of the new constructor template allowing for implicit (yet typesafe) conversions back to durations.

template <typename Rep, typename Period>
constexpr operator std::chrono::duration<Rep, Period>() const { return m_microseconds; }

EDIT: I have this implemented and tested locally. It seems to work exactly as we'd expect. I can push this up if the team wants to see it (and let CI verify it) before making a decision.

ChrisThrasher avatar Jun 17 '22 14:06 ChrisThrasher

Implicit conversions between sf::Time and std::chrono::duration are probably OK, since both are already "duration" types and there's less that can go wrong than when implicitly converting to seconds/milliseconds. But I would say we make either both conversion constructor and operator implicit, or none, for symmetry.

Bromeon avatar Jun 18 '22 08:06 Bromeon

I went ahead and added the implicit conversion to std::chrono::duration.

ChrisThrasher avatar Jun 18 '22 14:06 ChrisThrasher

No changes. Just keeping up with master.

ChrisThrasher avatar Jun 24 '22 17:06 ChrisThrasher

Rebased on master and fixed new compilation error introduced by a57640c2c89042d7ecc2c5f87712b553c8e14612

ChrisThrasher avatar Jun 28 '22 03:06 ChrisThrasher

This is still unaddressed:

Another thing that came to my mind: how good is debugger support now across compilers? Before, it was possible to see something like sf::Time { m_microseconds: 323811 } in debug watches and always know what's going on. Is the new internal chrono::duration field displayed as a deeply nested, barely decipherable type, or is there decent support for debug printing?

Because as mentioned in the early review, I don't see a practical advantage of storing std::duration internally -- yes, we remove magic numbers (which are quite obvious in that context), but introduce very hard-to-read chrono shenanigans. So let's at least make sure that usability of sf::Time doesn't deteriorate.

Bromeon avatar Jun 28 '22 15:06 Bromeon

but introduce very hard-to-read chrono shenanigans

To be fair, this is idiomatic <chrono> code. However verbose the API is, anyone familiar with C++11 time utilities will know what is going on.

So let's at least make sure that usability of sf::Time doesn't deteriorate.

image

In the VS Code debugger I can still easily see the value of the duration.

ChrisThrasher avatar Jun 28 '22 16:06 ChrisThrasher

I changed how we're printing standard types such that we never have to open up namespace std since I'm not 100% sure that's legal.

ChrisThrasher avatar Jun 28 '22 18:06 ChrisThrasher

Now it only needs a small comment to explain why we use StringMaker for stringification.

kimci86 avatar Jun 30 '22 13:06 kimci86

Now it only needs a small comment to explain why we use StringMaker for stringification.

Done

ChrisThrasher avatar Jun 30 '22 15:06 ChrisThrasher

Rebased and reformatted because of #2159

ChrisThrasher avatar Jul 11 '22 18:07 ChrisThrasher

With Bromeon's and Vittorio's approval, is there anything else we're waiting on before this is good to merge?

ChrisThrasher avatar Jul 23 '22 04:07 ChrisThrasher

Rebased on master, fixed a compiler warning caused by Doctest changes, and found somewhere to use this new implicit chrono::duration->sf::Time conversion to simplify code.

ChrisThrasher avatar Jul 30 '22 21:07 ChrisThrasher

Added a missing & on a parameter and rebased on master.

ChrisThrasher avatar Aug 04 '22 07:08 ChrisThrasher

@Bromeon would you mind checking this out?

ChrisThrasher avatar Sep 05 '22 17:09 ChrisThrasher

Rebased on master and added an assert to handle a potential edge case that could cause UB in the tests.

ChrisThrasher avatar Sep 05 '22 18:09 ChrisThrasher

Woho! Thanks for this addition 🥳

eXpl0it3r avatar Sep 05 '22 21:09 eXpl0it3r

This change may be related to a a master branch compilation issue I am now seeing on Windows:

C:\UserData\SFML\include\SFML/System/Time.inl(223,42): error C2039: 'int64_t': is not a
member of 'std' [C:\UserData\SFML\build\src\SFML\System\sfml-system.vcxproj]
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29333\include\chrono(24): message : s
ee declaration of 'std' [C:\UserData\SFML\build\src\SFML\System\sfml-system.vcxproj]

PierceLBrooks avatar Sep 21 '22 20:09 PierceLBrooks

Very possible there's simply a missing #include <cstdint> somewhere. Can you share how you can reproduce this error?

ChrisThrasher avatar Sep 21 '22 20:09 ChrisThrasher

@ChrisThrasher Of course! My steps were as follows:

git clone https://github.com/SFML/SFML.git
cd SFML
git checkout master
mkdir build
cd build
cmake ..
cmake --build .

However, this assumes that Visual Studio 2019 will be the default pick of your system, so you may need to specify the appropriate -G flag for CMake.

PierceLBrooks avatar Sep 21 '22 20:09 PierceLBrooks

That's it? Weird, because we already test VS 2019 in CI. Anyways, should still be an issue fix. Mind filing an issue about this so we don't lose track of this bug? Thanks!

ChrisThrasher avatar Sep 21 '22 20:09 ChrisThrasher

Will do, I can try to address it with a pull request later on as well if I can track down where the cstdint include belongs! FYI, checking out the 2.5.1 release version branch allows the build to succeed just fine.

PierceLBrooks avatar Sep 21 '22 20:09 PierceLBrooks

https://github.com/SFML/SFML/issues/2211#issue-1381582926

PierceLBrooks avatar Sep 21 '22 22:09 PierceLBrooks