SFML community forums

General => SFML development => Topic started by: reconn on December 23, 2021, 05:44:30 pm

Title: Introduce chrono to sf::Time
Post by: reconn on December 23, 2021, 05:44:30 pm
sf::Time and chrono library

To keep SFML simple, explicit but up-to-date with the features of the STL, the introduction/integration of <chrono> would be of help to some.

sf::Time is a simple self-contained class, therefore, does not (yet) allow the chrono literal conversions, operator overloads, etc.

I propose to carefully define/discuss an extended API for sf::Time (and sf::sleep function) that extends sf::Time by the chrono library. Moreover, since <chrono> would be included already, sf::Time could possibly benefit from it internally (unsure of that though).

New allowed conversions and overloads:
// Currently
sf::Time t1 = sf::milliseconds(1001);
// Extended capabilities after e.g., using namespace std::chrono_literals;
sf::Time t2 = 1001ms;
sf::Time t3(1001ms);
sf::Time t4 = 1s + 1ms;
t1 += 1ms;

// Resulting type: sf::Time or std::chrono::duration?
// How about sf::Time with implicit conversion to chrono?
auto t5 = sf::seconds(1) + 1s;

Integrating Chrono makes sense only partially as it itself is very flexible but with some serious drawbacks. Consider these two examples readability- and convenience-wise:

/* Example 1 */
using namespace sf;
Time t = milliseconds(1001);
std::cout << "Time: " << t.asSeconds() << " second(s)\n";
// Output: Time: 1.001 second(s)

/* Example 2 */
using namespace std::chrono;
duration t = milliseconds(1001);
std::cout << "Time: " << duration<float>(t).count() << " second(s)\n";
// Output: Time: 1.001 second(s)

Chrono is more explicit so for a library developer it is quite useful to know what is/isn't going on but the user is bloated with unnecessary information. However, carefully providing chrono extensions could synergize well with other modern libraries.

Rising questions and concerns
- Would Time really benefit from chrono internally? (Probably would have to implement first to find out)
- Does SFML want to keep implicit narrowing conversions and overflows in sf::Time? (Those are edge cases but still exist)
- Would SFML like to introduce asNanoseconds?
- How will compilation time be affected? (Hopefully not much)
Title: Re: Introduce chrono to sf::Time
Post by: Ruckamongus on December 25, 2021, 12:41:42 pm
https://github.com/SFML/SFML/commit/dfff93fe04c3633f38315608ecd8b51b0aafea86
Title: Re: Introduce chrono to sf::Time
Post by: reconn on December 25, 2021, 07:37:57 pm
Yes, that is my PR. Can you elaborate more?
Title: Re: Introduce chrono to sf::Time
Post by: Paul on August 21, 2022, 11:53:55 am
I would say that sf::Time unnecessarily duplicates the code of std::chrono and greatly limits the flexibility of working with time. Using time units, conversions and other operations through std::chrono is much more natural and clear. Why should there be a default unit of something called "Int64 m_microseconds;" or "second = float". The user himself should determine the precision he needs for a given operation or what a given time period represents.

Instead of "time.AsSeconds", sf::Clock can be extended to "clock.getElapsedAsSeconds()", but basically it doesn't have much use. Perhaps make more sf::Clock alternatives like stopwatch, pauseable clock and so on would be better idea how improve work with time.

For the stated drawback (even if it's not important at all):

// Instead of
std::cout << "Time: " << duration<float>(t).count() << " second(s)\n";

// You can write
std::cout << "Time: " << duration<float>(t) << "\n";

// Output: Time: 1.001s
 
Title: Re: Introduce chrono to sf::Time
Post by: Nexus on August 25, 2022, 08:33:18 pm
In my opinion, the Chrono API is absolutely horrible from a usability point of view. The genericity is rarely needed, because 99% of durations in software fall into human-observable scale. For those, a default implementation would not only suffice, but make life a lot easier.

You also see that every other mainstream language has converged towards one duration type; e.g. Java took several attempts but their latest Duration/Instant pair is quite a nice and intuitive API. Even lower level languages like Rust decided to go with one type (https://doc.rust-lang.org/std/time/struct.Duration.html). That doesn't mean they're rightTM of course, but it's something to keep in mind.

I don't know what the rationale for genericity is; to me, it falls into classic Boost "what if the user wants quantum scale" kind of over-engineering -- actively making the API worse for the majority of users, in order to satisfy some corner cases. On top of that, SFML in particular operates at a very narrow and specific time scale ranging from milliseconds to seconds, in very rare cases hours or days.

This is why I think a simpler abstraction on top of Chrono has its merits -- this is only my personal opinion however.
More comments on the topic: