Live data from Hacker News

Why should I have written ZeroMQ in C, not C++ (2012)

250bpm.com

51–60 of 170 posts

Re: Why should I have written ZeroMQ in C, not C++ (2012)

#51

I think there's a lot of merit to this post, but it really shows its age. Modern C++ typically suffers from only the last point raised - exceptions during destruction, but that goes against the all good C++ practices. Overall, the author's approach to exception use is flawed. It's bad form to use exceptions for code flow purposes, as per their example. If you throw an exception in the same block that catches the exce…

A problem is if you use RAII for other resources than memory, for example files. You want to put the close function in the destructor then, but close can throw an exception. http://www.cplusplus.com/reference/fstream/ofstream/close/ (Of course the solution is easy, you wrap close in a try catch block, but as alwaya all the solutions are easy in c++ but this is one of the surprising corners, which might be done more often wrong then not)

Re: Why should I have written ZeroMQ in C, not C++ (2012)

#52

Earlier quoted context omitted.

> 3. Throwing in destructors is not a C++ problem, it's a general semantic problem around resources that aren't guaranteed to be freed up successfully. They are a pain in any language and you can only do best-effort approaches for not leaking them. C++ is quite good here, arguably quite better than Rust, since in C++ you can add a `noexcept(true)` clause to your destructor, and if it throws, your program terminates.…

I am not sure what you mean here. "throwing in a destructor", aka "panic during Drop" in Rust terms, runs the risk of seeing "thread panicked while panicking. aborting." In general, Rust treats panics as being something that terminates execution, not something for error handling. Where are you seeing these memory leaks in Rust code?

In Rust, this destructor is perfectly fine:

    impl Drop for Foo { 
       fn drop(..) { 
          self.cleanup();
          self.free_memory();
       }
    }
even if `self.cleanup()` panics. When that happens, `self.free_memory` will never be called, and memory will be leaked if `panic=unwind`, which is the default behavior. If there is a `catch_unwind` somewhere, these leaks will grow over time.

The same code in C++:

   ~foo() {
       this->cleanup();
       this->free_memory();
   }
does not have this issue, because if `this->cleanup()` unwinds, that unwind makes `~foo` abort, because `~foo` is `noexcept(true)` by default.

In Rust, one cannot make `Drop::drop` be "nounwind", so to emulate C++'s behavior, one needs to write:

    impl Drop for Foo {
        fn drop(...) { 
           if let Err(_) = cath_unwind(|| { 
              self.cleanup(); self.free_memory();
           }) {
               abort();
           }
        }
    }
or use a `DropGuard` to make sure that `self.free_memory()` is called if `self.cleanup` throws, etc.

Destructors being fallible are very weird things. In C++, destructors are infallible (at least by default). Rust supports fallible destructors, with all its cost (every time an object is dropped, doing this can fail, adding another "return" point from your function), yet minimal or zero value.

At least, I couldn't find any examples in the Rust book / reference / rust-by-example that show in which situations unwinding from a destructor is a good thing to do.

Re: Why should I have written ZeroMQ in C, not C++ (2012)

#53

I'm a bit confused about the current state of ZeroMQ and its relatives. There's nanomessage, which for some reason didn't pan out and is superseded by nng(?), but ZMQ still seem to be the most popular option. If I were to pick a broker-less message queue today, what should I pick?

My understanding: Pick zmq if you want something safe and battle-tested with good client library support. Pick nng if you want to be at the forefront of new tech or need some of its unique features over zmq ( https://nanomsg.org/documentation-zeromq.html ). Performance-wise nng still seems like it's inferior to zmq because it's not as heavily optimized. There are lots of performance-related unresolved Github issues.…

nanomsg was abandoned? I know the previous maintainer moved on to nng, but no one is maintaining it now?

Re: Why should I have written ZeroMQ in C, not C++ (2012)

#55
post #44

By now he probably realizes that the Java version is what he was looking for all along: https://github.com/zeromq/jeromq Why C/C++ coders don't even write "Hello World" in Java SE before drifting away into rewriting the ball bearing (1907) part of the wheel is beyond me. The Throwable class is exceptional in every way, it wastes almost nothing and gives the programmer the ability to catch all problems that occur in t…

> Java does not crash

    #
    # A fatal error has been detected by the Java Runtime Environment:
    #
    #  SIGSEGV (0xb) at pc=0x00007fae709d4a10, pid=18032, tid=140386298201856
    #
    # JRE version: Java(TM) SE Runtime Environment (8.0_60-b27) (build 1.8.0_60-b27)
    # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.60-b23 mixed mode linux-amd64 compressed oops)
    # Problematic frame:
    # V  [libjvm.so+0xa8aa10]  Unsafe_SetNativeLong+0xf0
    #
    # Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
    #
    # An error report file with more information is saved as:
    #
    # /opt/production/hs_err_pid18032.log
    #
    # If you would like to submit a bug report, please visit:
    #   http://bugreport.java.com/bugreport/crash.jsp
    #

Re: Why should I have written ZeroMQ in C, not C++ (2012)

#56
post #13

I was trying to figure out if this was satire. C++ gives you strictly more tools, particularly for guaranteeing reliability, and they're all optional. What particularly laughable here is the split ctor/init pattern is allegedly driving them towards C, whereas C doesn't even give you dtors! If you're forgetting your init calls, are you really telling me you're not going to forget your destructor call? There are plenty…

> C++ gives you strictly more tools, particularly for guaranteeing reliability, and they're all optional. You are right. But it doesn't help. When I used C++, I constantly shot myself in the foot by using its features. It did not feel like overusing, but I just wanted type-safety and to replace macros. It was impossible to restrict to useful features, because they are all useful in their way, only they come at the pr…

You wanted type-safety and to replace macros, but then you went back to C?

Re: Why should I have written ZeroMQ in C, not C++ (2012)

#57
post #13

I was trying to figure out if this was satire. C++ gives you strictly more tools, particularly for guaranteeing reliability, and they're all optional. What particularly laughable here is the split ctor/init pattern is allegedly driving them towards C, whereas C doesn't even give you dtors! If you're forgetting your init calls, are you really telling me you're not going to forget your destructor call? There are plenty…

> C++ gives you strictly more tools, particularly for guaranteeing reliability, and they're all optional. You are right. But it doesn't help. When I used C++, I constantly shot myself in the foot by using its features. It did not feel like overusing, but I just wanted type-safety and to replace macros. It was impossible to restrict to useful features, because they are all useful in their way, only they come at the pr…

this is ridiculous. Every remotely powerful programming language gives you an opportunity to shoot yourself in the foot. c++ is so simple, and with every new standard it becomes even better and easier to use. How can anybody possibly complain is beyond me.

Re: Why should I have written ZeroMQ in C, not C++ (2012)

#58

I think there's a lot of merit to this post, but it really shows its age. Modern C++ typically suffers from only the last point raised - exceptions during destruction, but that goes against the all good C++ practices. Overall, the author's approach to exception use is flawed. It's bad form to use exceptions for code flow purposes, as per their example. If you throw an exception in the same block that catches the exce…

Another way to support RAII without throwing exceptions is to somehow make the error case an explicit configuration of the class with well-defined behavior. For example, in a "File" class with no filedescriptor you could return a defined "error" from any function calls which would require a filedescriptor. Receiving such a return value or return structure would then signal that the File class is not in a working state.

Re: Why should I have written ZeroMQ in C, not C++ (2012)

#59

Earlier quoted context omitted.

I am not sure what you mean here. "throwing in a destructor", aka "panic during Drop" in Rust terms, runs the risk of seeing "thread panicked while panicking. aborting." In general, Rust treats panics as being something that terminates execution, not something for error handling. Where are you seeing these memory leaks in Rust code?

In Rust, this destructor is perfectly fine: impl Drop for Foo { fn drop(..) { self.cleanup(); self.free_memory(); } } even if `self.cleanup()` panics. When that happens, `self.free_memory` will never be called, and memory will be leaked if `panic=unwind`, which is the default behavior. If there is a `catch_unwind` somewhere, these leaks will grow over time. The same code in C++: ~foo() { this->cleanup(); this->free_m…

Unless I'm misunderstanding your comment, you can make panics abort rather than unwind.

https://doc.rust-lang.org/edition-guide/rust-2018/error-hand...

Re: Why should I have written ZeroMQ in C, not C++ (2012)

#60
post #34

Earlier quoted context omitted.

...and now you know what a "factory method" is.

Almost. One of the biggest advantages of a class factory is that it can return a pointer to an interface rather than a concrete class.

Yes but that doesn't meaningfully change what a "factory" is: a factory returns an already-constructed object or smart-pointer-to-that-object you can work with. Whether or not that object is concrete or an interface to a hidden implementation is less important IMO.
Post reply on HN