Announcement

Collapse
No announcement yet.

FreeType 2.10.4 Rushed Out As Emergency Security Release

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • #11
    Originally posted by HadrienG View Post
    As a less tongue-in-cheek answer, rewriting code in modern C++ is effectively the same amount of work as rewriting it in a whole new language, since there simply aren't that much foundational libraries / existing projects written in modern C++, and claiming that your code is super-safe C++20 while most of your codebase is actually written in C++98 style is just luring yourself into a false sense of safety.
    True, but then by same argument, rewriting distinct projects in Rust also makes little sense, because similarly any other library you depend on will not have been written in Rust. Even if you are lucky enough to need "no dependencies", there is still a multi-ten-million lines of code C-project running under your feet, called the OS. So this argument can go both ways (you even hinted at this).

    Originally posted by HadrienG View Post
    The "without having to learn a completely new language" argument is similarly dubious. So many changes have been hacked into the C++11/14/17/20 standards that learning and mastering them is effectively the same amount of work as learning two well-designed programing languages from scratch.
    Agreed, it is going to be a large undertaking. I still believe and suggest though that it is going to be an easier and shorter road going from C++98 to C++17 then going from C++98 (or from C) to Rust,

    Comment


    • #12
      Originally posted by Alexmitter View Post

      Jokes aside, Rust is currently not compilable via GCC, so its a instant fail.
      You can make a dynamic library in Rust and then link it with GCC. Firefox also now has parts of it written in Rust, although I am not sure how they have achieved it (however I assume that if a project as large as Firefox has managed it then so can Freetype).

      Originally posted by ultimA View Post
      Agreed, it is going to be a large undertaking. I still believe and suggest though that it is going to be an easier and shorter road going from C++98 to C++17 then going from C++98 (or from C) to Rust,
      If you are going to rewrite something completely its better off to do it properly (which in this case means using Rust).

      If you are going to do it incrementally then modern C++ may make more sense but I am not sure by how much.

      Originally posted by zxy_thf View Post
      For some reason Idk I feel lots of Linux system library developers only want to use C, even if its C implementation would be very verbose in today's view.
      Stockholm Syndrome TBH
      Last edited by mdedetrich; 20 October 2020, 02:42 PM.

      Comment


      • #13
        Originally posted by ultimA View Post
        > As a less tongue-in-cheek answer, rewriting code in modern C++ is effectively the same amount of work as rewriting it in a whole new language, since there simply aren't that much foundational libraries / existing projects written in modern C++, and claiming that your code is super-safe C++20 while most of your codebase is actually written in C++98 style is just luring yourself into a false sense of safety.

        True, but then by same argument, rewriting distinct projects in Rust also makes little sense, because similarly any other library you depend on will not have been written in Rust. Even if you are lucky enough to need "no dependencies", there is still a multi-ten-million lines of code C-project running under your feet, called the OS. So this argument can go both ways (you even hinted at this).
        I agree. As far as I see it, that's the main argument in favor of rewriting large bunches of existing infrastructure, as opposed to just writing new library / applications, with safer programming languages. Though I personally still think that's not usually worth the cost.

        In my opinion, modern C++ is just one of those safer options, among others. It's the right choice if you need C++'s feature set or maximal C++ ecosystem compatibility (e.g. GPGPU programming, GUI or metaprogramming are three things that C++ does much better than all other system languages at the moment), but in exchange you lose some degree of memory/thread/type safety and overall language polish/ergonomics since there's only so much you can do by gradually evolving a language over the course of >30 years in a backwards compatible fashion. As always in engineering, tradeoffs !
        Last edited by HadrienG; 20 October 2020, 03:38 PM.

        Comment


        • #14
          Originally posted by HadrienG View Post
          Both you and the OP are misunderstanding what Rust's lifetime annotations are doing. They are not about automatic memory allocation/liberation (which are handled via RAII in Rust, just like in C++), they are about proving the absence of dangling pointers and UAF at compile time when handing out references to preallocated data, in such a manner that changing the implementation of a function without changing its signature will never break its API.
          References in C++ provide similar guarantees. Yeah, I know that creating null-references is technically possible, but in reality is just a horror-story for teaching purposes.

          Originally posted by HadrienG View Post
          Const in C++ is unfortunately a bit of a lie. As long as you can const_cast it away (and I see people regularly do so, either due to a misunderstanding of the "const means thread-safe" motto or due to the general complexity of correctly initializing an object that contains const pointers), it does not help compiler optimization and can mislead programmer reasoning. It is therefore a much less powerful property than Rust's immutability, where the possibility of "internal mutation" still exists but is not on by default and must instead be explicitly opted into at type declaration time.
          I wouldn't call it a lie just because you have some rare and badly educated programmers who willingly cast away const and by the same action introduce bugs later in the program flow. const is const, the compiler enforces it rigorously and correctly, and anybody who uses const_cast to make it vanish should one of 1) have a very good reason to do so, 2) be educated, or 3) be fired (repeat non-learning offenders). The "very good reason" category basically only happens when the underlying interface is very badly designed.

          Originally posted by HadrienG View Post
          • Rust's reference type, which is much more commonly used in idiomatic code than raw pointers for obvious reasons, does not have a null state. Such a null state must be opted into via Option<T>. Therefore, for convenience reasons Rust APIs tend to use "typestate" patterns where APIs accept or provide nullable references, the presence of nulls is checked once in the code, and then the underlying reference is unpacked and the rest of the code can operate under the assumption that the reference is non-null.
          The same is very much possible in C++, but more than technically possible, is not even hard to do. Since memory allocation (new) does not return null-pointers, and failed constructors also do not result in null values but exceptions, the only way to get nulls in C++ is either 1) from external code (Rust suffers from this too), 2) default-initialized pointer types (can be easily avoided but yes I admit it needs extra typing), 3) explicitly returning nullptr (needs active willingness and "malign intent"). If you stay away from 2) and 3), then you have the same situation as Rust, where you only need to check for nulls on your interfaces and can thereafter assume all pointers are valid. Not running destructors on moved-from objects in Rust is a nice thing, and yes, this can avoid a manual boolean state in contrast to C++. Does this extra boolean state in C++ make it inherently null-unsafe? I don't think so, besides, you need it soooo rarely.


          Last edited by ultimA; 20 October 2020, 03:09 PM.

          Comment


          • #15
            As for warning about use-after-move in C++ ...

            I would love it. But I have seen C++ programmers actually reusing their objects after a move. "I know it's a newly constructed object, because I implemented the move assignment. Why should I create a new object when this one is right there?"

            I don't know if that attitude is changeable.

            Comment


            • #16
              Originally posted by ultimA View Post
              > Both you and the OP are misunderstanding what Rust's lifetime annotations are doing. They are not about automatic memory allocation/liberation (which are handled via RAII in Rust, just like in C++), they are about proving the absence of dangling pointers and UAF at compile time when handing out references to preallocated data, in such a manner that changing the implementation of a function without changing its signature will never break its API.

              References in C++ provide similar guarantees. Yeah, I know that creating null-references is technically possible, but in reality is just a horror-story for teaching purposes.
              This is not about nullness, it is about non-null pointer/references pointing to a live or dead allocation.

              From memory, the only form of reference validity checking that I've seen C++ compilers perform is to emit a warning when a function returns a reference to an inner local variable. Which is already much appreciated.

              In contrast, Rust is able to perform more advanced validity checking, for example it knows that object.method_that_returns_a_value().method_that_r eturns_a_reference_to_this() is incorrect. I spent a couple hours debugging one of those dangling reference errors caused by Eigen's expression templates the other day, so I know they do happen in real-world C++.

              > Const in C++ is unfortunately a bit of a lie. As long as you can const_cast it away (and I see people regularly do so, either due to a misunderstanding of the "const means thread-safe" motto or due to the general complexity of correctly initializing an object that contains const pointers), it does not help compiler optimization and can mislead programmer reasoning. It is therefore a much less powerful property than Rust's immutability, where the possibility of "internal mutation" still exists but is not on by default and must instead be explicitly opted into at type declaration time.

              I wouldn't call it a lie just because you have some rare and badly educated programmers who willingly cast away const and by the same action introduce bugs later in the program flow. const is const, the compiler enforces it rigorously and correctly, and anybody who uses const_cast to make it vanish should one of 1) have a very good reason to do so, 2) be educated, or 3) be fired (repeat non-learning offenders). The "very good reason" category basically only happens when the underlying interface is very badly designed.
              Besides utterly ridiculous use, I've seen const casting used because...
              • They wanted to add some form of caching to an API that wasn't designed for it, because performance problems only appeared late as the system grew. I'm not sure if I would call that bad interface design or not, it's one of those cases where you made the right design decision before, want to make the new right decision now, and backwards compatibility is getting in the way...
              • They wanted to hide some information that was felt to be irrelevant to the API end user (much in the same way that global & static variables are typically used in logging or lazy initalization of components that can only be set up at runtime)
              • They wanted to construct some sort of const graph of objects, where the objects mutate each other during the initialization procedure but are not mutated after initialization is over, and wanted the compiler to enforce property #2.

              ...which I thought were interesting edge cases with a surprisingly high frequency of occurence.

              > Rust's reference type, which is much more commonly used in idiomatic code than raw pointers for obvious reasons, does not have a null state. Such a null state must be opted into via Option<T>. Therefore, for convenience reasons Rust APIs tend to use "typestate" patterns where APIs accept or provide nullable references, the presence of nulls is checked once in the code, and then the underlying reference is unpacked and the rest of the code can operate under the assumption that the reference is non-null.

              The same is very much possible in C++, but more than technically possible, is not even hard to do. Since memory allocation (new) does not return null-pointers, and failed constructors also do not result in null values but exceptions, the only way to get nulls in C++ is either 1) from external code (Rust suffers from this too), 2) default-initialized pointer types (can be easily avoided but yes I admit it needs extra typing), 3) explicitly returning nullptr (needs active willingness and "malign intent"). If you stay away from 2) and 3), then you have the same situation as Rust, where you only need to check for nulls on your interfaces and can thereafter assume all pointers are valid. Not running destructors on moved-from objects in Rust is a nice thing, and yes, this can avoid a manual boolean state in contrast to C++. Does this missing boolean state in Rust make it inherently null-safer? I don't think so.
              In typical C++ codebases, I see 3/ happen worryingly often because people use the fact that functions return nullable pointers as a very poor form of error handling. This is a problem because the resulting pointer may be tucked somewhere in a class member and only dereferenced much later in the application lifecycle. Removing the null (as even C++ references do, but unfortunately they are too limited to fully replace "valid pointers" owing to not being reassignable) or making it mandatory to check as Rust does prevent this form of developer miscommunication entirely.
              Last edited by HadrienG; 20 October 2020, 03:32 PM.

              Comment


              • #17
                Just to clarify my standpoint. I am not saying in any way that C++ is superior to Rust as far as language design goes. I am only saying that with the tools given by the language, a programmer has a similarly easy job implementing type- and memory-safe code as in Rust. At least very much comparable. If you take into additional consideration that both collective knowledge and C++-tooling is in a much better state today then for Rust, well, you decide. When somebody starts telling others that "Maybe [project X] ... should be rewritten in Rust to avoid these buffer overflow problems", completely ignoring that you could achieve the same results without Rust but in a much more compatible and codingtime-efficient way, I like to point this stuff out. This is not about proving if Rust or C++ or Go or [pick your favorite language] is better, this is about stopping the mindless hype and choosing by thinking.

                Comment


                • #18
                  Oh, I forgot another problem with C++'s const design which caused me some pain a while ago. The fact that a const object with an inner pointer-to-mutable can still change the value targeted by that pointer from a const method.

                  I just recalled that this is actually the reason why people use const-casting for the "graph of eventually const objects" use case in my codebase. The problem that they were trying to solve is that with that form of "shallow" constness, if your objects can mutate each other without const-casting during initialization (via an inner pointer-to-mutable to other members of the graph), they can also mutate each other via that pointer-to-mutable in const methods after initialization.

                  So they had to choose between not using const_cast, but needing to be careful when writing const methods because the compiler is not checking const-correctness as intended, or using const_cast during initialization for the benefit of later on having proper compiler checking that const methods are not mutating accessible state from other objects.

                  Rust resolves this problem by applying "deep" constness rules for references. If you have an &-reference to an object with an inner &mut member, you cannot use that &mut member to mutate anything. It effectively acts as an &-reference when accessed via an &-reference.

                  Then there's also the question of mutable of course, which is yet another way to evade constness or accidentally write const methods that aren't. Proponents argue that accessors of mutex-synchronized objects should be const, and that this is only possible if the mutex is called mutable (or accessed by const_casting the this pointer, which is equivalent).

                  The Rust view here is that ultimately the occasional need for "internal mutability" (mutating via an &-reference) is unavoidable, so it's better to provide a relatively clean type system toolkit for doing that, rather than multiple broad "get out of const" language hacks that end up too often having wider effects than intended as the code evolves.

                  Comment


                  • #19
                    Originally posted by ultimA View Post

                    Now you're just talking without knowing C++ (or you do know it but just being bigotic about Rust).

                    > instead it [Rust] has an Option<T>and Result<T>
                    C++ too, see std:: optional and/or std::variant

                    > Also it has lifetime declarations so it automatically frees memory
                    C++ too, see smart pointers

                    > It also has ownership of values
                    C++ too, see non-copyable classes (and std::unique_ptr is non-copyable btw, so it ties nicely into ownership using smart-pointers and automatic lifetime management)

                    > So something else cannot modify a variable
                    C++ too, ever heard of const?

                    >Rust also doesn't suffer from null problems because it has no null value
                    It has no null value? Then I wonder what Ptr::null() or cpp_utils::NullPtr are for in Rust... Having to or not having to deal with nulls is not a language feature but a design-choice of the code author. In both Rust and C++ you can produce nulls and then you need to deal with them, or you can decide not to produce nulls in your code and then you don't suffer from them. But given that most existing and wide-spread libraries use null-pointers to a heavy extent (including even but not only OS interfaces such as POSIX or WinAPI), it would be a fatal mistake even in Rust to completely ignore nulls. All you can do is to decide not to produce nulls in your own code, which is easily possible in C++ too.
                    I didn't know about these features in C++.
                    No, this is different than const, I meant when you declare a variable and you pass it into a function then that function now owns the variable and can modify it, but you cannot modify it outside of that function since it no longer owns the variable.

                    Also I heard it is very easy to shoot yourself in the foot with C++, that it is very easy to do bad things. Linus even ranted about C++ calling it a horrible language, and refuses to let in C++ into the Linux kernel.

                    Also all of these nice features you mentioned were tucked on to C++ later like a patch, while Rust was built and designed explicitly with those features from the start.

                    Comment


                    • #20
                      Originally posted by ultimA View Post
                      Just to clarify my standpoint. I am not saying in any way that C++ is superior to Rust as far as language design goes. I am only saying that with the tools given by the language, a programmer has a similarly easy job implementing type- and memory-safe code as in Rust. At least very much comparable.
                      I would beg to differ with even that assertion. In my experience, C++ tends to be harder to use correctly than Rust because it has a lot more edge cases, a lot more users who "hold it wrong" by doing things you didn't expect, and because when you try to write C++ The Right Way you end up wasting so much brain power on silly backwards-compatibility things (typing std:: move all over the place, typing "const" everywhere for maximal const-correctness, figuring out SFINAE hacks to make your templates error out cleanly when a bad input type is passed in, parsing the huge compiler errors of third-party libraries that didn't do that...) that you have a lot less of it to 1/do the interesting part of your job (yay, useful business logic !) and 2/handle the number of cases where C++ has less safety checks than Rust : unchecked array indexing by default, dangling references, use-after-move, etc.

                      I certainly agree however that C++ has many advantages in exchange, not the least of which are more mature libraries, and more mature language features and tooling in various areas (I gave a list of my personal favorites above, though admittedly Rust is also ahead in other areas, e.g. cargo is much better than conan, rustdoc is much better than doxygen, rustup is much more convenient than cross-compiling your own GCC, and while C++ is finally, at LONG last, getting opt-in concepts that only a small minority will use until compilers are felt to be mature enough a decade from today, Rust has had mandatory traits from day one, which outperform concepts by working both in static and dynamic polymorphism scenarios).

                      In that sense, we agree that language choice is not so trivial and should be led by careful thought, rather than either mindless hype for the shiny new thing or its conservative counterpart, excessive disbelief in the genuine benefits of new approaches.
                      Last edited by HadrienG; 20 October 2020, 04:31 PM.

                      Comment

                      Working...
                      X