The abhorrent 🔥 rites of C



  • @asdf said:

    Have you ever seen C++11 code? I'm seriously starting to doubt that. All you need is provided by the STL.

    The STL provides a standard object for "undo this arbitrary state change I just made, by calling the arbitrary method that undoes it correctly"? Your claims are becoming more and more ridiculous the further you pursue them.



  • @Mason_Wheeler said:

    Sure, but what does that have to do with being able to handle destroy-on-unlock by itself or not?

    The mutex doesn't unlock itself in its destructor, because a mutex that's locked when it's about to be destroyed is an error in your program. (I.e., the mutex, which protects access to a shared resource, is being destroyed while the shared resource it is supposed to protect is being accessed).



  • Observation in passing about one key difference between try/catch with C++ RAII style and try/catch/finally:

    If you need to run something to unknit some state that must be left knitted until after the catch blocks complete, you need either a classic finally block OR:

    [code]
    // stuff stuff stuff

    {
    RaiiObject doodad(doodad_init_values);

    try
    {
      DifferentRaiiObject otherDoodad;
      // try things here
    } // otherDoodad is destructed here, BEFORE the relevant catch block runs.
    catch( This )
    {
      // handle This
    }
    catch( That )
    {
      // handle That
    }
    catch( TheOther )
    {
      // handle TheOther
    }
    

    } // Destructs doodad here.
    [/code]

    Which does look a little clumsy.



  • @cvi said:

    The mutex doesn't unlock itself in its destructor, because a mutex that's locked when it's about to be destroyed is an error in your program. (I.e., the mutex, which protects access to a shared resource, is being destroyed while the shared resource it is supposed to protect is being accessed).

    That makes even less sense. Why in the world would you destroy your mutex while it's still in use?


  • Winner of the 2016 Presidential Election

    @Mason_Wheeler said:

    The STL provides a standard object for "undo this arbitrary state change I just made, by calling the arbitrary method that undoes it correctly"?

    Yes, exactly that was proposed (and will most likely be standardized) for C++17:

    (And you can easily write one yourself if you don't want to wait.)

    @Mason_Wheeler said:

    Your claims are becoming more and more ridiculous the further you pursue them.

    No, it's becoming more and more obvious that you don't know much about C++.



  • @Mason_Wheeler said:

    Sure, but what does that have to do with being able to handle destroy-on-unlock by itself or not?
    It makes no sense for the mutex to unlock itself when it's destroyed, because once it's destroyed, it no longer exists. What do you care if a non-existent mutex is locked or not? And you can't lock a non-existent mutex either.

    What you're confused about is tying the existence of the mutex with the locked state of the shared object. Whether an object is locked or not is one piece of state that must always exist. If the mutex was constantly being created and destroyed, you'd have race conditions when two threads try to create a mutex for the same shared thing at the same time.

    So instead, the first thread to call lock on the already existing mutex locks it. The mutex has no way of knowing when the first thread is done with it, so it can't call unlock on itself. The calling thread delegates the responsibility of calling lock and unlock on the unique_lock, so that it will always leave the mutex in the correct state no matter what happens.

    @PJH said:

    With all these perfect solutions to avoid the problems, would someone like to tell me why malloc(), new and new[] are still things in C+11, since they got rid of other things (like the old meaning of auto)? 🍹
    Because the solutions are library classes that are implemented in the language itself. new, malloc, and whatever other allocators you use are primitive operations, and the library composes them in different ways to solve different problems. You might as well ask why bricks still exist when we have brick walls.

    @LaoC said:

    Now as far as I read, the "unique" part means there can only ever be one copy of that pointer so it's always clear who owns it and when it needs to get destroyed. If I'm getting that correctly, that doesn't work with hardware registers.
    You're not getting that correctly. The unique means that there can only be one owner (as opposed to shared, where there are many owners). The owner is the one responsible for freeing the memory. You can get raw pointers out of a unique_ptr, and they behave just like you would expect. You're just not supposed to free them. You let the unique_ptr object do that.

    And you can "move" the ownership around, in which case the original unique_ptr loses ownership and another pointer takes over the responsibility.



  • It may be intentional, but it isn't necessarily a good idea. The problem isn't that it exists, but that it is syntactically quiet.

    Not bothering with it still allows you to get a raw pointer, because you can still call operator ->() directly (it must, eventually, return a raw pointer to the target type), but the call sticks out like a sore thumb, and acts as a clue to code reviewers that you're doing something a little out of the ordinary.

    shared_ptr_variable->member() is exactly the same as:
    shared_ptr_variable.operator ->()->member()



  • @Mason_Wheeler said:

    That makes even less sense. Why in the world would you destroy your mutex while it's still in use?

    Do you even know what a mutex is, and how it's used? You're the one suggesting the destructor should call unlock on it!



  • @Mason_Wheeler said:

    That makes even less sense. Why in the world would you destroy your mutex while it's still in use?

    It doesn't make sense, and that's what people have been trying to tell you in regard to:

    @Mason_Wheeler said:

    Missing the point. Why does std::mutex not unlock itself (as necessary) in its own destructor?



  • @Mason_Wheeler said:

    @cvi said:
    The mutex doesn't unlock itself in its destructor, because a mutex that's locked when it's about to be destroyed is an error in your program. (I.e., the mutex, which protects access to a shared resource, is being destroyed while the shared resource it is supposed to protect is being accessed).

    That makes even less sense. Why in the world would you destroy your mutex while it's still in use?

    Read more carefully. That's exactly @cvi 's point. You wouldn't, unless there's a bug. A mutex object that is destroyed while locked should assert in debug builds, and explode in flames on release builds.


  • Winner of the 2016 Presidential Election

    @Steve_The_Cynic said:

    The problem isn't that it exists, but that it is syntactically quiet.

    There is a lot of existing code that expects raw pointers and sometimes you simply cannot use a reference. If the standards committee had proposed a really ugly syntax for getting the raw pointer out of a shared_ptr, people simply might not have started using it.



  • @asdf said:

    Yes, exactly that was proposed (and will most likely be standardized) for C++17:


    I look at that and I see 18 pages of highly technical gobbledygook that boils down to "here's a generic way to use templates to create a class that, when destroyed at the end of a scope, executes arbitrary code provided by the developer, in the try/finally block automagically generated by RAII." How can you not see that this is, by definition, much more complicated than just executing your arbitrary code in a try/finally block without creating a class to do it for you and a bunch of compiler magic and template magic to do that for you?

    @Kian said:

    It makes no sense for the mutex to unlock itself when it's destroyed, because once it's destroyed, it no longer exists. What do you care if a non-existent mutex is locked or not? And you can't lock a non-existent mutex either.

    Well, apparently someone cares enough to write that template that unlocks it upon destruction.

    @Kian said:

    Do you even know what a mutex is, and how it's used? You're the one suggesting the destructor should call unlock on it!

    As opposed to having some other object wrap around it to unlock and destroy it?

    @Steve_The_Cynic said:

    Read more carefully. That's exactly @cvi 's point. You wouldn't, unless there's a bug. A mutex object that is destroyed while locked should assert in debug builds, and explode in flames on release builds.

    Then what is the point of that template?


  • Winner of the 2016 Presidential Election

    @Mason_Wheeler said:

    How can you not see that this is, by definition, much more complicated than just executing your arbitrary code in a try/finally block without creating a class to do it for you and a bunch of compiler magic and template magic to do that for you?

    Are you now arguing that complexity is bad even if it's hidden from the developer and handled in the compiler and/or standard library? Because I'm sure the code generation for try/finally isn't that simple either.



  • @Steve_The_Cynic said:

    It may be intentional, but it isn't necessarily a good idea. The problem isn't that it exists, but that it is syntactically quiet.

    I don't think it's a problem. Passing references to smart pointers is a bad idea. Smart pointers shouldn't be in the signature of a function unless that function needs ownership. Otherwise, pass a reference to the object itself, or a pointer if the pointer may be null. Aside from looking ugly, when you pass the reference to the smart pointer, accessing the object itself then requires two steps of indirection: the reference to the smart pointer, and then the jump to wherever the object lives. If you just pass the pointer or reference to the object, there's only one step of indirection.


  • Considered Harmful

    @asdf said:

    @LaoC said:
    when I last wrote C++, auto_ptr was all there was and it was crap

    😷

    So your point is that C++98 was not much better than C. I agree. Take a look at modern C++ sometime.

    No, not at all. That was just a caveat that what I know about unique_ptr is from skimming some documentation and not actual experience so I might have gotten it wrong.

    @LaoC said:
    Say you have a network buffer pointed to by one of these. You enter the pointer into some DMA controller register after shoving it through an MMU translation to find the physical address. [...]

    Well, if you do stuff like that, you have to use raw pointers, of course. Still doesn't mean C is any better than C++ for implementing something like that.

    I wasn't trying to argue that. But when you have to forego all the modern safety features of C++ to do what you need to do, there's little point in using it. Even more because you have to forego them both at the lowest level and at the highest, the border to userland. You really don't want the tight coupling and the headaches with the interface to other programming languages that come with a C++ ABI either.

    @Steve_The_Cynic said:
    Both of them (the last time I looked) have a nice quiet method for accessing the raw pointer, T *std::shared_ptr<T>::get().
    That's intentional and not a problem. If you use C++11 correctly, you know that a raw pointer means "I can use this object temporarily, but I do not own it".
    That's not much different from "I know that I have to `free` what I `malloc`ed".


  • Are nasal demons on strike?



  • @asdf said:

    Are you now arguing that complexity is bad even if it's hidden from the developer and handled in the compiler and/or standard library? Because I'm sure the code generation for try/finally isn't that simple either.

    I'm arguing that creating an entire class to do something in its destructor inside a magic try/finally block is needlessly complex versus using a try/finally block without having to create any classes for what can be done with one single line of code.

    How is this difficult to understand? How many different times, in how many different ways, do I have to restate this before you finally (no pun intended) grasp the obvious?!?



  • @Mason_Wheeler said:

    Well, apparently someone cares enough to write that template that unlocks it upon destruction.

    Not destruction of the mutex. Destruction of the object that represents ownership of the mutex. Once your thread gives up ownership of the mutex, the mutex still exists, and is available for other threads to take ownership.


  • Winner of the 2016 Presidential Election

    @LaoC said:

    But when you have to forego all the modern safety features of C++ to do what you need to do, there's little point in using it.

    You don't necessarily have to do that (see @kian's post, I totally missed that you misunderstood the limitations of unique_ptr) and even if you do, you can still use the modern features in other parts of the code to make your life easier.

    @LaoC said:

    That's not much different from "I know that I have to free what I malloced"

    Yes, there's a huge difference between remembering a basic rule (never use feature X) and having to inspect every single code path to make sure all resources are cleaned up correctly all the time.



  • @Mason_Wheeler said:

    How is this difficult to understand? How many different times, in how many different ways, do I have to restate this before you finally (no pun intended) grasp the obvious?!?

    Apparently, you like typing the same thing over and over again. You wouldn't defend try-finally otherwise. Really, you should be thanking us.



  • @Kian said:

    Not destruction of the mutex. Destruction of the object that represents ownership of the mutex. Once your thread gives up ownership of the mutex, the mutex still exists, and is available for other threads to take ownership.

    Oh! You mean holding the lock on the mutex. ("Ownership" has a completely different meaning, particularly when we're discussing the freeing of objects!) OK, that actually makes a little bit more sense conceptually.

    It's still stupid to create an entire class to do what you should be able to do in one line of code without having to create a class for it. (And even stupider to create a template to create that class!)


  • Winner of the 2016 Presidential Election

    @Mason_Wheeler said:

    I'm arguing that creating an entire class to do something in its destructor inside a magic try/finally block is needlessly complex versus using a try/finally block without having to create any classes for what can be done with one single line of code.

    How is this difficult to understand? How many different times, in how many different ways, do I have to restate this before you finally (no pun intended) grasp the obvious?!?

    And how many times do I have to state that You don't have to do anything that complex? Why do you care if the internals of a feature are a bit complicated if the end result is that you don't have to worry about freeing resources anymore. At all. Which you do when you have to put finally blocks everywhere. A whole class of bugs has just been eliminated for you!


  • Winner of the 2016 Presidential Election

    @Mason_Wheeler said:

    It's still stupid to create an entire class

    "entire class"

    Holy shit, what do you have against classes?

    @Mason_Wheeler said:

    you should be able to do in one line of code

    For the end user (developer), it IS one line of code. So what are you complaining about?



  • @asdf said:

    Why do you care if the internals of a feature are a bit complicated if the end result is that you don't have to worry about freeing resources anymore. At all. Which you do when you have to put finally blocks everywhere.

    Because when your language advertises its high performance, internals matter. Details like this matter. Creating an entire class to do a single line of code's job is just plain ridiculous, period.

    @asdf said:

    Which you do when you have to put finally blocks everywhere.

    See above, re: "try/finally is not about freeing resources unless you're so brain-damaged by C++ that you literally can't see it in any other way; it's about exception-safe, guaranteed reversible state changes."

    @asdf said:

    A whole class of bugs has just been eliminated for you!

    If by "eliminated" you mean "swept under the rug, hidden within an abstraction inversion that makes it more difficult to debug when something inevitably goes wrong," then yes, I agree entirely.

    @asdf said:

    what do you have against classes?

    I have nothing against classes. I have plenty against using the wrong tool for the job. Creating an entire class that only exists for its destructor is a needlessly heavyweight solution when a single line of code, without gratuitous object construction and destruction, would work just as well.


  • Winner of the 2016 Presidential Election

    @Mason_Wheeler said:

    Because when your language advertises its high performance, internals matter. Details like this matter.

    Creating a template class like unique_lock or unique_ptr has exactly ZERO runtime overhead. Not a single CPU instruction is necessary for doing that. Maybe that's what you're not getting.

    @Mason_Wheeler said:

    it's about exception-safe, guaranteed reversible state changes

    That's what RAII is about as well.

    @Mason_Wheeler said:

    If by "eliminated" you mean "swept under the rug, hidden within an abstraction inversion that makes it more difficult to debug when something inevitably goes wrong,"

    You've never used C++11. If you had, you'd know that code using those RAII wrappers is mostly guaranteed to be correct when it compiles.



  • There is no construction and destruction in the compiled assembly. There's just the call to the function. See the example I used above of combining unique ptr with malloc. It's literally as fast as doing it manually, but not error prone.



  • @asdf said:

    Creating a template class like unique_lock or unique_ptr has exactly ZERO runtime overhead. Not a single CPU instruction is necessary for doing that. Maybe that's what you're not getting.

    How do you call a function (such as a destructor) with no function call overhead? Does this template guarantee that the destructor will be inlined in every possible use case?

    @asdf said:

    You've never used C++11. If you had, you'd know that code using those RAII wrappers is mostly guaranteed to be correct when it compiles.

    Yeah, mostly. The problem with mostly is that the more layers you bury your mostly under, the harder it is to get at or fix when it breaks down.


  • Considered Harmful

    @Kian said:

    @LaoC said:
    Now as far as I read, the "unique" part means there can only ever be one copy of that pointer so it's always clear who owns it and when it needs to get destroyed. If I'm getting that correctly, that doesn't work with hardware registers.
    You're not getting that correctly. The unique means that there can only be one owner (as opposed to shared, where there are many owners). The owner is the one responsible for freeing the memory. You can get raw pointers out of a unique_ptr, and they behave just like you would expect. You're just not supposed to free them. You let the unique_ptr object do that.

    And you can "move" the ownership around, in which case the original unique_ptr loses ownership and another pointer takes over the responsibility.


    I stand corrected. But … I do have to keep the object around somewhere, right? If I wanted to free buffers in an interrupt handler when DMA is done, I'd have to have a table to find the original object and manually trigger the destructor instead of just kfreeing the pointer from hardware?



  • You do have to keep it around. How you do it is up to you. If you stored it in a table, you have to delete the entry in the table, same as before. You just don't need to remember to call the free function on it first.

    I'm assuming the interrupt handler is written in C++, of course.


  • Winner of the 2016 Presidential Election

    @Mason_Wheeler said:

    How do you call a function (such as a destructor) with no function call overhead?

    Well, the destructor has to be called, of course. It contains what you have to do to clean up the resource and has to be executed whether you use RAII or not.

    @Mason_Wheeler said:

    Does this template guarantee that the destructor will be inlined in every possible use case?

    Guaranteed: Probably not, since the compiler is given a lot of freedom. But those templates are designed in a way that every sane compiler will inline what it can. For example, I'm pretty sure that every single C++11-capable compiler will inline the lambda passed to a scope_exit (see draft I linked).

    @Mason_Wheeler said:

    The problem with mostly is that the more layers you bury your mostly under, the harder it is to get at or fix when it breaks down.

    Uh, okay. Please show me an example of code using one of these templates which fails and is not easy to debug. Otherwise I won't believe your claim, since you obviously don't use C++11.

    Those templates are simple enough that there's not much that can go wrong inside of them.



  • @asdf said:

    Well, the destructor has to be called, of course. It contains what you have to do to clean up the resource and has to be executed whether you use RAII or not.

    Unless, of course, you're not using RAII and you can just write a single line of code inline (no need for templates, lambdas, or anything else,) and not have to call the destructor.

    Seriously, there's a reason why the phrase "explicit is better than implicit" exists.



  • @Mason_Wheeler: @asdf's point is that it's safer (and more economic in effort) to type:

        class mutex_lock : noncopyable
        {
            mutex& m;
        public:
            mutex_lock(mutex& m) : m(m) { m.lock(); }
            ~muxex_lock() { m.unlock(); }
        };
    
        void SomeFunction()
        {
            mutex_lock lk(myMutex);
            //Do stuff
        }
    
        void SomeOtherFunction()
        {
            mutex_lock lk(myMutex);
            //Do stuff
        }
        void SomeMoreFunction()
        {
            mutex_lock lk(myMutex);
            //Do stuff
        }
        void LetsAddAFewMoreFunctions()
        {
            mutex_lock lk(myMutex);
            //Do stuff
        }
        void AndAnother()
        {
            mutex_lock lk(myMutex);
            //Do stuff
        }
    

    Than:

        void SomeFunction()
        {
            myMutex.lock();
            try
            {
                //Do stuff
            }
            finally { 
                //Other clean up
                myMutex.unlock();
            }
        }
    
        void SomeOtherFunction()
        {
            myMutex.lock();
            try
            {
                //Do stuff
            }
            finally { 
                //Other clean up
                myMutex.unlock();
            }
        }
        void SomeMoreFunction()
        {
            myMutex.lock();
            try
            {
                //Do stuff
            }
            finally { 
                //Other clean up
                myMutex.unlock();
            }
        }
        void LetsAddAFewMoreFunctions()
        {
            myMutex.lock();
            try
            {
                //Do stuff
            }
            finally { 
                //Other clean up
                //whoops!
            }
        }
        void AndAnother()
        {
            myMutex.lock();
            try
            {
                //Do stuff
            }
            finally { 
                //Other clean up
                myMutex.unlock();
            }
        }
    

    PS: You should program in C++/CLI, which offers both destructors and finally blocks (and I have to admit those are useful when your code is unique enough).

    PPS: Discourse, stop adding line feeds to everything I copy and paste!



  • @Mason_Wheeler said:

    Seriously, there's a reason why the phrase "explicit is better than implicit" exists.

    Yes, in the Zen of Python. It's a joke. On the other hand, the DRY principle (Don't Repeat Yourself) is a well respected design principle.


  • Winner of the 2016 Presidential Election

    @Mason_Wheeler said:

    Unless, of course, you're not using RAII and you can just write a single line of code inline

    • try
    • finally
    • <actual code here>

    That's at least three lines of code. Repeat for each scope. (-> code duplication for free!)

    @Mason_Wheeler said:

    and not have to call the destructor.

    I was talking about unique_ptr here, which destructs an actual object. The destructor of the "wrapper" itself will obviously be inlined.

    @Kian said:

    > Seriously, there's a reason why the phrase "explicit is better than implicit" exists.

    Yes, in the Zen of Python. It's a joke. On the other hand, the DRY principle (Don't Repeat Yourself) is a well respected design principle.

    QFT

    And even though "explicit is better than implicit" is true sometimes, this is not one of those cases. There's no "magic" happening behind the scenes, all your code is doing is the obvious thing, which it does anyway (local variables go out of scope -> object destruction).


  • Winner of the 2016 Presidential Election

    I was also talking about this case:

    
    std::unique_lock<std::mutex> foo() {
        // lock some mutex
        return lock;
    }
    
    std::unique_lock<std::mutex> bar() {
        // do stuff
        return foo();
    }
    
    int main() {
        auto lock = bar();
        // do stuff
        return 0;
    }
    
    

    Really easy to handle with RAII, extremely unintuitive and error-prone if you don't have an object that represents the lock you're holding.



  • @Medinoc said:

    PS: You should program in C++/CLI, which offers both destructors and finally blocks (and I have to admit those are useful when your code is unique enough).

    When I'm doing CLR programming, I generally use Boo, which has powerful metaprogramming built in. I would accomplish the example above by the use of the lock macro, (which comes standard with the Boo language,) which expands to essentially the second example. Guaranteed, well-tested safety, no spurious object creation and destruction, and unlike C++, the compiler has a pipeline that will show you the final version of your program before codegen, after all macros have been expanded and all implicit conversions and transformations have been performed. You don't need it too often, but when you do, it makes debugging the tricky stuff soooo much easier.

    @Kian said:

    Yes, in the Zen of Python. It's a joke.

    You are apparently not familiar with the concept of "ha ha but serious", which is prominent among computing jokes.

    @asdf said:

    That's at least three lines of code. Repeat for each scope. (-> code duplication for free!)

    As opposed to a line for a { to open a scope, a line for a } to close the scope, and at least one line to set up your disposer object?

    @asdf said:

    And even though "explicit is better than implicit" is true sometimes, this is not one of those cases. There's no "magic" happening behind the scenes, all your code is doing is the obvious thing, which it does anyway (local variables go out of scope -> object destruction).

    That's not obvious at all. What if you got that local from something else that's still holding a reference to it. (Oh, right! C++'s object model is utterly broken. It has objects-as-value-types, which should never exist in the first place. But that's a completely different discussion.)


  • Winner of the 2016 Presidential Election

    @Mason_Wheeler said:

    What if you got that local from something else that's still holding a reference to it.

    That's why classes like unique_ptr and unique_lock exist: They can only be moved, not copied, so there is no other reference (unless you're deliberately abusing C++).

    So: Bzzzzzt, wrong, try again.



  • @Mason_Wheeler said:

    As opposed to a line for a { to open a scope, a line for a } to close the scope

    Now you're just arguing in bad faith, you need two of each when you use try/finally!



  • @Mason_Wheeler said:

    You are apparently not familiar with the concept of "ha ha but serious", which is prominent among computing jokes.

    Yes, I'm also familiar with the concept of people writing things that are serious but wrong. Like a language where whitespace has meaning. Hey, that's Python too!

    Besides, do you use explicit type names where var (in C#) or auto (in C++) would suffice?


  • Winner of the 2016 Presidential Election

    @Kian said:

    Like a language where whitespace has meaning.

    If you want to bash Python, I'll have to start disagreeing with you.

    @Kian said:

    Besides, do you use explicit type names where var (in C#) or auto (in C++) would suffice?

    :hand:

    I do that quite often, whenever the return type of a function cannot be inferred from its name.


  • area_pol

    @ronin said:

    Its a special type of pointer. FILE is already a type. The FILE * semantic actually tells you that it is a FILE pointer. FILE is the type. * signifies that its a pointer to a file. What would actually change if you omit the pointer? Nothing. Virtually no one tries to use free() on a file pointer. It is not even an issue.

    Yeah, because you remember you can't do it on a FILE. But why would you have to remember it? And what if you see Foo *? Can you call free on it?

    @ronin said:

    Doing fclose() on a file is not an issue amongst C programmers

    It's not an issue because it's something you must do. In C++ this isn't an issue because you don't have to do it.

    @ronin said:

    Show me a large security hole that has resulted from it. Is not more complicated than doing .close() on a file object. You are arguing non issues.

    So you really don't see any difference between needing to track every execution path through the lifetime of some data and manually adding the correct deallocation function everywhere, for doing literally nothing? Let me repeat that: literally nothing. Use a smart pointer and you don't have to call delete/close/free/whatever. And you are guaranteed there will bo no memory leak and no double free etc. You seriously want to argue there no difference between that and C?

    @ronin said:

    You mean the one that is based on the Linux kernel, which in turn is written in C? ( https://en.wikipedia.org/wiki/Tizen ). Given how unenthusiastic you are about C, why haven't you reimplement the rest of the kernel in C++?

    You were told about 10 times already. Do you homework and read it.

    @ronin said:

    I though that was the meat of your argument, that you know what is it like to implement an OS from the ground up in C++.

    No, the meat of the argument is that C is an unsafe and horrible language, and C programmers are mostly idiots who fail to see reason and would rather argue to death, defending their language, than to admit there are better alternatives. Oh wait...

    @ronin said:

    But regardless, you must have some killer performance measurements of what writing those not-quite-userspace parts of the OS in C++11 are. Can you share them with us?

    Nope, because I don't have them, nice try. But what you have is Google and tons of info where and why C++ outperforms C. Hell, I even linked a book on it.

    @ronin said:

    C++ has worse spatial locality than C.

    Bullshit. Saying something doesn't exactly make it true, you know, especially when it so absurd.

    @ronin said:

    Compilers can do that too now. I was hesitant to mention sanitizer routines since I don't know if they exist in GCC. On clang -fsanitize=< memory/address/etc > would do exactly that.

    But WHY on earth would you rather do it, when can NOT do it and be sure it works?

    @ronin said:

    Exception handling has an overhead.

    Especially those zero-cost exceptions. Man, are those a runtime penalty! And small note: you don't have to use them (but you actually should; topic for another time maybe).

    @ronin said:

    You claim that you can get much better performance writing the entire OS in C++? Fine, so be prepared to backup your claims instead of restorting to angry outbursts against the C language or claiming that you are the only one in possesion of the truth.

    For crying out loud - how many times do you have to be reminded why OSes are written in C mostly? And how many times can you NOT search why/where C++ outperforms C?

    @ronin said:

    Yes, I read the link you posted.C++ can qsort() faster than C, but that doesn't prove that its faster for coding operating systems.

    That means you totally missed the point. Read that book again till you do. Hint: it isn't dependent on kernel-user space, it's a language feature.

    @ronin said:

    Generating a 10000 element list and asking for the first element is faster than both C and C++ because it computes fewer things. That doesn't make it faster for the general case and I wouldn't even want to see the code that generates.

    That page explained exactly why it's faster in general case. Try again, this time by actually reading it.

    @ronin said:

    The claim that C has to evolve and be replaced is a valid one. The claim that is a bad language is, at best a dubious one

    Because all those CVEs we get are just by coincidence mostly in C, rather than C++, code.

    @ronin said:

    The claim that C++ can replace it for ALL THE THINGS has to have some foundations

    Given it's a superset (besides some minor quirks) of C, than yes - it can replace C.



  • How do people care so much about C. There's like 120 new posts this morning. Jesus.



  • @NeighborhoodButcher said:

    Let me repeat that: literally nothing. Use a smart pointer and you don't have to call delete/close/free/whatever. And you are guaranteed there will bo no memory leak and no double free etc.

    Then why are memory leaks so common in released software written in C++? Must not be as magically simple as you claim.

    Memory leaks are common in C++ software. Memory leaks are common in software written in other native languages, where memory must be managed manually. Memory leaks are common in managed code programs, where there's supposed to be a garbage collector that magically makes memory leaks not exist (hey, that claim looks familiar!) but someone left a reference assigned somewhere by mistake.

    I've been programming my entire life, (well, not literally, but since third grade, so close enough,) and I have never seen any technology, in any language or framework, that magically removes the need to think about memory management and gets rid of memory leaks for you.

    What I have seen, many times, is snake oil salesmen claiming such a thing exists, which removes the perception of the need to think about memory management, which in turn removes a lot of actual thinking about it, which then causes inattentive people to create memory leaks and not know how to track them down or fix them. I've seen that enough times to know that any such claim should be considered highly suspicious, if not a flat-out lie, by default.


  • Winner of the 2016 Presidential Election

    @Mason_Wheeler said:

    Then why are memory leaks so common in released software written in C++? Must not be as magically simple as you claim.

    • Because most programs are still mostly C++98
    • Because most Tutorials still teach the "old" C++. C++11 is meant to be used differently
    • Because people are idiots

    @Mason_Wheeler said:

    What I have seen, many times, is snake oil salesmen claiming such a thing exists, which removes the perception of the need to think about memory management

    Nobody ever claimed you don't need to think about memory management in C++. Our claim was that you don't have to think about proper object destruction and the various code paths.

    If you use RAII correctly, managing memory in C++ becomes as easy as managing memory in a garbage-collected language. Making sure you don't keep references to objects you don't need is still your responsibility, like in any other language.



  • @asdf said:

    If you use RAII correctly, managing memory in C++ becomes as easy as managing memory in a garbage-collected language.

    ...which is also full of memory leaks, as I pointed out above.


  • Winner of the 2016 Presidential Election

    @Mason_Wheeler said:

    > If you use RAII correctly, managing memory in C++ becomes as easy as managing memory in a garbage-collected language.

    ...which is also full of memory leaks, as I pointed out above.

    :moving_goal_post:

    Seriously, what's your point right now? That programming is hard? That people are idiots?



  • My point is what it has been this whole time: that RAII is a needlessly complicated abstraction inversion for every use case that does not involve resource management. (I'm not sure how we even got on the subject of memory leaks!)


  • Winner of the 2016 Presidential Election

    @Mason_Wheeler said:

    My point is what it has been this whole time: that RAII is a needlessly complicated abstraction inversion for every use case that does not involve resource management.

    I feel like we've already debunked that myth, so:

    Please name 5 use cases for finally that do not involve resource management and are made unnecessarily complicated by the fact that C++ favors RAII.



  • @asdf said:

    I feel like we've already debunked that myth

    Where? How? I posit that it's impossible to do in RAII what you can do with try/finally without doing something needlessly complicated involving creating an entire class, and what do I get? Examples of creating classes! Examples of creating templates that create classes! How exactly do you "debunk" my thesis by demonstrating the truth of it?


  • Winner of the 2016 Presidential Election

    @Mason_Wheeler said:

    without doing something needlessly complicated involving creating an entire class

    How many times do I have to repeat it?

    Those classes are in the standard library.

    You don't need to do anything, just use them.


Log in to reply