
Why should I have written ZeroMQ in C, not C++ (2012) - creolabs
http://250bpm.com/blog:4
======
leni536
1\. 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.

~~~
hoistbypetard
> 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).

This comment criticizes an article written in 2012 about a project developed
starting in 2007 for not using a technique that debuted in the 2017 edition of
ISO C++. For projects that could force all consumers to use C++11 or later,
it'd be straightforward to add your own.

The idea of doing so in a cross-compiler way when you need to support the
original ISO standard or C++0x in the late 2000s sounds very problematic.

~~~
ChrisLomont
It's not hard to write an optional<T> in C++ 2003, and likely can be done in
C++ 98.

If you don't like that, return a T*, null if not created correctly.

~~~
hoistbypetard
I didn't mean to suggest the language spec would have made it hard so much as
that I'd have really not wanted to approach it in a way that was going to work
with VC6/2002/2005/gcc 2.95/sunpro/ibm/hp compilers of the era. (I think it
was 2009 before I got to push VC6 off the raft.) ISO support in C++ compilers
was __really bad __for a long time.

Our typical pattern was to return T*. But that fits well with the "I should
have just written it in C" argument IMO.

------
jcelerier
Author 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-p...](http://nibblestew.blogspot.com/2017/01/measuring-execution-
performance-of-c.html)

~~~
usefulcat
> you cannot "forget" to propagate an exception

Not only that, but you don't have to write a ton of boilerplate to manually
propagate errors back up the stack since the compiler will do that for you.
And it will do it in a consistent, well-defined manner.

~~~
wvenable
Furthermore, properly structured C++ applications with RAII use the same path
for normal cleanup for exceptional cleanup meaning that you're always covering
that code. You don't have an entirely separate error handling path that only
gets executed for errors.

------
gumby
This 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.

~~~
oefrha
I haven’t written anything nontrivial in C++ for many years and so far have
only learned and used a couple minor features newer than C++11. Would you be
kind enough to point to some resources on why C++17/20 are “powerful and
expressive systems programming languages that aren’t like the object-oriented
messes of old”?

~~~
gumby
I wrtote a reply to this question in a comment parallel to yours.

In addition:

I don't write many classes in the sense of the Gang of Four book. I do write
classes that are really just ways to speak with the type system, e.g. a
"class" that has one instance variable that is an integer, so takes up only as
much space as a native int, yet perhaps acts a special kind of table index.

Sequence manipulation, auto, and destructuring bind provide clearer ways to
express algorithms without getting hung up in towers of class abstraction (an
evil fetish) yet not lots of low-level manipulation and boilerplate like you'd
see with C.

In c++20 generics and templating are almost unified which makes expression so
much clearer (most of the old templating syntax is unnecessary in most cases).
It lets you manipulate the type system much more clearly. You can do a lot
more at compiler time -- you'll never get the expressive power of the Lisp
macro system, but it's a lot closer with a lot of the old syntactic junk
discarded.

Auto declaration has transformed the use of the type system and genericity.

In terms of resources to learn this stuff: I haven't found any single good
source of info for these revisions.

I once wrote a comment on this but it took a while and of course vanished into
the whirlwind of YC comments. So instead I'll suggest a web search for "new in
c++17" and "new in c++20". I know that's kind of lame, but that's the best
high level answer I can suggest.

I will say www.cppreference.com is excellent, but when it comes to newer
features you often need something a little higher level to really grok what it
says. It might answer your specific question though.

I follow the trip reports from committee meetings and read proposals that
happen to interest me which often give context to a design decision.

I started a new project (blank editor buffer) using C++-17 in 2016. I hadn't
written c++ since around 2000 (though I'd previously been involved in g++
development since around '87.* So I treated it as if it were a brand new
language I'd never seen before. The only book I bought was Stroustroup's "tour
of C++" which introduced c++14, then used searches like the ones above to keep
up with developments in C++.

If you're in the same boat I'd say choose C++20, even though the full language
isn't available yet, which is what I did with C++17. Some features I was able
to bring in from Boost (which is where many new features begin) and some I
just couldn't use until it was time to refactor something anyway. Still, this
really made a difference.

* yes before g++ was released by the FSF -- tiemann and I both worked at MCC at the time and used to have dinner at 3AM to discuss what each of us was working on, which is how we later came to start Cygnus.

~~~
oefrha
Thanks!

------
cryptonector
I 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.

------
sxyuan
It 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.

~~~
rumanator
> Don't use C++ exceptions

That's not what Google's C++ style guide says at all. They only argue that
Google's old C++ code base is not exception-tolerant, thus as they don't want
to waste time and they don't want risk adding bugs by refactoring their legacy
code then they just decided to not use exceptions.

That's it.

~~~
sxyuan
You're right. My point wasn't that one should blindly follow Google's style
guide and avoid using C++ exceptions, only that there is an alternative to the
C/C++ dichotomy that the author presents. It's possible to write millions of
lines of C++ without using exceptions. There's a trade-off, which is described
in more detail in the style guide, and the choice depends on the specifics of
one's project.

------
commandlinefan
I'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.

~~~
Koshkin
C++ is a multi-paradigm language, and programming in it does not force you
into the object orientation. Sure, there is a lot of encapsulation going on in
the standard library, but it does not mean that you have to follow the same
approach in code you write (although I find that it is the ability to specify
destructors that separates C++ from the plain old C, and you cannot do that if
you do not write classes).

------
lisper
Oh, 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-...](https://www.i-programmer.info/news/184-cc/11862-c-undefined-
behavior-depressing-and-terrifying.html)

------
dang
A 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)

------
jdashg
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 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.

~~~
beeforpork
> 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 price of spreading everywhere until you don't understanding what is going
on. Typing rules are insanely complex. Overloaded template error messages and
hilarious amounts of boiler plate member functions just to satisfy the
constructor rules and to catch the best overload for every integer type were
the result.

I went back to C. It's a relief.

~~~
kova12
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.

~~~
TheOtherHobbes
A language with four different pointer metatypes for memory management and
(unrelatedly) four different pointer type meta-casts is not "simple".

The subdialect of features that you have settled on may be simple, but the
language itself is a nightmare of random feature archaeology and creeping
throw-it-against-the-wall-and-see-if-sticks extensions, which somehow still
fails to provide clean simple tools for polymorphism.

------
herrkanin
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?

~~~
WnZ39p0Dgydaz1
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](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
Would you say either is something you would put in a decently scaled
production system?

~~~
Supermancho
Using ZMQ (from C) at high volume, we found it dropped data, so we abandoned
it.

~~~
mycelium
What did you replace it with?

~~~
Supermancho
Direct writes locally to ebs and then stream to S3 and async collation after.

------
fxtentacle
I 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.

------
weyhhweyh
Are 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
Bjarne Strsoustroup's short book "A Tour of Modern c++" fits the bill, and was
written for the same purpose.

~~~
blt
Has it been updated for C++20?

~~~
Zitrax
"It covers C++17 plus a few likely features of C++20.", from
[http://www.stroustrup.com/tour2.html](http://www.stroustrup.com/tour2.html)

------
deepsun
From 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.

------
elcritch
This 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)

------
kazinator
ZeroMQ 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?

------
decker
This 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."

------
hedora
Given 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.

------
laurowyn
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 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.

~~~
andi999
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/](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)

~~~
laurowyn
Interesting corner case that I can't say I've ever seen before.

fstream is a bit of a mess. Even the documentation for close is a bit
contradictory. Its exception safety states that an exception is caught and
rethrown after closing the file if one is thrown by an internal operation.
That to me sounds like the actual resource being managed will be closed
(assuming the fd is valid), but any data may not be flushed out of the write
buffer.

sounds to me like iostream needs some serious attention in future specs, but
that's unlikely to happen unfortunately. Creating new toys > fixing old ones.

------
malkia
Writing 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
Not 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-...](https://devblogs.microsoft.com/cppblog/making-cpp-exception-
handling-smaller-x64/) \- 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
That 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.

~~~
malkia
"But if you statically link your C++ runtime" \- you need to take careful
measures your symbols not to leak as visible. Doable, but takes some time.
Also might be easier with gcc/clang - e.g. -fvisibility=hidden, but nothing
like this in msvc AFAIK.

~~~
jcelerier
Msvc symbols aee hidden by default, but why would symbols be visible be a
problem ?

~~~
malkia
That's not true at all. Sorry they are completely visible, unless all of them
are "static". Most of all it also leaks what imports need to be done - as in I
require this from MSVCP140.dll or MSVCP140_1.dll - like "_CxxFrameHandler4"

Again I'm talking about statically linked libraries, linked to the dynamic CRT
(MSVCRT - e.g. /MD, not /MT)

------
Gaelan
@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.

------
bra-ket
previous discussion:
[https://news.ycombinator.com/item?id=3953434](https://news.ycombinator.com/item?id=3953434)

~~~
dang
A few others listed here:
[https://news.ycombinator.com/item?id=22937991](https://news.ycombinator.com/item?id=22937991)

------
netdur
The 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.

~~~
LeifCarrotson
Is that a security concern, an obscurity concern, or a readability concern?

~~~
netdur
usability, 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.

------
arendtio
Somehow it reads like a love letter to Go, but I bet he wouldn't like the
Garbage Collector ;-)

------
Ididntdothis
When 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.

~~~
anand-bala
All 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)

~~~
Ididntdothis
Zig definitely looks interesting. I may use it in a smaller project to try
out.

------
bullen
By 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.

~~~
kllrnohj
> 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.

I write C++ & Java on a daily basis.

I don't know why I would _ever_ willingly choose Java for a personal project.
It's not fun & it's inflexible. And the C++ side of me cringes with how
inefficient & slow "simple" things are. Especially for anything that doesn't
last long enough for the JIT to come along and make it not horrendous.

If I want to pay for a JVM I'd at least go with Kotlin. But I'd take C# over
Java if I had a choice.

And I don't know why you think C++ means "rewriting the ball bearing"? There's
a pretty decent standard library these days, and no shortage of libraries to
add anything else. Depending on what you're doing there's a richer set of
available libraries for C/C++ than there are for Java even (such as anything
to do with graphics).

> Java [..] has the same performance as machine code

It so incredibly doesn't. Maybe when value types are added then Java can
regain some ground on the performance front, but right now you're absolutely
paying a price for Java. Often a price well worth paying, but still a price.

~~~
winrid
I find Java a lot more fun if you don't use getters/setters for everything.

