
Why I should have written ZeroMQ in C, not C++ (2012) - babawere
http://250bpm.com/blog:4
======
npalli
Previous discussion with 207 comments

[https://news.ycombinator.com/item?id=3953434](https://news.ycombinator.com/item?id=3953434)

Then, the original author wrote a follow-up which had another ton of comments
on his blog and another 162 comments on HN.

[https://news.ycombinator.com/item?id=4455225](https://news.ycombinator.com/item?id=4455225)

I wonder if anything new can be said here.

~~~
babawere
Most importantly can go lang really take the place of C++

~~~
jandrewrogers
No, a garbage-collected language cannot replace C++. Explicit control of
memory and deterministic operation is an important tool for some types of
software e.g. high-performance server software.

~~~
trimbo
Azul has made a JVM with predictable GC.

[http://www.azulsystems.com/technology/java-garbage-
collectio...](http://www.azulsystems.com/technology/java-garbage-collection)

So yes, it is possible to have pauseless GC. That won't fit all problems that
C++ is used for (like embedded), but it certainly hits on the server software
side of things.

~~~
jandrewrogers
I do not see how this solves the server software side of things. Most high
performance server software (1) has precisely one process/thread per core and
(2) takes complete control of its memory management. These behaviors have
substantial performance benefits. A GC thread of any type will bleed
performance, and that is ignoring the loss of performance from poor memory and
cache efficiency that is inherent with GCs. High performance server code is
one of those areas that highlights where GCs are inappropriate. I've written
quite a few server engines in GC languages; you expect to see about half the
performance of C/C++ as a rule of thumb.

Most modern server software performance is significantly bound by memory
bandwidth and latency. Introducing inefficiencies in this area tends to result
in material degradation of software throughput. In fact, this is the only
substantial advantage of C/C++; it can be efficient with memory access,
caching, and utilization in a way that no GC language can be, and these are
major contributing factors to performance.

~~~
cbsmith
The notion that poor memory cache efficiency is inherent with GC's is not
terribly well founded. Copying collectors (like the one the HotSpot VM uses
for Eden Space) tend to have great cache efficiency as compared to typical use
of the C heap, where you can't relocate chunks of correlated memory together.
Even mark-and-sweep collectors can do quite well for themselves.

I'd actually say quite the opposite of what you are saying: in a server
environment, you often care more about throughput and mean latency (as opposed
to 99th %-ile latency), and GC can really do great there. What's nice is that
GC allows you to have near zero cost heap allocations and amortizes the cost
of cleanup. What tends to be the problem is a lot of languages with GC make it
difficult to efficiently work with the systems's buffer cache, so you
typically see problems with systems that do disk I/O with large amounts of
data.

I've written a LOT of C/C++ & JVM based servers. Particularly for large
projects with a lot of independent moving parts and object lifecycles that are
not tightly tied to sessions, the JVM based servers are remarkably competitive
if they aren't disk IO bound.

There's a reason why a lot of HFT firms use Java...

C/C++ still kicks butt for tightly written servers with fairly fixed
functions, and generally for anything that does tons of disk IO (though the
Cassandra guys have, as an example, demonstrated one can do quite well if you
take the time to get it right), and of course anything with unsigned
arithmetic (seriously, when are we going to acknowledge that this was a bad
idea?).

~~~
kevinnk
> The notion that poor memory cache efficiency is inherent with GC's is not
> terribly well founded.

It may not be "inherent" to GC, but in practice every GC that I'm aware of has
terrible cache efficiency compared to manual memory management.

> Copying collectors (like the one the HotSpot VM uses for Eden Space) tend to
> have great cache efficiency as compared to typical use of the C heap

False comparison. The real test is against high performance memory techniques
(like memory pools, which are relatively easy and common in practice if you
care at all about cache efficiency) not "typical use of the C heap." When you
actually compare GC vs high performance manual memory management, manual
memory management is significantly faster.

~~~
cbsmith
> False comparison. The real test is against high performance memory
> techniques (like memory pools, which are relatively easy and common in
> practice if you care at all about cache efficiency) not "typical use of the
> C heap." When you actually compare GC vs high performance manual memory
> management, manual memory management is significantly faster.

That's not a false comparison. They represent about equal development effort
(well, the GC version is likely still less effort).

Sure, if you spend a ton of time optimizing your memory usage for your
specific needs you will do better than a general purpose algorithm (though it
is remarkable how often those solutions often implement their own forms of
automatic memory management tuned for their needs). If it turns out that you
couldn't do that, it'd be awfully hard to collect a paycheck (and in fact most
programmers could not collect that paycheck).

Of course, even that effort would not measure up as compared to an omniscient
GC algorithm (and the paycheck for building that would be even bigger ;-).
What is the point?

GC's do well. When you really need to tune memory usage, you get in there and
do the work, and you and do that in GC'd runtimes too. A good memory manager
will, for common workloads, start you off in a better place and make the work
of tuning easier. Certainly there are cases where it won't be so helpful, but
that doesn't mean they are "bad" at memory cache efficiency.

------
tptacek
_Library of data structures and algorithms (STL) is part of the language. With
C I would have to either depend on a 3rd party library or had to write basic
algorithms of my own in 1970 's manner._

Broken record here: there is a middle ground between importing a "3rd party"
library (usually this means GLib) and writing your own Red-black tree: you can
just port a lot of the STL back to C. I did that 10 years ago to get a
workable straight-C RB tree by specializing STLPort's <map> on void-star. It's
been tremendously helpful.

Another idea that more C code should adopt from STL is <vector> \--- making a
thin wrapper around resizable arrays the go-to container, rather than linked
lists which are the de-facto go-to today.

~~~
10098
Actually, I wish C had support for templates. It doesn't seem like templates
go against C's core philosophy, and not having to cast void*'s back and forth
would be nice.

~~~
asveikau
You don't have to cast void * back and forth to anything.

    
    
       void *p = /* ... */;
       char *q = p;
    

Despite what the C++ weenies will say this is valid C! Please do not commit
this cardinal sin:

    
    
       char *p = (char*)malloc(n);
    

No no no! No need to cast that! Please just write:

    
    
       char *p = malloc(n);
    

And if somebody tells you that won't compile without an explicit cast, please
kindly remind them what language you are writing.

[Actually I agree with you that C++ templates are really nice, chiefly for the
reason that they allow you to avoid _function pointers_. In C++ you can pass
your "callback" to a template and it gets inlined right with the body of the
function.]

~~~
peteri
Very true. It's my biggest annoyance with Visual Studio when I write straight
C code that the intellisense error checker moans about this.

The compiler is of course perfectly happy when it's running in C mode. But
then I'm still upset about <stdbool.h> it should get off my damn lawn.

------
bcoates
The conclusion I get from the article, the comments, and the other threads, is
this:

C++ is _not_ a better C.

This was a really common attitude about a decade ago: don't use C for
anything, just point cpp at your C code and use whatever random collection of
C++ features you think make your life easier. We've learned since then that
that's a recipe for disaster, and you should either write a C program in C
(and save yourself the heartache) or embrace the fact that C++ is an entirely
different language.

The author is clearly committed to writing a C program and doing that in any
language other than C is a mistake.

~~~
rdtsc
That certainly been Microsoft's approach to its dev suite. They refused to
support C99 and just tell people to use C++ instead. Had to use it for a
project and had to sift through code and roll back valid C99 code to make it
compatible with their braindead compiler.

C++ has its place and its strong point but it is not a strict superset of C in
terms of features and improvement.

~~~
aa0
Microsoft pretty much let their c compiler rot. It doesn't support dynamic
allocation, ie, int arr[numElts];, it doesn't even support var declaration
anywhere but at the beginning of a function. Making code I've been writing
compatible with msvc has been a pure headache.

~~~
pjmlp
It is a C++ compiler, just use C++ and you will be fine.

~~~
rdtsc
No. I am using C and specifically some C99 features. They claim C++ is just C
and ++ but it isn't.

(There is an Intel compiler that does support C99 btw if anyone is interested
but you have to pay for it).

[http://software.intel.com/en-us/articles/c99-support-in-
inte...](http://software.intel.com/en-us/articles/c99-support-in-intel-c-
compiler)

~~~
pjmlp
> They claim C++ is just C and ++ but it isn't.

Who says this?

Citing Andrew Koenig 's 1989 statement: "As Close as Possible to C, but no
Closer".

Thankfully I would add, as C++ has more strong typing than C.

As for doing pure C99 development on Windows, there are other options as you
point out.

Personally as a software developer I don't have issues to pay for the work of
others.

~~~
rdtsc
Microsoft's mentioned that in their forums in replies to people complaining
about lack of C99 -- "just use C++".

> Thankfully I would add, as C++ has more strong typing than C.

Doesn't matter. My project was large, already written in C99, was not in C++.
I do not like C++, I don't care for learning virtual destructors mixed with
mutliple inheritance, templates stl and friend methods. I am not the only one
on the project. So just "don't use it" is easy to say for one person project,
when multiple people work on it everyone start to use their favorite subset
from C++ huge specification and I don't like that.

> As for doing pure C99 development on Windows, there are other options as you
> point out.

It was annoying but we just rolled back C99 specific features.

~~~
pjmlp
> Microsoft's mentioned that in their forums in replies to people complaining
> about lack of C99 -- "just use C++".

Yes, this is true. For Microsoft C is a legacy language that can well be
replaced by C++ for what they care.

But I think I never saw them stating "... C++ is just C and ++ ...", even of
the official communication done by Herb Sutter.

I am on the opposite field, I only touch C if really obliged to do so, which
does not happen since 2001.

So I am a bit biased regarding Microsoft's decision, but I do understand some
developers would rather stay on C land.

------
munificent
Maybe this makes me an odd C++ user, but in the fifteen years I've been using
it, I've never actually used exceptions. I would solve his "how do you handle
a failed constructor" problem by either:

1\. Try to define classes where construction can't fail and the class is
always in a valid state. This works most of the time. Failing constructors are
pretty rare in my experience.

2\. If it can fail, limit it to classes that can only be constructed on the
heap. Encapsulate the failure code in a static method that wraps the
constructor which can itself never fail. In other words:

    
    
        class Foo {
        public:
          // Creates a new Foo or returns NULL on failure.
          Foo* create() {
            Bar* bar = doThingWhichMayFailAndReturnNull();
            if (!bar) return NULL;
    
            return new Foo(bar);
          }
    
        private:
          Foo(Bar* bar) : bar(bar) {} 
          Bar* bar;
        };
    

This way it is impossible to get a Foo that's in an invalid state, but no
exception-handling is required. The caller does have to check for NULL, of
course.

3\. If I do want to have a class that can be stack-allocated (usually so I can
use RAII) and can possibly fail, define an explicit invalid state for the
class and check that. Like:

    
    
        class Connection {
        public:
          Connection() {
            connected = openConnection();
          }
    
          ~Connection() {
            if (connected) closeConnection();
          }
    
          // Outside code is responsible for checking this.
          bool isConnected() const { return connected; }
    
        private:
          bool connected; // True if in valid state.
        };
    

I think I generally have the same philosophy as the author. I don't like
exceptions (in C++, I love them in other languages), and I _really_ don't like
broken-state objects. But that doesn't seem insurmountable to me.

~~~
asher
When I worked at Yahoo, the policy was to not use C++ exceptions. We used
integer return codes. We mostly followed your #1, in which constructors do no
actual work.

The Google coding guide, at least a few years ago, prohibited throwing C++
exceptions.

So you are hardly alone in avoiding them.

------
dragontamer
Lets start with the problem: he doesn't want to use exceptions (fine, I can
agree with that), but also wants a "constructor" to return an error value if
necessary. So whats wrong with the obvious solution?

Create a private constructor with a friend function: a "static" factory
method. Bam, no more "semi-initialized" crap that you have to deal with.

~~~
lcampbell
To extend that, I don't understand his issue with discarding destructor errors
-- most errors I can think of during resource release are mundane (e.g.,
close(2) returning ECONNRESET) and can safely be ignored. Why not just have
`int term();`, which returns the mundane errors, be optional and automatically
called from the destructor?

Are there any non-mundane error states that would occur during a stack unwind
that you can't safely ignore?

~~~
dragontamer
Yes actually: proper destruction and cleanup of a mutex must happen, or else a
deadlock will likely occur soon.

But proper cleanup of non-mundane issues is a well known problem in all
programming languages, C included. If you don't handle the mutex correctly
100% of the time, issues come abound. At least in C++, there is a methodology
/ philosophy (RAII) that handles most situations.

------
commentzorro
Clearly the author spends no time at HN. If he'd have programmed ZeroMQ in Go
then all his exception handling would have been handled concisely and
beautifully, just as desired. Better though, if he'd programmed it in Haskell
there simply wouldn't be any exceptions because there couldn't be any errors.
[Shout-out to Mindy Hiller. You go girl!]

~~~
redsymbol
Go's 1.0 release didn't happen until 2012. He started working on ZeroMQ in
2007.

Not sure what "spends no time at HN" means. Martin is an unusually excellent
software engineer by almost any measure, as evidenced by his years of
impactful open-source development - 0MQ being just one example.

And Haskell wouldn't make sense for a project like this, only because of some
of his goals with the project - which include: to eventually have it
integrated in the Linux kernel; to make it easily usable by code in any
language; to have it easily and universally cross-platform; etc.

(Dang... did I just feed a troll?)

~~~
detrino
I wasn't sure until "Mindy Hiller".

------
mikepurvis
Avoiding exceptions I can totally understand—that guideline is broadly
understood, and right there in the Google C++ style guide.

I'm struggling to understand the business about constructors, though.
Shouldn't a constructor be mostly just providing a sane initial state? What's
it doing which has the possibility of needing to throw an exception? I think
there's a reasonable middle-ground here, where you put the no-fail stuff in
the language-provided constructor, and then have separate initialization
functions (with return codes) which take care of the more brittle bits as
necessary.

What's an example of an object which requires complex initialization? (I'm
assuming that anything IO-related, like a socket, will for testing reasons
have been passed in to the constructor rather than created by it.)

------
the_mitsuhiko
C++ is only as horrible as you want it to be. I am currently using C++
internally to write a library and have chosen very carefully which subset to
use. It's fun.

\- external API is C only

\- no exceptions

\- some macro magic to allow customization of allocators

\- a proxy allocator for the STL containers to use my custom allocators.

\- only trivial constructors

It's a pleasure to work with. True, the allocators in C++ suck, but things
could be worse and I can still use most of the STL without much troubles.

~~~
apaprocki
Based on your allocator woes, it sounds like you might like bsl, with
polymorphic allocators: [https://github.com/bloomberg/bsl/wiki#the-bde-
standard-libra...](https://github.com/bloomberg/bsl/wiki#the-bde-standard-
library)

------
10098
He is comparing _checking_ the error status in C to _notifying about an error_
in C++. Think about it. Those are different things. You can think about
throwing exceptions as returning an error status, except the code that handles
the error in the caller function is isolated into the catch block.

~~~
rumcajz
Unless there's no catch block in the caller function. Then it starts to get
messy.

~~~
10098
To be fair, things may also start to get messy if you forget to check the
return value of a function.

~~~
rumcajz
Ack. Bur that's something you are not supposed to do in C. Not handling an
exception within the function, on the other hand, is perfectly normal, if not
encouraged, in C++.

~~~
10098
I wouldn't say that it's encouraged, but it indeed is normal. However, one
should only do it in the case when the caller function actually can't do
anything to handle the error. In this case, the idea is to pass the exception
further up to the caller's caller, etc. The bad part is, you have to be
mindful of it when writing code, and add exception handling code in callers as
necessary, however, I would argue that writing error-handling without
exceptions also requires the same level of attention.

I'm not saying exceptions don't have their gotchas ([1]), however, with all
due respect, I don't think that your article does a good job at convincing the
reader that using exceptions is a poor choice.

[1]
[http://ptgmedia.pearsoncmg.com/images/020163371x/supplements...](http://ptgmedia.pearsoncmg.com/images/020163371x/supplements/exception_handling_article.html)

------
mmariani
Previous discussion:
[https://news.ycombinator.com/item?id=3953434](https://news.ycombinator.com/item?id=3953434)

~~~
babawere
What makes relevant today is that "go" is steadily becoming a replacement for
"C++"

~~~
w3pm
Except that go is a garbage collected language and as such will never be a
replacement for C++. I could however see C++ dying, leaving C and go behind.

~~~
popee
If rust could do that (someone hacked and remove GC), I'm sure go authors will
figure something out. Well, guess it all depends on interest on that topic.

------
voyou
Better title: "Why I should have written ZeroMQ in C++, not C++ without
exceptions."

~~~
astrodust
Writing C++ properly requires an entirely different mind-set to writing in
"fancy C" using a C++ compiler. Bitching about exceptions is a sign you don't
know what you're talking about, or your expectations are culturally
incompatible with the language you're using. When you call a method, you must
be aware of what exceptions it could throw and handle those you're interested
in from. Ignore this at your own peril.

Return codes in C are easily ignored even if they're serious. In C++ you are
compelled to deal with error conditions even if your action is to ignore them.
This is a fundamental philosophical difference.

Better title: "Why I should've learned C++ thoroughly before writing ZeroMQ".

~~~
w3pm
What about the author's complaints about exponentially increasing code
complexity when introducing new features or new exception types? I feel like
the author made an attempt to write c++ the "correct" way and found it
unsuitable for the software's goals (zero undefined behavior).

~~~
astrodust
There's been several extended discussions on this, some involving the author,
and in virtually every case he's been berating the language for doing things a
particular way but often because it's just one way of many and he's ignorant
of the other ways of doing it.

There was some bitching about allocators, for example, as if he didn't know
about the C++ allocator override feature which isn't even hard to implement.
Then there was more confusion about template objects for things like `vector`
where he was using them as you might a C linked list library, then complaining
that you had to allocate twice as many objects.

I think the author is dimly aware of what C++ _really_ is, and just refuses to
play along because they'd rather be writing C code anyway.

John Carmack could probably tear apart every single one of those complaints in
ten minutes and have time left over to talk about his new CTO position. That's
because Carmack spends the time to learn his tools inside and out and doesn't
simply bitch about things being not to his liking.

------
jbrechtel
Why are so many people wary of third party libraries? I can understand some
random, unmaintained gem on RubyGems but in this case we're talking GLib,
right?

I guess I mean, shouldn't the stigma be attached to unmaintained or immature
libraries not simply third party ones?

~~~
wolfgke
GLib is difficult to get to compile on Windows. On
[https://developer.gnome.org/glib/stable/glib-
building.html](https://developer.gnome.org/glib/stable/glib-building.html)
there isn't even a guide for this.

------
lazyjones
Based on this critique, the author would probably have found Go even more
useful than C. He would not only have avoided the OO and exceptions mess he
found problematic in C++, but also the tedious manual memory management in
C/C++.

~~~
babawere
The Author admitted that .... He Said "Practically an advertisement for Go"
[https://groups.google.com/d/msg/golang-
nuts/QWy3YJvcUk0/E22P...](https://groups.google.com/d/msg/golang-
nuts/QWy3YJvcUk0/E22PU93EdlMJ)

~~~
cpeterso
Can Go generate .so libraries that can be called from (and call back to) C/C++
code?

~~~
babawere
Not sure but there seems to be shared library support for 6l/5l
[https://code.google.com/p/go/source/detail?r=1eadf11dd1b7b19...](https://code.google.com/p/go/source/detail?r=1eadf11dd1b7b19d4857681363553c2cfd2ad47d)

------
themckman
For those interested, the author is working on a new library along the lines
of ZeroMQ with a ZeroMQ compatibility layer called nanomsg[0].

[0]: [http://nanomsg.org](http://nanomsg.org)

~~~
erickt
Actually, the ZeroMQ compatibility layer was recently cut from nanomsg:

[http://www.freelists.org/post/nanomsg/0MQ-compatibility-
libr...](http://www.freelists.org/post/nanomsg/0MQ-compatibility-library-
removed)

------
deathanatos
Don't fight the language (and this goes for any language). If you fight the
language (go against the idioms of the language), you're going to end up
unhappy with it. In my opinion, he's fighting the language. Additionally,
while he rants at C++ for some of its behavior, he doesn't really address what
he'd do in C. Also, he seems to not understand what "undefined behavior" is.
Last, RAII is your friend in C++, and IMHO, is the difference between
programming in "C with some extra stuff, but mostly more verbose" and C++.

Exceptions:

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

The example is basically that when you call a function, it could raise any
error, so we don't know which to catch. This problem exists in most languages
where errors can occur (I've had to deal with this a lot in Python). So, C.
How do you translate the errors across function calls? are you, or are your
functions just returning 0/1? (at which point, you've lost much of the
richness of exceptions, I think) Instead of handling errors now at the source
and handler, you'll also need to handle them at every intermediate point (if
(f()) { free things; return ???; }), and this burden makes exceptions
wonderful.

> Consider what happens when initialisation of an object can fail.
> Constructors have no return values, so failure can be reported only by
> throwing an exception.

Raise an exception, except… (no pun intended)

> However, I've decided not to use exceptions.

(You're fighting the language.) Is this just a complaint that maintaining
exception safety in ctors is difficult? unique_ptr and shared_ptr (and RAII)
make it fairly straight-forward and easy, and often a ctor can be completely
exception safe without try/catch. (If releasing the partially constructed
state is the issue, it's hard to tell.)

> if termination can fail, you need two separate functions to handle it

Yes, you will. Honestly, this never sat well with me, but I think it makes
good sense. But his example seems to imply you can fold them into one function
in C, which you simply can't (under the same assumptions that the C++ code was
held to). If "termination" can fail, you'll still need thing_terminate() and
thing_deallocate(), or you'll never be able to deallocate the thing; or you're
assuming that the C thing_terminate() will just report the error and
deallocate anyways. The larger C++ problem remains in C, and the author
doesn't touch on it: if you're (manually, in C) unwinding the stack due to
error, and you need to release a thing in the process, and that release fails:
what do you do? C++ gives you two options, in my opinion: either you ignore
it/log it, by not throwing in a dtor, or (and people don't like this one) you
throw in the dtor and risk there being two exceptions. C++'s response will be
to terminate the program. In C — or any language — what would you do with two
errors?

~~~
asveikau
> So, C. How do you translate the errors across function calls? are you, or
> are your functions just returning 0/1? (at which point, you've lost much of
> the richness of exceptions, I think)

If you think your options are only "returning 0/1", you're not being creative
enough.

For example COM has a 32-bit integer called HRESULT that all methods return.
The high bit says something is an error. A few bits in the high 16 bits
indicate where an error is from. The low 16-bits are private to whatever space
is specified in the aforementioned bits. So if you take an error like
0x80070002 you can say it's an error, from the Win32 subsystem, and the error
code is 0002 or file not found in this specific instance. Another component
can set the high bits differently and end up with their own 16-bit error
codes. I find when I write COM code the errors are much more meaningful than
in a high-level language because it's all very explicit, you're constantly
thinking about it at every function call, whereas in exception-based systems
it's all just very haphazard and hand-wavy.

Or you could look at something like glib. Each method might have an out
parameter which is a gerror object. This includes an error code and a string.

In other words there are several conventions out there. If you pick one and
stick with it across a code base you can be very productive with a minimal
amount of nonproductive "philosophical questions" like the ones in your post.
Even if you have several libraries which have different conventions, you can
adapt _their_ conventions to the conventions of _your_ code base and it's
typically not a big deal.

> Instead of handling errors now at the source and handler, you'll also need
> to handle them at every intermediate point (if (f()) { free things; return
> ???; }),

Actually this "early return" pattern is quite bad in plain C, because if you
have N allocations for example, each "return" statement needs to free between
0 and N buffers before leaving the function. That gets very maddening. One way
to avoid this is to have only one "return" statement at the end of your
function and free any loose buffers if-and-only-if they are non-null. This
involves either constantly re-checking some error status, or using a goto to
jump to the end of your function when something fails. IMO the 2nd is much
cleaner. (Let that wrap around your high-level-language brain... but also know
that any large C only code base is doing the same thing if they are sane; grep
the Linux kernel for "goto" and you might start to get a picture... and while
you're bashing C error handling and probably thinking ill for me for pulling
out "goto", please keep in mind your "exception" concept is really just a goto
that crosses stack frames...) In the end this pretty well simulates what you'd
do in destructors in a C++ code base following the RAII pattern.

> and this burden makes exceptions wonderful.

In my experience it, on the contrary the exception concept just makes sure you
have no idea that stuff can fail, and every small failure in a "shockingly"
unexpected place blows up your entire program and terminates the process. Or
you have Java's goofy "checked exceptions" concept and so lazy programmers
will introduce do-nothing catch blocks to shut up the compiler.

~~~
lmm
>For example COM has a 32-bit integer called HRESULT that all methods return.
The high bit says something is an error. A few bits in the high 16 bits
indicate where an error is from. The low 16-bits are private to whatever space
is specified in the aforementioned bits. So if you take an error like
0x80070002 you can say it's an error, from the Win32 subsystem, and the error
code is 0002 or file not found in this specific instance. Another component
can set the high bits differently and end up with their own 16-bit error
codes. I find when I write COM code the errors are much more meaningful than
in a high-level language because it's all very explicit, you're constantly
thinking about it at every function call, whereas in exception-based systems
it's all just very haphazard and hand-wavy.

If you want to have an exception that just contains an error code you can do
that (e.g. java's SQLException works that way). Personally I find it much more
useful to be able to give errors textual name, and declare a hierarchy
relationship between errors.

> Actually this "early return" pattern is quite bad in plain C, because if you
> have N allocations for example, each "return" statement needs to free
> between 0 and N buffers before leaving the function. That gets very
> maddening. One way to avoid this is to have only one "return" statement at
> the end of your function and free any loose buffers if-and-only-if they are
> non-null. This involves either constantly re-checking some error status, or
> using a goto to jump to the end of your function when something fails. IMO
> the 2nd is much cleaner. (Let that wrap around your high-level-language
> brain... but also know that any large C only code base is doing the same
> thing if they are sane; grep the Linux kernel for "goto" and you might start
> to get a picture... and while you're bashing C error handling and probably
> thinking ill for me for pulling out "goto", please keep in mind your
> "exception" concept is really just a goto that crosses stack frames...) In
> the end this pretty well simulates what you'd do in destructors in a C++
> code base following the RAII pattern.

Sure - exceptions exist to replace something you would otherwise have to use
goto to do, that's pretty much the entire point. But they're better than goto
for the exact same reason that a while loop is better than implementing a loop
with goto.

> In my experience it, on the contrary the exception concept just makes sure
> you have no idea that stuff can fail, and every small failure in a
> "shockingly" unexpected place blows up your entire program and terminates
> the process. Or you have Java's goofy "checked exceptions" concept and so
> lazy programmers will introduce do-nothing catch blocks to shut up the
> compiler.

You have the exact same problem in C. Either programmers have no idea that
library functions can fail and never check return codes, or you have something
like gcc's warn_unused_result and lazy programmers introduce do-nothing error-
code checks.

~~~
asveikau
> You have the exact same problem in C. Either programmers have no idea that
> library functions can fail and never check return codes, or you have
> something like gcc's warn_unused_result and lazy programmers introduce do-
> nothing error-code checks

You forgot the other option. The careful programmer knows they have to check
return codes and is suspicious when it's not happening. With exceptions you
have no idea when they can happen because it's not part of the function
signature. Or in the case of Java, handling errors is like homework, a
constant struggle to keep the nagging compiler happy, instead of a consistent
style that you apply throughout a code base.

------
Kiro
I don't understand the error handling example. Can't you just do the same
thing in C++?

~~~
TwoBit
Exactly. The author thought that using C++ means you are forced to use
exception handling, despite the fact that most C++ engineers don't use it. And
he had this dumb idea about doing non-trivial things in constructors, another
thing most experienced C++ engineers don't do. I don't care what experience he
claims to have; he is a noob.

------
X-Istence
[2012]

