Post AFGKm4OW7dCco3QK0G by codewiz@mstdn.io
 (DIR) More posts by codewiz@mstdn.io
 (DIR) Post #AFBxYmrIvuS54skJvs by codewiz@mstdn.io
       2022-01-07T08:53:28Z
       
       0 likes, 0 repeats
       
       We all know that C++ inherited unsafe semantics from C, but one would expect modern library features such as std::chrono to be carefully designed to avoid the old integer conversion bugs... right?Not the case: it's very easy to silently cause truncation or overflow in correct-looking code, such as:  set_timeout(300s);Try to get a compile-time diagnostic or at least a run-time error for this interface:https://godbolt.org/z/K7fYGcx8h#cpp #programming
       
 (DIR) Post #AFBxkLQUkg2HTMiXBo by tennoseremel@mstdn.io
       2022-01-07T08:55:29Z
       
       0 likes, 0 repeats
       
       @codewiz Modern solutions require modern bugs :blobcatderpy:
       
 (DIR) Post #AFC2hZirdqiYiJoALY by codewiz@mstdn.io
       2022-01-07T09:51:04Z
       
       0 likes, 0 repeats
       
       This is some background info on std::chrono:https://www.youtube.com/watch?v=P32hvk8b13MAt 27:00 there's a hint that one could handle overflows by just using a safeint type:  duration<safe<int32_t>>I don't think it's as simple as this slide hints: what are you going to do? Throw an exception at runtime? Saturate to the maximum representable value? Call abort()?Constants that will certainly overflow should result in a compile-time error, just like in this case:  uint8_t byte{666};
       
 (DIR) Post #AFC2kHw8E59YQ7J8Lo by codewiz@mstdn.io
       2022-01-07T09:51:34Z
       
       0 likes, 0 repeats
       
       This is some background info on std::chrono:https://www.youtube.com/watch?v=P32hvk8b13MAt 27:00 there's a hint that one could handle overflows by just using a safeint type:  duration<safe<int32_t>>I don't think it's as simple as this slide hints: what are you going to do? Throw an exception at runtime? Saturate to the maximum representable value? Call abort()?Constants that will certainly overflow should result in a compile-time error, just like in this case:  uint8_t byte{666};#programming #cpp sucks
       
 (DIR) Post #AFCZxq5RrexQaTjbxw by keadamander@mastodon.social
       2022-01-07T16:03:44Z
       
       0 likes, 0 repeats
       
       @codewiz With most compilers (c/c++)  it is possible to treat warnings as errors GCC :  with the compiler flags -Wall and -Werrorerror: unsigned conversion from ‘int’ to ‘uint8_t’ {aka ‘unsigned char’} changes value from ‘666’ to ‘154’ [-Werror=overflow]   17 |    uint8_t x = 666;      |                ^~~cc1: all warnings being treated as errors
       
 (DIR) Post #AFCefSBcUEiJ1Hfpia by codewiz@mstdn.io
       2022-01-07T16:56:28Z
       
       0 likes, 0 repeats
       
       @keadamander This works with ints, but there's no warning nor copile-time check if you do the same thing with a duration:  // All these silently overflow!  cpu_duration d1{300s};  cpu_duration d2 = 300s;  cpu_duration d3(seconds{300});Try running my testcase in the Godbolt sandbox.
       
 (DIR) Post #AFCi05VQJ7cucYT2hM by keadamander@mastodon.social
       2022-01-07T17:33:48Z
       
       0 likes, 0 repeats
       
       @codewiz I will use g++ for that. As I am still stuck in C++99:Where is cpu_duration class defined?Where can I find the definition of 300s?
       
 (DIR) Post #AFCijpAZNU2zH8WQvg by keadamander@mastodon.social
       2022-01-07T17:42:04Z
       
       0 likes, 0 repeats
       
       @codewiz I will use g++ for that. As I am still stuck in C++98:Where is cpu_duration class defined?Where can I find the definition of 300s?
       
 (DIR) Post #AFCjPsniGbOMhTt0Ns by codewiz@mstdn.io
       2022-01-07T17:49:42Z
       
       0 likes, 0 repeats
       
       @keadamander It's in the testcase I posted on Godbolt:https://godbolt.org/z/K7fYGcx8h
       
 (DIR) Post #AFDlnRpGbY8b3UwAj2 by jeeves@mstdn.io
       2022-01-08T05:51:03Z
       
       0 likes, 0 repeats
       
       @codewiz What would Rust do?  Not very familiar with Rust but it seems like it would give you a compile error if the function were a const fn , according to https://doc.rust-lang.org/reference/const_eval.html.  If it weren't a const fn, you'd get a panic in debug mode and silent overflow in release?  And if it were a const fn that returned Option/Error, then you'd be safe but have to unwrap at runtime I think.  Would be nice if you could configure a const fn to cause a compile error when returning None/Error.
       
 (DIR) Post #AFDnDYLj4ZWils8DdQ by codewiz@mstdn.io
       2022-01-08T06:06:59Z
       
       0 likes, 0 repeats
       
       @jeeves I had this same question on my mind, and I wanted to write a testcase as soon as I find some time.Feel free to beat me at it, and please share your code on Godbolt!
       
 (DIR) Post #AFEcSmc7PpSh18ooyW by jeeves@mstdn.io
       2022-01-08T15:41:12Z
       
       0 likes, 0 repeats
       
       @codewiz Here's a tiny example of Rust erroring out at compile time on overflow (didn't even have to designate the fn as const): https://rust.godbolt.org/z/P8Kx6aaMK.  As far as I can tell, Rust doesn't give you the option to emit a warning/error when a particular const fn returns None/Error.
       
 (DIR) Post #AFEeIDBMUhwc8nOGC8 by jeeves@mstdn.io
       2022-01-08T16:01:42Z
       
       0 likes, 0 repeats
       
       @codewiz My previous Toot was wrong so I deleted it; it turns out Rust is actually panicking at runtime, not compile time on integer overflow in a const fn: https://rust.godbolt.org/z/hvPY1YvGz
       
 (DIR) Post #AFEhWyPZze6gXKlrvs by keadamander@mastodon.social
       2022-01-08T16:37:58Z
       
       0 likes, 0 repeats
       
       @codewiz Read the whole thread and watched the video ... now i get your point. :-)
       
 (DIR) Post #AFEiC8E8P9J0qr2lOq by keadamander@mastodon.social
       2022-01-08T16:45:24Z
       
       0 likes, 0 repeats
       
       @codewiz Watched the video and read the whole thread this time. Now I think I got your point. It also compiles silently with g++ on my computer. As he states this as a compile time type conversion with no effect on runtime, I would expect it to throw a compiler error too.The question is : why doesn't it?BTW : Thx for godbolt link... also didn't knew that yet.
       
 (DIR) Post #AFEiTuNoc9q38033U8 by keadamander@mastodon.social
       2022-01-08T16:48:37Z
       
       0 likes, 0 repeats
       
       @codewiz Watched the video and read the whole thread this time. Now I think I got your point. It also compiles silently with g++ on my computer. As he states this as a compile time type conversion with no effect on runtime, I would expect it to throw a compiler error too.The question is : why doesn't it?BTW : Thx for godbolt link... also didn't know that yet.
       
 (DIR) Post #AFG8fcMIFNIgfQXYpc by namark@qoto.org
       2022-01-09T09:16:47Z
       
       0 likes, 0 repeats
       
       @codewiz exception, abort or compiler error? yeshttps://yewtu.be/watch?v=93Cjg42bGEwhttps://www.boost.org/doc/libs/develop/libs/safe_numerics/doc/html/index.html
       
 (DIR) Post #AFGKH4ESfv8lKHw9UO by codewiz@mstdn.io
       2022-01-09T11:26:48Z
       
       0 likes, 0 repeats
       
       @namark I just tried std::chrono::duration<boost::safe_numerics::safe<int32_t>> and immediately ran into trouble because duration_cast uses std::common_type, and oddly, Boost does not provide specializations for its safe types.After defining a bunch of variants, I got the code to compile with exceptions. Then I tried switching to traps (since our embedded codebase is built with -fno-exceptions), and I hit another error cascade that I'm too tired to debug 😡
       
 (DIR) Post #AFGKm4OW7dCco3QK0G by codewiz@mstdn.io
       2022-01-09T11:32:24Z
       
       0 likes, 0 repeats
       
       @namark Here's the code:https://godbolt.org/z/GssW4G3dsIt won't compile in Godbolt because it needs Boost, and it won't compile with g++ or clang either, because of some weird error:In template: type 'boost::safe_numerics::trap_exception' does not provide a call operator
       
 (DIR) Post #AFGLYAgM31K6CuhfoO by codewiz@mstdn.io
       2022-01-09T11:40:24Z
       
       0 likes, 0 repeats
       
       @namark Oh wait, that's how they implemented the compile-time trap:// emit compile time error if this is invoked.struct trap_exception {    constexpr trap_exception() = default;    // error will occur on operator call.    // hopefully this will display arguments};Yeah, but it barfs a pageful of impenetrable template errors leading to this! I can't impose this horror on the whole team.
       
 (DIR) Post #AFGONS5bHtUys75mNc by namark@qoto.org
       2022-01-09T12:12:47Z
       
       0 likes, 0 repeats
       
       @codewiz you seemed to have a problem with the concept so that was an explanation and a proof of concept implementation. Pleasantly surprised it worked at all! If you care about it you should participate, cause as far as I know that is the most developed of any such concepts: https://github.com/boostorg/safe_numericscommon_type trait sounds like a misnomer, but I imagine chrono didn't have anything better to use. safe_numeric may provide alternatives on that front, though it will likely never be standardized in it's current form. It's much more likely for a more generic numeric library to get in, that would also handle arbitrary precision and provide the safe_numerics functionality as its subset, and probably not called "safe" cause that's not a very good name either.
       
 (DIR) Post #AFGuJAR2n2ylH05m3E by codewiz@mstdn.io
       2022-01-09T18:10:34Z
       
       0 likes, 0 repeats
       
       @namark Sorry if it sounded like I was shooting the messenger. I find it upsetting that modern C++ comes with a sophisticated type-safe library for  time conversion, but then it actually overflows just like K&R C.I found an issue for adding common_type specializations for safe_numerics, but it was closed 2 years ago. I added a comment asking to re-evaluate:https://github.com/boostorg/safe_numerics/issues/30
       
 (DIR) Post #AFGzyEnHzLkWR3tsbQ by jeeves@mstdn.io
       2022-01-08T16:11:51Z
       
       0 likes, 0 repeats
       
       @codewiz I got it to error out at compile time: https://rust.godbolt.org/z/qhhTxb8hr.  Had to change from calling the const fn in the argument list of println!() to first assigning the result of the const fn to a const variable, then calling println!() with that variable, so it would evaluate the const fn in a const context (defined at https://doc.rust-lang.org/reference/const_eval.html#const-context).
       
 (DIR) Post #AFGzyFEaLraFnifg9I by codewiz@mstdn.io
       2022-01-09T19:14:00Z
       
       0 likes, 0 repeats
       
       @jeeves Ok, overflowing in constant expressions is an error also with C++, but only with signed integers:https://rust.godbolt.org/z/e947Pq8s6
       
 (DIR) Post #AFIFcmsIOCQ2Blzms4 by namark@qoto.org
       2022-01-10T09:44:08Z
       
       0 likes, 0 repeats
       
       @codewiz the main point is that it's a nuanced issue out of scope for chrono. Any halfway solution would achieve very little and lower the quality of the library. The arithmetic bound checking is universally applicable, so it would be kind of silly to have something for durations that you couldn't use for other types. Also imposing any sort of compile-time, run-time or maintenance overhead any such checks would bring is nonsensical in a world where everyone in every other application of arithmetic is perfectly happy to just use a type that is assumed to be "large enough" to represent any value. Most just want to use a 64 bit int, and forget it can overflow, even at nanosecond precision, and some would  vehemently argue that even on systems that don't have wide enough words, it should just be emulated in software.
       
 (DIR) Post #AFW8dob0wsnE6zkyh6 by codewiz@mstdn.io
       2022-01-17T02:31:48Z
       
       0 likes, 0 repeats
       
       @jeeves Followup on the unnecessarily hard problem of getting compile-time checking for overflows in conversions between std::chrono units:https://godbolt.org/z/7E9MMMMfzTL;DR: too complicated and unergonomic until C++ adds constexpr function parameters.#cpp #programming
       
 (DIR) Post #AFW8i7hHsBSQVlkG2a by codewiz@mstdn.io
       2022-01-17T02:32:37Z
       
       0 likes, 0 repeats
       
       @jeeves Followup on the unnecessarily hard problem of getting compile-time errors for overflows in conversions between std::chrono::duration constants:https://godbolt.org/z/7E9MMMMfzTL;DR: too complicated and unergonomic until C++ adds constexpr function parameters.#cpp #programming
       
 (DIR) Post #AFWFK2n3AIDQ1OuE0e by jeeves@mstdn.io
       2022-01-17T03:46:42Z
       
       0 likes, 0 repeats
       
       @codewiz could you have the conversion function return the equivalent of Rust's Result type and creating a STATIC_UNWRAP macro that hides the template grossness?
       
 (DIR) Post #AFWFMeG0pm2unqh80O by jeeves@mstdn.io
       2022-01-17T03:47:11Z
       
       0 likes, 0 repeats
       
       @codewiz could you have the conversion function return the equivalent of Rust's Result type and create a STATIC_UNWRAP macro that hides the template grossness?
       
 (DIR) Post #AFWc1scQXbX2D1FEmm by codewiz@mstdn.io
       2022-01-17T08:01:08Z
       
       0 likes, 0 repeats
       
       @jeeves Not feasible in C++ for cases like initializing global constants and passing durations to constructors (without using exceptions!)
       
 (DIR) Post #AFk5ngapyil0XP80Q4 by codewiz@mstdn.io
       2022-01-23T20:05:58Z
       
       0 likes, 0 repeats
       
       I wanted to see if I could get better compile-time overflow checks with Rust, but the limitations are similar: arguments of const functions are not themselves compile-time constants that you could check with assert!().So this is what I could come up with:https://godbolt.org/z/5zf1earKT@jeeves #Rust #cpp #programming
       
 (DIR) Post #AFk5p7fWnUOKnj0VaS by codewiz@mstdn.io
       2022-01-23T20:06:14Z
       
       0 likes, 1 repeats
       
       I wanted to see if I could get better compile-time overflow checks with Rust, but the limitations are similar: arguments of const functions are not themselves compile-time constants that you could check with assert!().So this is what I could come up with:https://godbolt.org/z/5zf1earKT@jeeves #Rust #cpp #programming
       
 (DIR) Post #AFk6IKfpWZhJX64ozI by codewiz@mstdn.io
       2022-01-23T20:11:28Z
       
       0 likes, 0 repeats
       
       Even in Rust nightly, support for constant evaluation is still very limited compared to C++20's:https://doc.rust-lang.org/reference/items/generics.htmlAt this time, you can't even pass float parameters or enums, let alone structs.There's ongoing work to expand the capabilities of constant evaluation in Rust, but it will take years before they can close the gap with C++, which is not standing still:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/#mailing2022-01@jeeves #cpp #rust #programming
       
 (DIR) Post #AG2tnBhsWbADxpe6nw by namark@qoto.org
       2022-02-01T21:50:56Z
       
       0 likes, 0 repeats
       
       @codewiz another thing you could do is define and use your own bound checked literals of different sizes. Here is an example for just plain integershttps://git.sr.ht/~namark/libsimple_support/tree/master/item/source/simple/support/int_literals.hppIf you stick with just one duration type throughout the codebase that would be all you need, otherwise if you want to mix and match duration of different granularity and size, your custom literals can return a wrapper type that would only be implicitly convertible to durations that they can fit in.
       
 (DIR) Post #AG2wOxM2XBLUMcZIHI by namark@qoto.org
       2022-02-01T22:20:11Z
       
       0 likes, 0 repeats
       
       @codewiz keeping in mind that it's still a half measure, which won't save your from many potential pitfalls. For anything non trivial you'd still need to do a lot of mental (or pen and paper) calculations to make sure there is no overflow, so literals would be the least of your concerns on that front.
       
 (DIR) Post #AG3uwuDyfku1kfolJg by codewiz@mstdn.io
       2022-02-02T09:38:37Z
       
       0 likes, 0 repeats
       
       @namark Returning a wrapper type that's only implicitly convertible when it fits is possible only if each literal creates a different type with the value encoded in a non-type argument (like std::ratio<N,M>).Perhaps this is doable with user-defined literals by declaring a templated operator"" which returns auto.
       
 (DIR) Post #AG3vMlQ81o63yYpbJA by namark@qoto.org
       2022-02-02T09:43:18Z
       
       0 likes, 0 repeats
       
       @codewiz yes, unless there are any explicit restriction in the standard that I'm not aware of, the operator can return any type based on the value of the literal. The literal value comes in as a variadic pack of chars.