C++ says “We have try at home”

(devblogs.microsoft.com)

47 points | by ibobev 6 hours ago ago

35 comments

  • jasode 3 hours ago ago

    The submitted title is missing the salient keyword "finally" that motivates the blog post. The actual subtitle Raymond Chen wrote is: "C++ says “We have try…finally at home.”"

    It's a snowclone based on the meme, "Mom, can we get <X>? No, we have <X> at home." : https://www.google.com/search?q=%22we+have+x+at+home%22+meme

    In other words, Raymond is saying... "We already have Java feature of 'finally' at home in the C++ refrigerator and it's called 'destructor'"

    To continue the meme analogy, the kid's idea of <X> doesn't match mom's idea of <X> and disagrees that they're equivalent. E.g. "Mom, can we order pizza? No, we have leftover casserole in the fridge."

    So some kids would complain that C++ destructors RAII philosophy require creating a whole "class X{public:~X()}" which is sometimes inconvenient so it doesn't exactly equal "finally".

    • locknitpicker 2 minutes ago ago

      > So some kids would complain that C++ destructors RAII philosophy require creating a whole "class X{public:~X()}" which is sometimes inconvenient so it doesn't exactly equal "finally".

      Those figurative kids would be stuck in a mental model where they try to shoehorn their ${LanguageA} idioms onto applications written in ${LanguageB}. As the article says, C++ has destructors since the "C with Classes" days. Complaining that you might need to write a class is specious reasoning because if you have a resource worth managing, you already use RAII to manage it. And RAII is one of the most fundamental and defining features of C++.

    • thombles 2 hours ago ago

      HN has some heuristics to reduce hyperbole in submissions which occasionally backfire amusingly.

      • mort96 2 hours ago ago

        Yeah it's a huge mistake IMO. I see it fucking up titles so frequently, and it flies in the face of the "do not editorialise titles" rule:

            [...] please use the original title, unless it is misleading or linkbait; don't editorialize.
        
        It is much worse, I think, to regularly drastically change the meaning of a title automatically until a moderator happens to notice to change it back, than to allow the occasional somewhat exaggerated original post title.

        As it stands, the HN title suggests that Raymond thinks the C++ 'try' keyword is a poor imitation of some other language's 'try'. In reality, the post is about a way to mimic Java's 'finally' in C++, which the original title clearly (if humorously) encapsulates. Raymond's words have been misrepresented here for over 4 hours at this point. I do not understand how this is an acceptable trade-off.

        • mcny 29 minutes ago ago

          Personally, I would rather we have a lower bar for killing submissions quickly with maybe five or ten flags and less automated editorializing of titles.

      • tux3 2 hours ago ago

        It's rare to see the mangling heuristics improve a title these days. There was a specific type of clickbait title that was overused at the time, so a rule was created. And now that the original problem has passed, we're stuck with it.

      • pjmlp 34 minutes ago ago

        You have a few minutes to change the title after the submission, I do it all the time.

    • vidarh an hour ago ago

      I'm curious about the actual origin now, given that a quick search shows only vague references or claim it is recent, but this meme is present in Eddie Murphys "Raw" from 1987, so it is at least that old.

  • winternewt an hour ago ago

    Destructors are vastly superior to the finally keyword because they only require us to remember a single time to release resources (in the destructor) as opposed to every finally clause. For example, a file always closes itself when it goes out of scope instead of having to be explicitly closed by the person who opened the file. Syntax is also less cluttered with less indentation, especially when multiple objects are created that require nested try... finally blocks. Not to mention how branching and conditional initialization complicates things. You can often pair up constructors with destructors in the code so that it becomes very obvious when resource acquisition and release do not match up.

    • yoshuaw 11 minutes ago ago

      I couldn't agree more. And in the rare cases where destructors do need to be created inline, it's not hard to combine destructors with closures into library types.

      To point at one example: we recently added `std::mem::DropGuard` [1] to Rust nightly. This makes it easy to quickly create (and dismiss) destructors inline, without the need for any extra keywords or language support.

      [1]: https://doc.rust-lang.org/nightly/std/mem/struct.DropGuard.h...

    • sigwinch28 32 minutes ago ago

      A writable file closing itself when it goes out of scope is usually not great, since errors can occur when closing the file, especially when using networked file systems.

      https://github.com/isocpp/CppCoreGuidelines/issues/2203

    • jchw 36 minutes ago ago

      Destructors and finally clauses serve different purposes IMO. Most of the languages that have finally clauses also have destructors.

      > Syntax is also less cluttered with less indentation, especially when multiple objects are created that require nested try... finally blocks.

      I think that's more of a point against try...catch/maybe exceptions as a whole, rather than the finally block. (Though I do agree with that. I dislike that aspect of exceptions, and generally prefer something closer to std::expected or Rust Result.)

      • mort96 30 minutes ago ago

        > Most of the languages that have finally clauses also have destructors.

        Hm, is that true? I know of finally from Java, JavaScript, C# and Python, and none of them have proper destructors. I mean some of them have object finalizers which can be used to clean up resources whenever the garbage collector comes around to collect the object, but those are not remotely similar to destructors which typically run deterministically at the end of a scope. Python's 'with' syntax comes to mind, but that's very different from C++ and Rust style destructors since you have to explicitly ask the language to clean up resources with special syntax.

        Which languages am I missing which have both try..finally and destructors?

        • jchw 4 minutes ago ago

          I don't view finalizers and destructors as different concepts. The notion only matters if you actually need cleanup behavior to be deterministic rather than just eventual, or you are dealing with something like thread locals. (Historically, C# even simply called them destructors.)

        • dist-epoch 22 minutes ago ago

          Technically CPython has deterministic destructors, __del__ always gets called immediately when ref count goes to zero, but it's just an implementation detail, not a language spec thing.

    • dist-epoch 24 minutes ago ago

      Python has that too, it's called a context manager, basically the same thing as C++ RAII.

      You can argue that RAII is more elegant, because it doesn't add one mandatory indentation level.

  • tryfinally 2 hours ago ago

    I always wonder whether C++ syntax ever becomes readable when you sink more time into it, and if so - how much brain rewiring we would observe on a functional MRI.

    • deliciousturkey an hour ago ago

      In my opinion, C++ syntax is pretty readable. Of course there are codebases that are difficult to read (heavily abstracted, templated codebases especially), but it's not really that different compared to most other languages. But this exists in most languages, even C can be as bad with use of macros.

      By far the worst in this aspect has been Scala, where every codebase seems to use a completely different dialect of the language, completely different constructs etc. There seems to have very little agreement on how the language should be used. Much, much less than C++.

    • sigmoid10 2 hours ago ago

      It does... until you switch employers. Or sometimes even just read a coworker's code. Or even your own older code. Actually no, I don't think anyone achieved full readability enlightenment. People like me just hallucinated it after doing the same things for too long.

      • Yoric an hour ago ago

        Sadly, that is exactly my experience.

      • usrnm 2 hours ago ago

        And yet, somehow Lisp continues to be everyone's sweetheart, even though creating literal new DSLs for every project is one of the features of the language.

        • vkazanov an hour ago ago

          Lisp doesnt have much syntax to speak of. All of the DSLs use the same basic structure and are easy to read.

          Cpp has A LOT A of syntax: init rules, consts, references, move, copy, templates, special cases, etc. It also includes most of C, which is small but has so many basic language design mistakes that "C puzzles" is a book.

          • lmz 15 minutes ago ago

            The syntax and the concepts (const, move, copy, etc) are orthogonal. You could possibly write a lisp / s-exp syntax for c++ and all it would make better would be the macros in the preprocessor. The DSL doesn't have to be hard to read if it uses unfamiliar/uncommon project specific concepts.

        • varjag 20 minutes ago ago

          It's because DSLs there reduce cognitive load for the reader rather than add up to it.

          • usrnm 15 minutes ago ago

            Well-designed abstractions do that in every language. And badly designed ones do the opposite, again in all languages. There's nothing special about Lisp here

    • m-schuetz 2 hours ago ago

      "using namespace std;" goes a long way to make C++ more readable and I don't really care about the potential issues. But yeah, due to a lack of a nice module system, this will quickly cause problems with headers that unload everything into the global namespace, like the windows API.

      I wish we had something like Javascript's "import {vector, string, unordered_map} from std;". One separate using statement per item is a bit cumbersome.

  • NooneAtAll3 15 minutes ago ago

    > Update: Adam Rosenfield points out that Python 3.2 now saves...

    how old is this post that 3.2 is "now"?

  • mojuba 2 hours ago ago

    I like how Swift solved this: there's a more universal `defer { ... }` block that's executed at the end of a given scope no matter what, and after the `return` statement is evaluated if it's a function scope. As such it has multiple uses, not just for `try ... finally`.

    • dwattttt 2 hours ago ago

      I was contemplating what it would look like to provide this with a macro in Rust, and of course someone has already done it. It's syntactic sugar for the destructor/RAII approach.

      https://docs.rs/defer-rs/latest/defer_rs/

      • mojuba 19 minutes ago ago

        I don't know Rust but, can this `defer` evaluate after the `return` statement is evaluated like in Swift? Because in Swift you can do this:

            func atomic_get_and_inc() -> Int {
                sem.wait()
                defer {
                    value += 1
                    sem.signal()
                }
                return value
            }
    • troglo-byte an hour ago ago

          #include <iostream>
          #define RemParens_(VA) RemParens__(VA)
          #define RemParens__(VA) RemParens___ VA
          #define RemParens___(...) __VA_ARGS__
          #define DoConcat_(A,B) DoConcat__(A,B)
          #define DoConcat__(A,B) A##B
          #define defer(BODY) struct DoConcat_(Defer,__LINE__) { ~DoConcat_(Defer,__LINE__)() { RemParens_(BODY) } } DoConcat_(_deferrer,__LINE__)
      
          int main() {
              {
                  defer(( std::cout << "Hello World" << std::endl; ));
                  std::cout << "This goes first" << std::endl;
              }
          }
      • rezonant an hour ago ago

        Why would that be preferable to just using an RAII style scope_exit with a lambda

        • troglo-byte 23 minutes ago ago

          Meh, I was going to use the preprocessor for __LINE__ anyways (to avoid requiring a variable name) so I just made it an "old school lambda." Besides, scope_exit is in C++23 which is still opt-in in most cases.