18 ms·
Why should I have written ZeroMQ in C, not C++ (2012)
- hedora 6y agoGiven the article’s age, the issues brought up are reasonable. Now that it’s 8 years later, I’d summarize it as: - exceptions were a bad idea (in any language); don’t use them. - implementation inheritance and constructors / destructors that contain non-trivial logic were a bad idea; don’t use them. This basically means you shouldn’t be writing idiomatic object oriented code. Fortunately, C++ supports other programming paradigms, and in current C++, you can avoid both these historical warts; they shouldn’t affect day-to-day systems programming or error handling.
- herrkanin 6y agoI'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?
- fuzzybear3965 6y agoI'm planning on using core NATS (as opposed to NATS streaming) for a lot of my message queuing. A brokered message queue is the same as a brokerless message queue where message forwarding (brokering) is not performed...
- WnZ39p0Dgydaz1 6y agoMy 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 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 is not an option because it's essentially abandoned. I've been using nng for a project of mine and I'm happy with it so far.
- metadaemon 6y agoWould you say either is something you would put in a decently scaled production system?
- WnZ39p0Dgydaz1 6y agozmq is used in a many large-scale production systems. It's definitely safe to use and has been stable for a long time. nng is relatively new. Personally I have not had any issues with it. I'd look through the open Github issues and see if you can find any dealbreakers. The deciding factor may be language/library support. I would also classify it as "safe to use in production" though.
- Supermancho 6y agoUsing ZMQ (from C) at high volume, we found it dropped data, so we abandoned it.
- mycelium 6y agoWhat did you replace it with?
- Supermancho 6y agoDirect writes locally to ebs and then stream to S3 and async collation after.
- ndesaulniers 6y agonanomsg was abandoned? I know the previous maintainer moved on to nng, but no one is maintaining it now?
- WnZ39p0Dgydaz1 6y agoI don't know. Maybe it's not abandoned and maintained by someone. I was using the word loosely as in "focus has moved to nng" and it makes little sense to use nanomsg for a new project at this point. In my experience, if the core contributor moves on and it's not a company, it's only a matter of time before it's fully abandoned. Better to get away from it soon ;)
- commandlinefan 6y agoI've always felt that object-oriented programming as a concept (that is, define structures and then define functions to operate on them together) is useful: object-oriented programming as a language design is not. Once you understand what encapsulation, inheritance and polymorphism are, you're much better off using them as guidance to structure your program in an otherwise procedural language like C than wrestling with a few dozen new keywords and an opaque specification.
- acqq 6y agoI use C++ for decades, but I used it always for the projects that were supposed to last long. With that limitation, I've constantly avoided two things: 1) dependence on external C++ libraries, especially boost. 2) trying to always have the code using the latest changes in the standard proposals or the standards. With these two limitations, and a lot of additional internal "practices" it's possible to maintain a long-running C++ project without doing what I consider "wrong" stuff. Note: I grew up on assembly, so I was always biased by considering what the optimal code should be as presented to the CPU, after the compilation. The smaller the project and the environment around the project is, these my preferences could be relaxed. There are surely use cases where "anything could go." But then, the question is always, why not something "more convenient" than both C and C++? But now I also almost think that if I would be forced to reduce these my "hard projects" "rules and practices" to a minimum number of words, I would probably state it "only C allowed."
- StillBored 6y agoWell, using C++ as a C with objects has always been a choice. One I personally believe in. That means you basically write a C program, then when it makes sense you create c++ classes/templates as needed. At first this sounds like the C++ features are infrequently used, but the last major project I wrote like this, probably 90% of the code was encapsulated. But there was very little class->class communication outside of a few global classes due to the use of a dbus like abstraction allowing all the individual classes to communicate out of band. (sort of like a collection of micro services if you will all running in the same process as different threads using a global message broker).
- 6y ago
- sxyuan 6y agoIt seems like the author's main complaints are avoided if one follows the Google C++ style guide, which says: - Don't use C++ exceptions - Don't do work in constructors (prefer "Init" or factory functions instead) https://google.github.io/styleguide/cppguide.html https://google.github.io/styleguide/cppguide.html There may be other reasons to prefer C over C++, but if you don't like exceptions, you don't have to use them.
- hedora 6y agoRegardless of what the style guide says, multiple studies of real-world exception handling correctness have been done, and the results are clear: Very few (if any) real-world codebases correctly handle exceptions. As the article points out, correct exception handlers would grow exponentially with program size. People can’t reason about that, so they don’t handle things like multiple error paths, or they conflate one error code with another, and so on. The results hold across programming languages.
- Ididntdothis 6y ago“prefer "Init" or factory functions instead“ I vote for factory functions. They bring you pretty close to how C would do it.
- gumby 6y agoThe google style guide says “ On their face, the benefits of using exceptions outweigh the costs, especially in new projects. However, for existing code...” and they use a lot of existing code.
- pansa2 6y ago> they use a lot of existing code. Doesn’t everyone? Lots of C++ code is not exception safe, including large libraries such as Qt. If I’m writing code that uses such a library, would it be better to take Google’s approach and avoid using exceptions myself, or try to “wrap” the non-exception-safe library somehow?
- 6y ago
- Ididntdothis 6y agoWhen I did C++ I always ended up using a very small subset of it. No exceptions, no inheritance besides a few interfaces. But in general I agree with his point. There are a few things C could improve though. One would be a way to automatically clean up resources. Maybe something like “defer” in Go. I wonder if templates would fit into C. STL is super useful. A real string type would be good too.
- jdashg 6y agoThat new dialect of C exists, and it's called C++!
- deleted 6y ago[deleted]
- ExtremisAndy 6y agoYeah, I know what you mean by using a small subset of C++. To be perfectly honest (and I'm just a hobbyist who writes simple tools for myself... so I can get away with this a little easier than a professional could) but I got really frustrated when I was learning C++ and almost decided it wasn't worth it. Then I decided that I'd basically just treat C++ as C with strings, classes, and a few nifty, ready-made containers and useful algorithms. And doing this allowed me to enjoy the language quite a bit and do some very fun and useful things in it, all without becoming overwhelmed by its size/complexity. But again... this is as a hobbyist, nothing more.
- BubRoss 6y agoThere was a language called clay that was basically C with templates, operators, destructors and move semantics (with better syntax). It produced C ABI compatible compilation units and could pretty much slide into a C project. It used llvm but has been defunct for a long time. It is a shame because I think a lot could have been learned from people at least looking at it before coming up with their new llvm based native languages.
- anand-bala 6y agoAll the things you mentioned can be found in Zig! [1] It's exception semantics are very similar to Go, but unlike Go, you have to handle the error. It has constructs like `defer` and `errdefer` which allow you do clean up at the end of the scope or on exception respectively. It has support for Generics and Metaprogramming without having to resort to preprocessor macros. It just released v0.6.0 and is actively in development with an engaging and helpful community.[2] [1]: https://ziglang.org/ https://ziglang.org/ [2]: https://github.com/ziglang/zig/wiki/Community https://github.com/ziglang/zig/wiki/Community
- gumby 6y agoThis article is simultaneously both obsolete and completely current. Obsolete in that the C++ committee has addressed almost all of the issues raised (e.g. constructor semantics, xception semantics et al) though the discussion of site-of-error/handling-of-error continues unabated. Later versions of C++ (17and 20) are powerful and expressive systems programming languages that aren’t like the object-oriented messes of old. The reason that this article remains completely current is that I suspect the majority, and likely vast majority, of C++ is still written in C++03/C++11 and full of dreadful issues like the ones in the article.
- zwieback 6y agoTrue, I recently returned to C++ after many years absence and I'm thrilled with all the good stuff that has been added BUT it took me quite a while to learn how I'm going to unlearn/relearn my old practices.
- gumby 6y agoI decided to treat it as if it was a language I had never seen before. That helped a lot.
- mehh 6y agoLikewise, but also I now realise, after a long break I really like programming with "system languages". I get a certain amount of satisfaction that I don't get with higher-level languages, where I spend so much time plugging together half-built crappy modules. Partly this is because I have realised, I like knowing the nuts and bolts of what is going on in a computer and solving problems with consideration of how it will run. It also has less magic and crappy layers going on. C++ has evolved a lot from what I used many years ago, and it looks mostly for the better, thankfully we have escaped the 'thou must OO' age.
- zwieback 6y agoTotally agree. I enjoy the productivity in Python but when you look what's behind numpy, scipy, etc. there's a lot of C, C++ and even Fortran. It's good to know both.
- jdashg 6y agoI 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 of approaches besides split-init anyway, but it feels like the author feels pulled towards C for other reasons, and is looking for reasoning after the fact. C is not a magic bullet for simpler code! C is useful for solving social problems with deciding what features and patterns of C++ to use, if you've given up on code review and style guidelines. If you decide that's you, good luck! You'll need it.
- pradn 6y agoOne good alternative to ctor+init() is a static factory method with a private ctor. It's just as simple as in C.
- fluffything 6y agoNot sure what a "factory method" is, but struct Foo { private: int a = 0, b = 0; Foo() = default; Foo(int a, int b): a(a), b(b) {} public: static Foo init_zeroed() { return Foo{}; } static Foo init_ones() { return Foo{1, 1}; } }; will do. You can add as many static methods acting as "named constructors" as you want.
- stickfigure 6y ago...and now you know what a "factory method" is.
- Koshkin 6y agoAlmost. One of the biggest advantages of a class factory is that it can return a pointer to an interface rather than a concrete class.
- weyhhweyh 6y agoAre there any good resources (websites/books) to learn some of the new ways of doing things in C++ 20 vs older. Eg something that shows the old patterns with pitfalls and what replaces them in C++ 20?
- billfruit 6y agoBjarne Strsoustroup's short book "A Tour of Modern c++" fits the bill, and was written for the same purpose.
- jcelerier 6y agoAuthor asserts that "The decoupling between raising of the exception and handling it, that makes avoiding failures so easy in C++, makes it virtually impossible to guarantee that the program never runs info undefined behaviour." (and all the woes he encounters afterwards stems from trying to avoid exceptions due to that assertion). But that is not my experience at all. Exceptions are much more reliable than error codes - there will always be a case where you forget to propagate an error you got, but you cannot "forget" to propagate an exception... (and if you do, you can always run a debugger post-mortem to get a nice stacktrace of where the exception was thrown - `coredumpctl gdb` is a super nice tool for that on Linux systems with systemd). And languages without exceptions just end up with a galore of `if err != nil { panic(err); }` or similar horrors... Also, regarding "compiler is likely to produce more efficient code.", benchmarks have shown that exceptions are generally faster : http://nibblestew.blogspot.com/2017/01/measuring-execution-performance-of-c.html http://nibblestew.blogspot.com/2017/01/measuring-execution-p...
- rectang 6y ago> there will always be a case where you forget to propagate an error you got Not if the ecosystem supports Result types a la Rust (and others). You don't get to access the return value unless you deal with handling the error first.
- jcelerier 6y ago> You don't get to access the return value unless you deal with handling the error first. Well, that's exactly what I was referring to. 80% of the time you don't / can't know what to do with the error at a given call site, so from what I could see, either people end up doing nothing and "abandoning" the error (bad) or panicking (worse). It's better to just let the error go transparently so that if someone actually can do something meaningful upper in the call stack, it's possible. source : https://github.com/search?l=Rust&q=panic&type=Code https://github.com/search?l=Rust&q=panic&type=Code
- deleted 6y ago[deleted]
- laurowyn 6y agoI 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 exception, you've wasted a whole lot of time doing something an IF-statement could solve trivially. All projects that I have worked on that use C++ has declared that exceptions should be used in exceptional circumstances only - that is, when you cannot handle the error in the current code block and the function interface does not support returning enough information to the caller to detail what went wrong. An exception is more than just throwing your hands in the air and saying "I can't do that" - it has context, purpose, history. Getting rid of exceptions breaks some of the fundamental design concepts of the language. 2-step initialisation is the only way around errors during construction whilst forbidding exceptions. But 2-step init breaks a fundamental rule of RAII - the constructor acquires the resource and establishes all class invariants or throws an exception if that cannot be done. If the constructor is not allowed to throw exceptions, as per the language's standardised interface for constructors, then my library or application needs to be modified to support whatever process a third party library has defined as appropriate. There is a wide surface for bugs to creep in, let alone costing me time, money and effort in supporting whatever interface they've come up with. If an object has been constructed, it should be in a valid state unless an exception has been thrown, in which case I'm told what went wrong. If I can fix it before the stack is fully unwound, I can save the day. but if there's nothing I can do, the exception has to roll to the top as that's the only other option. That then begs the question; should I catch all exceptions at the top level and prevent crashing, or should I crash and allow whatever system I'm running under to restart me? That depends on the project, but typically I'd let it go. systemd should bring me back, docker should restart me, kubernetes should restart my pod, etc etc. If I focus on what is within my control, and delegate everything else, the system will be cleaner and much more maintainable. I've never come across a situation where exceptions during destruction is a problem, but am very interested in any examples. C++ standards define that you _can_ throw exceptions, but you shouldn't for the exact reason raised in the article - the process will be terminated as there's nothing else that can be done. If there aren't any destructors containing the throw keyword, it's not likely to throw an exception - OOM or other system exceptions are still possible, but why are you allocating memory in a destructor? destructors just need to release resources and clear down the object, it shouldn't be requesting more resources. Thinking about saving the object state before exit? wrong place to do it.
- leni536 6y ago1. That "C equivalent" for error handling is not an equivalent at all. If one could handle the error in the same function then there is no reason to throw. The "C equivalent" is returning an error code, which has other problems. 2. One can handle errors in initialization without exceptions and half-initialized objects: Make your constructor private and expose a static member function that returns an optional<T> (where T is the given class). 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.
- fluffything 6y ago> 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. In Rust, you cannot really do that (e.g. catch_unwind won't catch exceptions thrown by sub-object destructors). And well, C++ tries to completely ban throwing destructors by default, while Rust tries to support that as much as possible.. with the most common consequence being memory leaks..
- steveklabnik 6y agoI 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?
- fluffything 6y agoIn 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.
- bra-ket 6y agoprevious discussion: https://news.ycombinator.com/item?id=3953434 https://news.ycombinator.com/item?id=3953434
- dang 6y agoA few others listed here: https://news.ycombinator.com/item?id=22937991 https://news.ycombinator.com/item?id=22937991
- bullen 6y agoBy now he probably realizes that the Java version is what he was looking for all along: https://github.com/zeromq/jeromq 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 the VM. public static void main(String[] args) { while(1) { try { // do everything, and always throw unchecked // Exceptions unless you need the programmer to // handle the problem } catch(Custom c) { // here we can react to the exact problem the // parent programmer devised } catch(Exception e1) { // something "normal" happened that would have // generated completely random behavior with // machine code } catch(Error e2) { // something "critical" happened that would have // generated a segfault with machine code!!! } finally { Thread.sleep(10); } } } The above code will never cause you to loose sleep. You can pretend there is a way to do this in any other programming language, but only C# would be able to and it is lackluster in so many other domains. C will not help you with exceptions or concurrency, it will punish you into believing Rust is the solution. C++ Exceptions are complete garbage, the only good part of C++ are std API (not implementation apparently) and Objects for structure, not for data (cache-misses). Java does not crash and has the same performance as machine code, that is why it is the leading server language in the world. Goodbye karma! Xo Edit: that was fast! Edit2: Please comment if you downvote.
- Bjartr 6y ago> 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 #
- deepsun 6y agoFrom Java pro view: In Java it's a sin to do anything complex in constructors. You only assign fields and maybe, maybe, call some pure static function to assign a computed field. That mainly comes from unit-testing perspective -- so you can mock the parameters for a unit object _before_ the object does anything. If you really need to do something complex to initialize those fields, it's better to extract them to different service class (and call it, ahem, Factory). So the code looks more like his C example, but wrapped in classes.
- malkia 6y agoWriting in C++ means that the C++ library you are using becomes a dependency. Probably not an issue, if you have the source code to all libraries that you use, but maybe an issue if you are vendor and try to support not only all possible modern compilers, but what they target to (platforms, runtimes, models, etc.) With just "C", not "C" only at the interface part, you can avoid that. Even to the point that your binary artifact (library) can be reused safely between DEBUG and RELEASE. Alternatively you need to ship this as shared statically linked to CRT library (which lots of vendors do), but it's general pain in the ass... for all platforms. So that is the hidden cost of C++, even if your interface is still "C".
- malkia 6y agoNot sure why this got downvoted? Anyone to comment? I use C++ daily on my job, and I'm very happy with its developments - especially C++11, C++14, C++17 and what's coming in C++20 - but statically linking problems are real, and pain to solve (long term). We do mostly MSVC, where some teams are still in VS2017, others VS2019 - and while they are compatible - the compatibility works only if your final app is compiled with the latest compiler from each version, which puts our team (doing mostly libraries, and providing precompiled ones) that we should use the lowest version, but then our CI infrastructure might use even earlier. With "C" there is less to care there, as most is set in stone (unless you use some really compiler vendor specific extensions, that appear during link time). Just an example - https://devblogs.microsoft.com/cppblog/making-cpp-exception-handling-smaller-x64/ https://devblogs.microsoft.com/cppblog/making-cpp-exception-... - this is great improvement, but also means that if you've compiled your lib with VS2019, and it throws an exception under application that uses VS017 - it'll not work.
- kllrnohj 6y agoThat exact same problem exists in C in the exact same way. It's somewhat less of an issue just because C is essentially frozen in time, but it has the same inherent problems & design issues. But if you statically link your C++ runtime & only expose C interfaces then you're fine. You can be "just like C" only at the ABI boundary, that's well supported and works great. It's a quite common setup even. Like for your particular example I don't think the solution to "in some situations I can't throw exceptions across an ABI boundary" is "switch to a language where exceptions don't exist at all and error handling is 'good luck'". It'd be nice if there was a way to flag an exported symbol as being ABI sensitive & letting the compiler patch up differences, that'd be a nice feature. Nuking it entirely seems like the opposite of a solution though? But you're still free to do that in C++ if you want. noexcept all your exported symbols & use error returns, just like C. Or even just compile with exceptions disabled entirely.
- deleted 6y ago[deleted]
- dang 6y agoA thread from 2018: https://news.ycombinator.com/item?id=18668669 https://news.ycombinator.com/item?id=18668669 2015: https://news.ycombinator.com/item?id=9577988 https://news.ycombinator.com/item?id=9577988 2013: https://news.ycombinator.com/item?id=6220049 https://news.ycombinator.com/item?id=6220049 2012: https://news.ycombinator.com/item?id=4455225 https://news.ycombinator.com/item?id=4455225 https://news.ycombinator.com/item?id=3953434 https://news.ycombinator.com/item?id=3953434
- arendtio 6y agoSomehow it reads like a love letter to Go, but I bet he wouldn't like the Garbage Collector ;-)
- netdur 6y agoThe only thing I dislike about C++ is that I can't declare private fields privately, I can use private: but it is there in header... in C I can just declare struct Foo; in header then implement it without expose its gusts.
- gpderetta 6y agoWhat do you mean? You can do exactly the same thing in C++. You can't instantiate a value of that type [1] as it requires knowing the size, but, via base classes and pointers you can still call functions on it. [1] but see the letter/envelope idiom.
- LeifCarrotson 6y agoIs that a security concern, an obscurity concern, or a readability concern?
- netdur 6y agousability, say I use external library Bar, then I declare private: Bar bar; the end use will have install Bar library as well to use my library despise Bar being embedded in my static library, I dislike that.
- maxlybbert 6y agoAre you familiar with the PIMPL idiom?
- netdur 6y agovery nice, I do that but never header of PIMPL
- maxlybbert 6y agoI wouldn’t use PIMPL unless you get something out of it (and “not letting anybody see the private stuff” counts as getting something). It’s a hassle, but it’s also the standard answer to the problem.
- cryptonector 6y agoI don't get it. Exceptions are bad -> don't use exceptions. Constructors can fail with exceptions -> use a factory pattern and handle errors explicitly. Destructors can't fail: right, so don't, and yes, if you need to do finalization that could fail then that has to be explicit (as in C) and you can handle errors there, and yes, that leaves you with half-destructed objects, but so what. No, I think high-level static data typing is very important. Today Rust would be a better language than C++ for this, but C++ is still better than C.
- Gaelan 6y ago@mods can we change the title to "Why I should have" instead of "why should I have"? The current title reads like a question with the question mark omitted ("Why should I have used C, not C++?") which, at least to me, implies that it's saying C++ was the right decision. The thesis of the post looks like the opposite.
- fxtentacle 6y agoI believe the placement new operator does exactly what is needed here. It'll take a self-allocated block of memory and run the constructor on it, thereby avoiding the risk of system generated exceptions as execution stays purely in user code. Basically, it allows you to use a C++ constructor like a C init function.
- kazinator 6y agoZeroMQ is a behemoth; for something that just handles communication, it compiles to like 660KB of i386 machine code. Is there an OS kernel with networking, virtual memory, USB and a file system hiding in there?
- elcritch 6y agoThis has become one of those “classic” articles. Worth a read if you haven’t. The bits about exceptions reminded me of an article on Nim‘s “goto” exceptions implementation [1]. The implementation appears to be a combo of error codes and goto’s to automatically propagate the exception. It performs well and works on embedded devices, although I’ve only done a smaller project in Nim on an Arduino based board. It’s odd having a serial port print stack traces. Careful a slow serial line can lead to a slow printout though! Nim with the new ARC GC and move semantics seems to make a nice language for embedded development or for creating re-useable libraries. Rust seems promising but so much embedded work is still C/C++ only at some level, while Nim compiles to C/C++ nicely. 1: https://nim-lang.org/araq/gotobased_exceptions.html https://nim-lang.org/araq/gotobased_exceptions.html
- lisper 6y agoOh, the irony of preferring to use C in order to avoid undefined behavior. C is every bit as much a minefield of undefined behavior as C++ is. https://www.i-programmer.info/news/184-cc/11862-c-undefined-behavior-depressing-and-terrifying.html https://www.i-programmer.info/news/184-cc/11862-c-undefined-...
- decker 6y agoThis is a complete straw man argument against C++ based on exceptions vs return codes. C++ can handle C return codes, and the compiler comment needs some data to back it up. I would give this more than a grain of salt if the author had an 'alternatives considered' section or some other legitimate arguments like "I'm OCD and I just can't handle the fact that if I want to alphabetize the methods in the declaration for a base class it's a breaking change in terms of binary compatibility for a shared library."