Hacker News new | comments | show | ask | jobs | submit login
Why should I have written ZeroMQ in C, not C++ (250bpm.com)
375 points by rumcajz 1605 days ago | hide | past | web | 202 comments | favorite



This is absurd. Exceptions are just generalised returns. You can use them well and benefit from them, or use them poorly and make the stuff of nightmares.

It’s the same with any feature of any language. You can choose not to use exceptions in your code, if that makes it easier for you to understand and manage, but it’s certainly not the fault of the feature in general.

Basically, it’s up to you to structure your code well. Yes, it gets bad if a potentially throwing function can be called from multiple points. The error needs to be handled at every one of those points, and probably in much the same way.

But if you see repetition, why aren’t you factoring it? And why are you calling that function from multiple unrelated locations in the first place?

I get it. I’ve used C++ for half a lifetime—it’s a hopelessly broken language. There are tons of things about it that just make me furious. But I can get stuff done in it because I’ve taken the time to understand it in depth. Don’t complain about the language or a particular feature thereof just because it’s not suited to your problem or your way of thinking.

There are other languages out there (Erlang) that are far better suited to stable zero-downtime systems than C++. There are languages (Haskell, Lisp) that help you better manage complexity than C++. Every language implementation worth its salt has C bindings, so you can write performance-critical code at a low level, while still managing the whole system in a high-level language. There are options. Explore them!


Bingo. When I read this:

"C++ exceptions just didn't fill the bill. They are great for guaranteeing that program doesn't fail — just wrap the main function in try/catch block and you can handle all the errors in a single place."

That's not C++'s fault, or Exceptions' fault in general: that's your fault for using an extremely useful and versatile language construct completely incorrectly.

If a high-level language is bad because it offers you more functionality and features than you know how to use, then sure, maybe you shouldn't be using a high-level language. But this argument is almost like decrying being rich because you don't know how to use your money wisely; trust me, it's still better to have money, and you shouldn't throw away your high-level life just because it's "simpler to be poor."

If your goal is to do high-level complex things, it's better to use a high-level language. If your goal is to do low-level pedal-to-the-metal things, then it's better to use a low-level language. But the simple fact that a high-level language is high-level is not a valid criticism. Not of the language, anyway.


>just wrap the main function in try/catch block and you can handle all the errors in a single place.

Yeah, this kind of statement always scares me. In my experience people proposing it tend to 'handle it' by simply ignoring it happened. As if by simply catching and not crashing you have averted disaster. Nevermind that very little, if any, code is truly exception safe. That exception just tore through N frames of code interrupting each one at who knows what step and who knows what kind of state the object is now in. If you were lucky they were at least using RAII objects to ensure they haven't leaked things, but that is only half the battle. Unless they are using some kind of transactional approach to mutating their own instance state you likely have an object in a 'half-transitioned' state, which will likley violate various invariants the original author assumed (incorrectly) would always hold. Continuing in C++ (or really any language) after swallowing an exception 'around main' is a supremely bad idea, unless you like heisenbugs or getting pwned.


I agree with your entire post. This part made me laugh though:

>you shouldn't throw away your high-level life just because it's "simpler to be poor."

It made me think of people that have won the lottery and after they've finally blown through all their winnings many concur that: it's "simpler to be poor."


I agree the whole rant about exceptions just seems wrong -- if you've got a state that you can handle right there in the function itself then it's not an exception. You just deal with it. The C code and the C++ code would be equivalent.

This feels more like a "Oh I have exceptions, I better use them" situation. And his solution is to pick a language without exceptions so he doesn't have to use them. It's all very odd.


Or in Common Lisp you would be able to "signal" a condition and then handle the situation. Signal will just do nothing unless some higher level code has an explicit handler for it [1].

[1] I remember reading a MIME encoding library years ago. There was a point where the code would detect an error but if the error was at the end it would just drop that part and write in the last couple of bytes. In this situation, most clients won't care that this is happening but what about those that do? Since this code signalled error by returning NULL there was no way to signal "saw an error, but fixed it". In Common Lisp, signal could have been used for clients that would want to report on this.


One of his first examples is additionally absurd and is either the flimsiest of strawmen or he simply doesn't understand when exceptions should be used:

    try {
        ...
        int rc = fx ();
        if (rc != 0)
            throw std::exception ("Error!");
        ...
    catch (std::exception &e) {
        handle_exception ();
    }
Why is he throwing exceptions and catching them within the same function? His "C equivalent" is what he should've been doing in C++.


He states how he understood exceptions:

"C++ exceptions just didn't fill the bill. They are great for guaranteeing that program doesn't fail — just wrap the main function in try/catch block and you can handle all the errors in a single place."

This is something i learned in my very first computer science lecture not to do.

To be fair: It's what exceptions can be good for - a last barrier before a crash and a way to handle errors later. But you're absolutely right that he simply could react directly to the error. Or throw specific exceptions and react to them. His described issue has nothing to do with exceptions themselves, just with the way he thinks he has to use them.


Exceptions indicate //exceptional conditions//, i.e. conditions other than the intended state transitions of the program. They aren't a guard against crashing, they are a mechanism for formalizing error states in the program.

Memory corruption? Appropriate response: fail. Logic error? Appropriate response: fail. Exception? Appropriate response? Decide if program can continue, respond.

I'd say the bigger issue is less of "exceptions" so much as "states". Adding any kind of error handling increases the number of state conditions that your program may be in, and that complexity is obscenely hard to manage. I don't think C or C++ or Java or Erlang or any other language will free you from the burden of proper software engineering.


Though we probably learned this is not the way to use them, I have seen numerous real-world examples where this was the case.

Not only that, I don't think C++ exceptions are necessarily very good at being a 'last barrier' before a crash - they simply can't catch all the errors, because they are good at indicating conditions that might be recoverable, not catching and recovering from logic errors that would cause a crash. There are plenty of ways to hang yourself in C++ without ever throwing an exception.


"Though we probably learned this is not the way to use them, I have seen numerous real-world examples where this was the case."

Ah, Java code at my workplace.

   catch(Exception e) {}


I believe you mean catch(Throwable t) {}


>He states how he understood exceptions: "C++ exceptions just didn't fill the bill. They are great for guaranteeing that program doesn't fail — just wrap the main function in try/catch block and you can handle all the errors in a single place." This is something i learned in my very first computer science lecture not to do.

Yes, and he learned not to do it also, thank you.

He makes a tongue in cheek comment about a common Exceptions abuse. Actually, in the very next sentence after than one, he explains why that is bad.

Do people on Hacker News believe that the guy doesn't know C++? He is the frigging author of one of the most useful, used, high quality C++ libs.


>Actually, in the very next sentence after than one, he explains why that is bad.

No, he doesn't. The very next sentence is:

"However, what's great for avoiding straightforward failures becomes a nightmare when your goal is to guarantee that no undefined behaviour happens."


And that's it, there's nothing more to it.

It's great if your goal is to avoid straightforward failure (= crash), it's bad if your goal is to also avoid undefined behavior (= invalid program state).

Have you discovered something else on the matter?


Of course there is. Have you actually read that discussion here? If you don't use exceptions in the way he described them, you don't have undefined behaviour (general big exception handling at the end vs granular error-checking, which is of course also possible with exceptions). Everything he described has nothing to do with exceptions, but with the way he uses them in the examples he showed.


>Of course there is. Have you actually read that discussion here?

Yes, and as a Comp Sci, and developer of 15 years, I knew it all before. It's not like the discussion got into any advanced territory.

>If you don't use exceptions in the way he described them, you don't have undefined behaviour (general big exception handling at the end vs granular error-checking, which is of course also possible with exceptions). Everything he described has nothing to do with exceptions, but with the way he uses them in the examples he showed.

Which is the ways that are relevant to his project and coding style. People on the HN thread were just quick to second-guess him without understanding the problem domain and his constraints (which were: "I want to use exceptions in X way for them to be worth to me. I won't even consider the Y way --what some in the HN discussion proposed-- because it's convoluted, requires far more code and maintenance and if I have to do that, I might as well do it in C".

For example, you cannot wrap each and every spot with a try/catch because this doesn't buy you anything over the C way of error checking.

Also, you cannot know what exactly occurred (at coding time, not runtime by checking the stack), unless you granulize (granuralize?) exceptions to death. But then, why not just check the error conditions/codes? And if you do so, what you get over C?

etc...


I agree, it was disconcerting to me as well. I read again and now I think that he is putting the throw there to exemplify that fx() can throw an exception.


An ellipsis means something unimportant has been elided. For example,

  try {
      foo();
  catch (std::exception &e) {
      handle_exception ();
  }

  int foo () {
      int rc = fx ();
      if (rc != 0)
      throw std::exception ("Error!");
  }


The combination of manual memory management and exceptions is particularly nasty.

When you can't count on a GC to clean up for you every function has to be carefully coded to prevent potential leaks. Browse through some of Herb Sutter's GOTW questions to get an idea of exactly how tricky this can be.


Idiomatic C++ does not rely on manual memory management—it relies on deterministic memory management. Use unique_ptr, or shared_ptr when it makes sense (hint: it rarely does).


In theory you have every single leakable resource wrapped up in a RAII container or a smart pointer. In reality you're almost always dealing with raw pointers coming from a lower level C API or old C++ code or some kind of object handle you haven't wrapped yet.

Of course the recommendation from Stroustrup & Sutter is to use the new smart pointers for everything but I think it will be a few years at least before most people can follow that advice.


Typically what I do in that situation is lightly wrap the API. For instance:

    Image* image = allocate_image(…);
    …
    deallocate_image(image);

    ↓

    struct ImageDeleter {
        void operator()(void* image) const {
            deallocate_image(image);
        }
    };

    unique_ptr<Image, ImageDeleter> image(allocate_image(…));
That certainly can’t work everywhere, but I have yet to be failed by it.


You can reduce verbosity of the code by passing destructor function directly to unique_ptr constructor:

    std::unique_ptr<Image, void (*)(Image *)>(image, deallocate_image);


Cool, thanks for the tip. I meant to offer an example for the general case.


People who don't do this are just asking for it.


That's a lot of boilerplate code. Okay there are macros to wrap such things, but I guess it's things like this that would make the future reader of the code puzzled.


jmq & basman provide examples of how to do it succinctly.


Or, perhaps easier:

scoped_ptr<Image> image(allocate_image(...));


Sure. And using deep-thrown (i.e. thrown across module/library boundaries -- often exceptions are local tricks used within tightly coupled code) exceptions in an application where you're dealing with such pointers would be bad design.


Agreed. His examples are nonsensical, in almost every case comparing handling an error in C with signalling an error in C++. To see what I'm talking about, here's his first comparison:

    // C
    int rc = fx ();
    if (rc != 0)
        handle_error ();

    // C++
    int rc = fx ();
    if (rc != 0)
        throw std::exception ();
This is absurd. This is not the same thing in two different languages. These two pieces of code do two different things. One handles the error, and the other signals that there's an error that should be handled elsewhere. For an apples-to-apples comparison, let's consider both cases in each language. If this is the appropriate place to handle the error, then you handle it. It looks like this:

    // C
    int rc = fx ();
    if (rc != 0)
        handle_error ();

    // C++
    int rc = fx ();
    if (rc != 0)
        handle_error ();
I.e., exactly the same. If you DON'T want to handle the error here, then you return an error code or throw an exception:

    // C
    int rc = fx ();
    if (rc != 0)
        // There's no way to know where or whether this will be handled
        return some_error_code;


    // C++
    int rc = fx ();
    if (rc != 0)
        // There's no way to know where or whether this will be handled
        throw some_exception();
You're in the same boat in C and in C++. The error must be handled elsewhere, and nothing you do here can ensure that it will be. Exceptions have some advantages, though:

1. You do not have to anticipate inconvenient collisions between error return values and valid return values.

2. If the calling function is not the appropriate place to handle the error, then no boilerplate is required in the calling function to pass the error up the stack.

3. Exceptions let you use destructors instead of explicit cleanup blocks.

4. Failure to write error-handling code will not result in an operation proceeding in an invalid state, deceptively reporting success, or silently corrupting state. An error return value can simply be ignored. An unhandled exception will cause a cascading failure that cannot be mistaken for successful completion of an operation.

5. You can always use return values to report errors if they happen to be preferable in some cases.

P.S. The more I understand his complaints, the more I think he is just trying to get too fancy. If construction and destruction are problematic for some objects, then classes with trivial constructors and destructors give him exactly the struct-like behavior he wants, so what is wrong with that? How in the world do you get into worries about exceptions thrown from constructors and half-initialized objects (exception in the sense that any allocated-but-not-initialized struct is in a "half-initialized" state)? This is like saying knives are too dangerous to have the in the kitchen because you are in the habit of thrusting your hand blindly into the knife drawer.


Superbly put. I cannot upvote this enough.


I second your Superbly put. I cannot upvote this enough too. I've been doing C and C++ for over 17 years from embedded up to CRUD accounting type apps.... Spot on my friend, spot on!


Well put.


Returns are statically typed. Exceptions are not.

In a static language like C++, this is a big disadvantage.


It's not absurd. Just the fact that C++ exceptions were used, would make this code not portable - not being able to run on some very popular devices.


Checked exceptions are great, but unchecked exceptions are a huge disaster only mitigated by failsafe features such as RAII or garbage collection with finalizers. Sometimes like in highly dynamic languages the failsafes are enough that it's ok to just use unchecked exceptions, but in a language like C++ you really can't afford to make mistakes in the first place.

The process of adding an exception in Java:

1. throw TheException

2. compile

3. add "catch(TheException e)" to fix errors or "throws TheException" to signature

4. repeat 2 until no more errors

Compare to C++:

1. throw TheException

2. examine all functions to determine if they might need to handle TheException or consult intuition about program structure

3. repeat 2 until 'done'

So the real problem with exceptions in C++ is the compiler can't tell you where to look next and what you forgot about. Step 2, magically find all the places that should catch the exception, is both time consuming and error prone.

I find it strange that C++ has all kinds of strictness with types (have to cast void*, const virus, etc) but with exceptions they just throw caution to the wind.


Java is the only language that I can think of that gets exceptions right, in that it forces you to acknowledge them every step in the chain and choose whether you're going to catch them or throw them upwards.


Strange. It seems to me that the consensus in the Java community is that checked examples were among the worst misfeatures of the language.

Googling for ---java checked exceptions--- is illuminating.


It is strange that the consensus in the Java community sort of ended up being against checked exceptions. I for one think that this consensus is completely wrong and I suspect that what looks like a consensus is just a bunch of very loud people shouting down everyone else (and to me not making very compelling arguments). Elliotte Rusty Harold's brief post on this pretty much nails it for me: http://cafe.elharo.com/blogroll/voting-for-checked-exception...


The problem is that it's a trade-off - it's not a case of one side being right or wrong. Checked exceptions introduce API fragility: when the implementation of a method changes in a seemingly inconsequential way (it may now throw an error), then all it's callers and all their callers, transitively need to have their signatures edited. What should be a simple matter between just the leaf method and the top-level now has to involve all the code between them, which may not even be in code you authored or controlled.

Unchecked exceptions, however, mean that code that would previously always succeed may now never even be executed. Which makes it difficult to reason about the behavior of your function.

It's the same trade-off that appears in several other areas of language design. Do you force developers to write down their assumptions in the source code, so that everyone reading it knows exactly what's going on? Or do you let them keep it in their heads, so that it can change easily when the system requirements change? Dynamic vs. static typing, global variables vs. parameters, polymorphism vs. conditionals, default arguments vs. explicitly specifying parameters - it's all the same fundamental argument applied to different language constructs.

Which side your on usually ends up depending on whether you read more code or write more code. Maintenance devs always want things to be explicit, because it makes it easier for them to grok the whole codebase and make the change they need to do. Green-field innovators always want things to be implicit, because it means they have to specify less, and everything they specify tends to end up changing anyways. Sometimes the same person ends up cursing both ends of the divide depending on what they happen to be doing at the time. I do a bunch of speculative prototyping for Search Features at Google; in this role, 90% of my code never sees the light of day, and so I write it with tools like Python or JQuery that let me easily change things around and not specify too many of the details. I also do a fair bit of infrastructure work on the search stack; in this role, I write barely any code and spend the bulk of my time tracking down where an obscure piece of data is coming from and how it needs to be modified to add a new feature.


It is a trade-off, on that I agree with you and I do understand that checked exceptions do tend to propagate implementation-level details up where that might be undesirable. I also agree that in green-field situations it can be awkward figuring out what to do with an IOException that you suspect will not exist in a few hours.

The problem as I see it is that if you advocate the alleged consensus that checked exceptions are evil then you are taking the position that there is no trade-off. Checked-exception lovers like myself acknowledge this grey area and the design challenges it presents. That is a big part of why I believe that a Java with checked exceptions is more "reality-based" than one without them, it recognizes the challenge and gives an API designer the tools to do the right thing. I want to have that hammer in my tool chest instead of banging on things with the back of my hatchet.


That's interesting. I'm not a day to day Java programmer so my perspective is different from the Java community's. I have ran into problems numerous times in other languages where an exception isn't discovered until runtime in production because there's simply no way of knowing whether a library's function might throw an exception or not. The workaround is to be overprotective. I've always envied that Java devs know what to expect from every function they call


Your java library might still throw unchecked exceptions, so you have to be as careful as if all exceptions were unchecked.


Haskell does this also, and its type system is much more expressive than Java’s, enough so that exceptions are not (exclusively) a built-in language feature.


Read up on exception specifiers in C++ and reconsider this comment.


I just did; what I couldn't figure out, was is there a way to specify multiple different exception types in your C++ throw()?

In Java, you can do:

public int myfunc(int a, int b) throws IOException, InvalidGoatException {}

How would you do this in C++?


You can do much the same thing, only the meaning is the reverse.


> @davedx asks : is there a way to specify multiple different exception types in your C++ throw()?

yeah throw() can contain multiple types that can possibly be emitted e.g. int foo() throw(a,b,c,d) implies that foo can only throw either a/b/c/d types.


Except that is only enforced at runtime, and converts the exception type to unexpected_exception, which is even worse!


Exception specifiers are deprecated and for good reason.


It looks mainly looks like a rant on exception, but nothing forces you to use exceptions with C++. You can even allocate memory in a way that doesn't throw.

We don't use exceptions for the reasons described in the article (and many others), we use boost::system::error_code instead.

But that's for the first half of the article, and the OP admits that he simply gave up on exceptions.

As for the part about half-initialized state, I think the problem is that the author is trying to do OOP in C++ without exceptions. Perhaps a more generic programming approach would solve his "half initialized problem".

Don't want to sound like a snob, but it really looks more like a design problem than a language problem.


> nothing forces you to use exceptions with C++

But unless you eschew every other C++ library including the STL, etc. other code is going to (potentially) throw exceptions whether you like it or not. C++ has exceptions built in at its most basic level - operator new() throws bad_alloc. Are you going write C++ with using 'new'? Are you going to ensure that no library you use uses 'new'?

I used to follow Herb Sutter's "guru of the week" problems and a large percentage of them were "how many exit paths are there from this block of code?" which would usually be a function about 4 lines long. Typically there were > 20 ways the function could exit, taking into account all the various operator overloadings, implicit constructors / type conversions, etc. And that was using code that itself usually had no use of exceptions - their mere presence in the language caused the problem.


I have seen exactly 0 programs that ever wrapped new calls in try/catch blocks to catch bad_alloc. If you can't allocate memory, you are likely going to need to exit anyway.

You can write quite a lot of C++ without ever using exceptions since exceptions. There are people who write exception-happy code. I tend to prefer only using exceptions for exceptional events like where the the only other option would be for the program to exit. I throw an exception in the hope that someone upstream will catch it and be able to handle it.

Honestly, if you throwing exceptions constantly as part of your normal executing code path, you're going to drive yourself crazy.


Agree. I've often opted out of using exceptions in C++ for high performance or low level systems. This usually meant coding C in C++, but it was still great to have namespaces, the STL, Boost libraries, and stricter static typing. Too often it seems that people think they have to be at one extreme or the other of the C - C++ spectrum - why not something in between?


> Don't want to sound like a snob, but it really looks more like a design problem than a language problem.

I stopped reading about halfway through as most of the code samples reinforced this.


I agree, this is just a rant on C++.

Coming from Java world, I can tell that it is nice being able to raise exceptions from the constructor.

However, switching from C++ to C is absurd. C++ is (mostly) a super set of C. Whatever you want to do in C, in C++ is also possible.

If there are some C++ features (exceptions, classes, contructors, etc.) that are making his coding task more complicated, he should simply stop using those features and refactor his code.


There is one advantage to writing in plain ANSI C and it is that you get it to compile and run on anything.


I wouldn't say absurd. Certainly you can use C functions in C++. But if you want to eliminate the use of most or all C++-only features, the only way to guarantee that is to not use C++. White or blacklists can be circumvented, intentionally or inadvertently.


I agree.

For me it looks more a blame the tools rant, instead of looking at improving the design.

All the alternatives proposed would work as well in C++, with the added benefit that C++ has a better type checking than C.


This strikes me as throwing the baby out with the bathwater. While the criticisms are valid, and people love to hate C++ for all sorts of reasons (I avoid C++ exceptions altogether), C++ is for practical purposes a superset of C, so if a C idiom works better than its C++ equivalent, you can just use it instead. I've been working on a project which has necessarily been written mostly in pure C (kernel code), and I do really miss C++'s extra type safety. (GCC has some type warnings that help, but it's still not as safe)

Then again, maybe an entirely different language, geared towards high availability, such as Erlang or Ada, might have been a better choice in the author's case.


Just because C++ is a superset of C doesn't make it strictly better. The presence of additional features can be a liability. The obvious problem is that if you want to use the C subset in a multi-person project (whose team evolves over time), you have to create a way to enforce that. Another example is that it's more difficult to produce static analysis tools like lint. (As an example, lint in C detects unused stack variables. This is trivial for C. This is extremely difficult for C++ because the mere construction of the object on the stack often has important side effects.)


It's not even a superset. While not as bad as Netscape changing LiveScript to JavaScript to market off of Java, "C++ is a superset of C!" is still a marketing gimmick. At least with C++11 everyone I've heard talk of it agrees it's a different language now, maybe people will stop bundling "C/C++" on the resume together. For anyone curious about a few other trivial problems of C and C++ incompatibility, check out http://yosefk.com/c++fqa/picture.html#fqa-6.11 (The full FQA there is quite good, being based off correcting/questioning the frequently cited C++ FAQ, so if you want to actually learn C++ more than it probably deserves the FQA will help you along the way. The full section on mixing C and C++ is here: http://yosefk.com/c++fqa/mixing.html )


What you describe as a marketing gimmick has let us gradually migrate a 10M line cross-platform C codebase to C++. There are plenty of reasons to dislike C++ but casually disparaging them often means you miss reasons for its continued real-world success.


While it isn't a strict superset, there aren't any C features you can't use in C++, unless you count lack of pointer type safety as a feature. This was the original point I was making (and note how I didn't say it was an actual superset).


The two ways (C90 and C99) you can create a variable length item at the end of a structure in C are invalid C++. You can't express them at all.

I work around it by making a one element array and using "sizeof(MyThing) - 1" everywhere when I want to reference the size then use placement new and the ::new operator to do the allocation and initialization of my object.

It works, but it's not very straightforward.

In general, I rather like C++, but it does make a few things harder than C.


You can't do it for structure fields in C++. The C++ idiom would probably be something like a member function that returned (void*)&this[1].


Of course there are: C99 initializers are heavenly and they don't exist in C++.


Yes -- see my post above. Syntax and semantics simple enough to be easily statically analyzed (both by humans and tools like lint) are a language feature that C has over C++.

More generally, many people see significant value in the very property of C that it doesn't let you do things that C++ lets you do. That's why the argument that "C++ is a superset" and "there's no good reason not to simply use a C++ compiler on your C code" is bogus.


If you think your biggest problem is forcing your team to use the prescribed language or language subset, it probably isn't.


The thing is, if you use the subset of C++ people usually recommend, you're hardly better off than with C. Turning off exceptions means you can't use the STL (alloc errors throw) and you can't do any non-trivial work in your constructor so you have to manually initialize everything anyway.


Not true. Many STL distributions have a compile-time flag you can set to disable all exceptions. Otherwise, there are entire STL distributions you can compile against that are exception free.

See here for many solutions: http://stackoverflow.com/questions/553103/can-i-disable-exce...


I wouldn't go so far as to actually turn of exceptions at the compiler level. I just see no need to use them in my own code. And as for failing STL allocations: a lot of the time you realistically can't recover anyway, and where you can and want to, well, you'll have to suck it up and put a try/catch block in there. The world isn't going to suddenly end.


In C++, allocation errors don't need to throw an exception.

  Object* obj = new (nothrow) Object;
If the above allocation fails, obj will have the value NULL. It's tedious, but you could replace the default, throwing allocation with a non-throwing allocation in all places of the STL. However, various parts of the STL throw other kinds of exceptions. But, I think one could have a compromise, where you deal with exceptions from libraries you use, but you don't throw any yourself, instead using C-style error handling.


If you replace the STL allocator with a non-throwing allocator how do you know that that vector you just push_back'd didn't actually allocate any new memory?


Indeed, you have to roll your own data structures. This is pretty common in the game development world, where exceptions are considered to have unpredictable performance characteristics.


That is a pretty tight requirement, considering the internals of Ogre 3D engine. It is full with exception classes (some might argue it is way too overengineered.)

Are custom data structures available as open source that don't use exceptions? Or perhaps can you please name an engine that already has this?


There is EASTL, though I've never used it myself. https://github.com/paulhodge/EASTL

Valve's Source SDK makes extensive use of their own data structures, however it is very specialized. For example, linked lists are allocated as growing, contiguous blocks of memory to reduce cache misses. The Doom3 source code might be worth looking at, but I'm sure it's the same story. https://github.com/TTimo/doom3.gpl

At the very least, there's a lot of great reference out there.


You don't. It will segfault.


Many would say doing non trivial work in the constructor is bad practice anyway. Constructors should be simple, set instance vars, and be done; all necessary state should be passed in. The objects job is to be what it is, not be its own factory. Construction of complex objects is better left to factory methods, factories, or builders.


> alloc errors throw

Which usually aren't really recoverable anyway. I mean, you can also blow the stack. Then what? Just ignore those essentially unrecoverable eventualities and terminate.


Yes. C syntax compiled with C++ compiler is a reasonable option. The question is whether it deserves being called C++ then.


Yes, it does, for the same reason that C++ code that is compiled with a C compiler can be called C.


Part of the reason I dislike C++ is that I learned Borland's Object Pascal first. Knowing something else that did the same things as C++, only in a less cluttered way, was a disappointment.

Also, back in 1990, there was arguably no standard, widely distributed, C++ library, either. (which also made Object Pascal more appealing, if you didn't mind getting a language from one vendor that only ran on DOS or Windows)

C is a quite acceptable portable assembler, though :-)


Don't get me wrong, I'd love to see a real contender for a C++ replacement. I just don't understand the attitude that because some aspect of C++ is distasteful, going back to pure C is the answer.


Then again, maybe an entirely different language, geared towards high availability, such as Erlang or Ada, might have been a better choice in the author's case.

I don't follow, but it's clear from your other comments that you know what you're talking about, so I fear I'm missing something.

His goal is for ZeroMQ/Crossroads to be part of the infrastructure, one of those reliable "3rd party libraries" he wants to avoid using himself. He wants it to be a rock solid base on which a high availability solution can be built. Likely this solution will be built in some higher level language (Perl, Python, Ruby, Java, Erlang), and he wants to insure that that language doesn't get tripped up by error handling in the his library.

My instinct is that you need to write the library in something that compiles to a shared library with a C ABI, since this is the only common denominator which the higher level languages can link to. While this is technically possible with Ada, I don't know of any common libraries that do it. And I can't see how it would work for Erlang. I think the only realistic choices are C and a subset C++ with "extern C". Maybe also Go? Do Ada and Erlang fit the bill for this in a way that I don't understand?


"Maybe also Go?"

Nope. At least not right now.

I love Go and Go makes it pretty easy to wrap C (and thus strengthens the argument for using C as a basis for any core libraries), but the story for consuming Go code from anything other than Go is basically non-existent right now. There isn't even a defined way to make Go shared libraries that other Go code can access yet.


This ^ If a language feature doesn't work for you, you don't need to use it. In this case not using it is writing c, but this is true to every program language. When I use a oo language I always use composition and ignore inheritance. Unfortunately you can't avoid dealing with those complexities if you use a library, but if the advantages of using a language/platform outweighs the advantages, is awlays possible to work around it.


> not using it is writing c

But that's not true. Giving up on exception handling and initialization code in constructors still leaves tons of super useful C++ to use. Generic programming, the standard library, what's left of OOP rather than crazy function pointer stuff, etc.


But you can't use the standard library, because it is all based on exceptions.


If you're happy for your program to crash on memory alloc failure, just don't handle them. It's often perfectly acceptable to do so.


Errrr... what did you just say!?!


This obviously depends on your environment. I don't recommend doing this in embedded devices or an OS kernel (this is probably why even C++ OS kernels have exceptions disabled, and no STL implementation).

But if you're just an app running in an operating system with virtual memory, the kernel's out-of-memory-killer can take you out anytime anyway, so trying to recover from every single failed memory allocation is often futile. The most likely reason to get a failed memory allocation on such a system is that you're out of address space (in 32-bit environments). If this is an issue for your app, you'll need to take higher level design decisions to cope with this fact anyway.

If you actually run out of memory (physical + swap) on such a system, you will usually not find out on allocation. Instead, allocations (mmap()/sbrk()) will succeed, but when you write to a previously untouched memory page, it will page fault, not find a backing store for it, and invoke the out-of-memory-killer. Good luck recovering from that.

Just to be clear, this doesn't absolve you from managing and conserving your memory diligently. It's just that if you wait for memory allocations to fail before reining in your program, you've probably made your user's system swap so badly it's barely usable. No cookie for you!


If the system is out of memory, and you're not in some special case huge allocation, odds are very high your program can't proceed in any useful way. You're going to have to terminate anyway. Why pretend it's a recoverable exception? It's usually of the same class as a stack overflow.

If you can't tolerate that philosophy, you might as well just give up dynamic allocation altogether and have everything in stack allocated std::array's. But you still might blow the stack.


What I meant by "not using it is writing c" is that in this case you can put it as "c vs c++", but the real issue is using or not using a language feature. I phrased it badly.


Part of the issue with doing that is that by compiling as C++ the symbols get mangled in a different manner and can lead to areas where you have to compile nearly everything as C++. Or you have to make sure that everything that you might want to face the outside world is marked with extern "C" {}.


This is by and large a very minor point. I typically define a macro similar to this:

  #ifdef __cplusplus
  #define CFUN extern "C"
  #else
  #define CFUN
  #endif
and mark any C-callable functions with it. Easy. The real linkage problems you'll run into with C++ are in dynamic libraries. The higher level of abstraction hides some problems that are really obvious at the C level. Fortunately, you only need to care about this if you need to support third-party plugins to your software. If that's the case, you may want to reconsider your choice of using a low-level language such as C++ or C.


This is pretty much the way I use C++; as C, except with the occasional excursion into C++ when it's more convenient.

The downside is that while I claim to be able to program in C++, my C++ idiom has nothing in common with anyone else's. For example: what the hell is an STL? How do exceptions work? I have no clue.


Practical use of the STL can be learned in an afternoon. Just focus on the Big Four: string, map, vector, and unordered_map (last one is in in c++11 only).

The venerable original SGI document should take you quite a ways: http://www.sgi.com/tech/stl/table_of_contents.html

Good luck, in case you decide to do this.


I think it's probably worth knowing the language and at least trying out all of it. Holding an opinion from an ignorant position is just blind dogma. Who knows, you might discover a delightfully useful facet.


Yeah, just don't use virtual methods or constructors on classes and his second complaint pretty much goes away. And you get a syntax and amenities that are still way nicer than C.


This describes quite well why I strongly recommend against exceptions in embedded code.

There are ways that you can use constructors, one way is to have an error-code as part of the object that the constructor can set. Another way is to use the empty-constructor/init-method pattern that he talks about and wrap it in a static method (or function) and that becomes your actual constructor. I think you can even make the constructor private which will prevent accidental calling of it.

But this is just another example of why high-reliability systems require considerably more discipline in C++ than in C, since C++ gives you more rope, it's easier to hang yourself.

There is an argument that such a limited dialect of C++ is so close to C (with a good 3rd party data-structures library) that there is no point to C++ in such situations (which seems to be the point the article is making), and I tend to go back and forth on this.


I really think the final nail in C++'s coffin is going to be its refusal to play ball in a polygot world. Most languages let you drop down into C pretty easily but C++'s weird name mangling, exceptions and complex, usually nonstandard ABI makes interfacing with C++ a major PITA. In the days when C++ was used soup-to-nuts this wasn't such a big deal but these days you almost always want some kind of higher level language on top.

Just look at all the contortions WinRT has to go through to expose its APIs versus the clean Foundation/Cocoa layering you see in iOS/OS X for a great example.


I agree. In fact, this is what I would have expected to read in the article.

The only single major issue in creating infrastructure in C++ is the enormous added complexity in the final library and dependencies.

While you can avoid mangling and support a "C" api by using ``extern "C"'' declarations, this mostly imposes a "c" like, no-oop API that doesn't really save much work compared to a "C" api.


Certainly in my own projects I have found that going OCaml<->C<->C++ with extern is infinitely more fun than trying to go OCaml<->C++ with SWIG.


+1. My thoughts exactly. One of the factors I reject C++ library for my pet projects is that I can't interface them easily with FFI (LuaJIT, Common Lisp CFFI, Python CTypes, etc.).

And one think that I love about ZeroMQ, is that even being C++ it exposes the stuff with extern "C" and makes life easier. I wish Qt, wxWidgets were easier to expose this way (okay, there is SWIG, or some other tools luabind, etc. - but they produce additional DLL/.so files that are sometimes as big as the library they are wrapping (if not bigger)).


At least with clang/llvm and gcc you now have binary compatibility between two competing compilers.

You can take a .o built with gcc (g++) and link it with a .o generated by clang (clang++) without issues. Same name mangling, same everything. This means that I on my system at least don't care anymore whether the project is built with GCC or with clang and I can use either or for my own projects.

The thing though is that the C++ standard doesn't have a default ABI, so saying that it is non-standard isn't even the case, that we get any compatibility is nice at all, and I understand that complaint.

The biggest issue I've ran into is that even with the same compiler on the same platform (using MSVC 2008) setting different compiler flags generated completely different code and memory layouts for std::vector that caused me to not be able to link against a DLL. At least some consistency exists with other compilers and different flags don't necessarily change compatibility.

I don't think it is entirely fair to compare WinRT with Foundation/Cocoa. WinRT has an entirely different goal than what Foundation/Cocoa provide, and Foundation/Cocoa provide none of the same things that WinRT provides such as a object model that can be used across different programming languages fairly easily. Mac OS X has nothing that even comes close to COM.


Mac OS X has nothing that even comes close to COM.

Because it doesn't need something like COM. As is so often the case, Apple has made some radical simplifications. ObjC has its faults but the C/ObjC/ObjC++ stack covers a lot of ground with minimal mental overhead.


COM can be used by any language and allows software components, object based, to be "easily" shared among languages.

How does it work in Mac OS X?


You either write to the underlying C API or you use ObjC.


Right, but Apple once thought it needed OpenDoc.


Name mangling is pretty common in any high level language, although people only bash on C++.


"There are only two kinds of languages: the ones people complain about and the ones nobody uses." - Bjarne Stroustrup


Can you name some other examples?


Ada, Modula-2, Modula-3, Oberon, Haskell, ML...

I cannot give specific examples, but I am pretty sure that you'll have a hard time trying to combine modules generated from different compiler vendors.

Already the first hurdle is that each vendor has its own language runtime that is only compatible its own implementation.

Second, not all languages define a binary ABI on the language specification.


Going forward, I think C++ is not a bad choice, but I can definitely see the distaste for exceptions. Unfortunately, as the author notes, simply not using exceptions doesn't quite do it. Fortunately, the hard work has been done for you. Much of Google's code was written in an exception-free manner before newer evolutions of C++ came out. This means that they have banned use of exceptions in their style guide. Luckily, it also means they've developed workarounds for most of the exception pain points.

I would encourage everyone to take a look at the Google C++ style Guide[1]. Most of the complaints are addressed in that article. Constructors can only signal errors through exceptions, so Google C++ allows only basic initialization in the constructor -- all real work is done in an explicit Init() method which can signal an error. I believe the guide addresses the posters other concerns too.

In general, the Google C++ style guideline is a very good resource for writing consistently good C++.

[1] https://google-styleguide.googlecode.com/svn/trunk/cppguide....


Agreed, it is a very good guide.

However, the ZeroMQ author goes a lot further than that for his specific application.

He made some reasonable arguments that even the use of OOP/classes in C++ makes catching undefined behaviour unnecessarily difficult for systems programming.


His other objections didn't make a lot of sense to me. Destruction doesn't seem like something that can fail. Your object has been deleted or it is going out of scope. You don't even manually call the destructor. It should be about freeing resources. what work is he doing in the destructor that can fail?


Well, fclose can fail, for example; it can as part of the closing process flush its buffer and that can fail like any other I/O process. Further, sometimes the error than happened during a previous write(2) call is only detected in the close(2); think NFS and quotas, or check the man page. So already we have errors that occur during teardown that need to be handled somehow.

Similarly one can imagine using RIAA to hold a file lock, or a temporary directory; this is a perfectly reasonable thing to do but there are dozens of reasons why the final rm -rf of the tempdir could fail.

The idea that somehow object teardown is immune to failure is overly naive, and the fact that you can't accommodate it easily in RIAA is quite disappointing and makes the whole paradigm much less useful for carefully coded software.


My claim is that object teardown is not what the destructor is for. Teardown encompasses many things that are not resource releasing. Destructors are for releasing resources, not necessarily guaranteeing their destruction.

For example, fclose can fail, but even if it does, the stream given is no longer held. In fact, any further use of the stream is undefined. There is certainly a place for logging in this situation, but if close was a behavior of the class, it should have a separate close function.

As far as locking goes, it is the responsibility of the locking routine (probably a semaphore) to release the callers hold on the lock, but it is not the callers responsibility to ensure that whatever resources are associated with the lock (e.g., a file) actually get deleted. That is the locking object's responsibility.

I agree that some aspects of RAII could be better, but C is not the answer to that. C doesn't even have scope-based RAII.


That's awesome, I'm already looking into crossroads-io. I like C because it's that much more readable: There is no magic, you always know where things are and where they are going, in terms of memory and control flow. C++ seems very convenient at first, all the nice standard libraries do have attraction for me but in the end you end up with tons of weird hierarchy magic, friend classes, nested classes, copy constructors and destructors firing at the weirdest places. Now everybody says: "Well you don't have to use those!". Well, true but in practice it doesn't turn out that way.


If you're enough of an expert in C++ to use it in failproof ways, then you're enough of an expert to do the same in C, and with far less mental overhead for yourself and everyone who comes after you to read the code.


90% of this thread isn't addressing his core point, which is that even if you use C++ without exceptions, constructors and destructors still make it hard to be strict about error handling, and the extra complexity isn't worth it for projects like his.

Fine to disagree, but respond to the point. So far the relevant objections I've seen are: (a) you shouldn't do anything in a constructor that might fail; and (b) destructors can't fail. Weak. Is there a good counterargument? To be precise: why is the OP wrong to say that, to be strict about error handling, his C++ needs to look like the block of code that follows the sentence, "There's a new 'semi-terminated' state" in the article?

(As for most of the comments, which boil down to "OP is ignorant of basic things about C++ and programming", come on you guys, how likely is that? Who's more likely displaying ignorance here?)


Author went from constructors+exceptions to init_foobar()+return value for error handling. When you misuse exceptions, they become unmanagable nightmare, which I believe would have happened with 0MQ. And throwing exceptions in constructors is one really good way to misuse them.

And ranting about it seems like ignorance to me, but that's beside the point.

For me, checking return values is just as, if not more, tedious than catching some exceptions. Return values should be used to guide the flow of the program state (IMHO), and exceptions represent invalid states.


You still haven't responded to his point, which is that to deal with the reality that constructors and destructors can fail, he ended up having to write more complex code than he would in C. (Specifically, the code in the passage I cited, which turns each object into a little state machine.) Saying "just make those things not fail" is no answer at all. So, to repeat: how could he meet his error-handling goals in C++ without handling object construction/destruction in that state machine style?

I have no opinion about this, but given that it's the point of the article, that's what people should be addressing.


It's not that constructors/destructors can fail, it's about the conscious decision when you make them to. If you choose not to use exceptions at all, you can prevent improper state before object construction, just like you would in C's init_foobar().


For careful error handling, my favorite language is Go. It lacks exceptions, so you have to return errors as in C. But it has python-style tuple return, making it easy to return a value or an error from any function.

The fact that errors are explicitly returned requires the programmer to explicitly handle them, which encourages design that considers error handling.

The evil of exceptions is they hide errors until they happen.


I wonder what error-monad looks like in C++, or C for that matter.

here's a rough, probably incorrect demo in python: https://gist.github.com/2653752

gives you nice, clean, exception-like error handling that doesn't leak to the wrong level of the call stack, and in a statically typed language the compiler can verify your error handling for you. quite similar to checked exceptions. call stack issues can be fixed with some low level C, or you could even unwind the stack with some exception trickery.


I struggled with a similar question for my low-level protobuf library UPB (https://github.com/haberman/upb). Here are some blog posts describing my dilemma about this: http://blog.reverberate.org/2009/11/porting-upb-to-c.html http://blog.reverberate.org/2009/12/torn-over-c-question.htm... I ultimately chose to stick with C, and I'm happy with my decision.

When people ask me why I stuck with C, it's difficult to give a single overwhelming reason why C is better. The most clear-cut one is that C++ is not supported in the Linux kernel and I want my library to be usable there. But the other reasons why I prefer C are not as easy to quantify.

One reason is that code written in C is more transparent. You can look at some C and know what it is going to do (add some numbers, call a function, etc). Macros are the one exception to this, but good C programs use these sparingly and clearly mark the macro with an UPPER_CASE_IDENTIFIER. C++ is much harder to "see through." Any function call could end up throwing an exception which does a non-local exit from the function. Any addition, assignment, pointer dereference, etc could actually be an overloaded operator. Any function call could be performing a user-defined implicit conversion on any of its parameters. For overloaded functions, the compiler decides what function is actually called by applying non-trivial pattern matching rules. The runtime will automatically invoke any static initialization code (like constructors of global statics) pre-main in an undefined order. etc. etc.

I intend to get a thorough security review of my library once its design stabilizes. I asked Tavis Ormandy (a fellow Googler and security badass) which language is easier to audit for security and he said C for the reasons of transparency I listed above.

People often say that you can just avoid using the parts of C++ that you don't like. That is true, but hard to enforce, especially on a multi-person project. If you're asking others to follow your set of rules, you have to write those rules down in meticulous detail, leading to something like the Google C++ style guide (http://google-styleguide.googlecode.com/svn/trunk/cppguide.x...), and at that point you're asking your contributors to study and internalize a large and complicated list of what is and isn't allowed just for the privilege of contributing to your project. I don't think this approach scales to lots of projects that are all choosing a different subset of C++.

There are definitely things I miss by not having C++'s facilities. Constructors and an implicit "this" parameter are both really nice. Constructors are remarkable in that you can write a single function that gives you three different construction patterns for free (direct construction, heap construction with "new", construction in an arbitrary location with placement new). Automatic upcasts are nice and totally safe. Inheritance and public/private specifiers let you express directly lots of things you can only express with comments in C.

But overall I think that using straight C is the right choice for a project like my library UPB.


good C programs use [macros] sparingly and clearly mark the macro with an UPPER_CASE_IDENTIFIER

This is only necessary if (a) the macro has side effects, or (b) the macro interprets its arguments multiple times. In cases where the macro is functionally equivalent to an inlined function call, there's no need to capitalize it.

asked Tavis Ormandy (a fellow Googler and security badass) which language is easier to audit for security and he said C for the reasons of transparency I listed above.

I agree on both counts: C is much easier to audit, and Tavis is indeed a security badass.


C is superior for libraries since a lib can then be used from so many other languages. A C++ library that doesn't provide a C API is an absolute nightmare to write bindings for.


So true. Binding to Python is such a pain: http://www.boost.org/doc/libs/1_49_0/libs/python/doc/. Lua is even worse: http://www.rasterbar.com/products/luabind.html....

Seriously, once you know how to use it, C++ actually makes binding to a lot of higher level languages a lot nicer than doing so with C.


... I am sorry, but that's very untrue.

As it happens, not every language is implemented using runtime written in C/C++, nor does every language modules written in C/C++ to extend it instead of FFI.

Dealing with libraries written in C++ without C API is one of the worst PITAs in my experience, leading to crazy things like KDE's SMOKE (which, to make it funnier, has bad documentation, so I'm still unsure if I can make it work outside Qt's object model)


> ... I am sorry, but that's very untrue. > > As it happens, not every language is implemented using runtime written in C/C++, nor does every language modules written in C/C++ to extend it instead of FFI.

You did an awesome job tearing apart that strawman, but you are misrepresenting what I said. That second sentence is in no way in conflict with my "very untrue" statement.

To refresh, with emphasis added for the reading impaired:

    C++ actually makes binding to *a lot* of higher level languages a lot nicer than doing so with C.
That you can find a distinct subclass of languages where you believe that it is hard to do bindings to C++, doesn't mean the above statement is even mildly untrue, let alone very untrue. Please review and reconsider:

* There are certainly a lot of higher level languages which don't qualify for your definition. * SMOKE is actually a binding through Qt's MOC, which is generally considered by the C++ community to be something which makes Qt pretty un-C++-ish. You are right to phrase it as "Qt's object model", because it sure as heck isn't C++'s.

Try looking at the LuaBind and Boost.Python libraries. They represent a very different approach to language binding, and it isn't dependent on there being a wrapper C API. While writing binding framework for a given language is a bit of a PITA, once that has been built, it really does make binding to a high level language a lot easier than trying to do it through a wrapper C API.


Throwing C++ exceptions across DLL boundaries could also be problematic. Note I say DLL, and accent on Windows, because on other systems more likely one C++ runtime to be used, always dynamically linked (well, there are exceptions like busybox - but they are not even written in C++).

Anyway, in the Windows world - a mix of compilers is the norm, and even the "standard" MSVC compiler differs in how it handles exceptions one version to another.

For example all the team is on VS2010, while I keep VS2005 and VS2008 because I have to compile plugins for Autodesk products released with these compilers (not only for exceptions, but also for RTTI, virtual functions handling, and Runtime overall - for this even "C" would get the blame).


On Linux you can mix compilers as long as they implement the Itanium ABI. At least I haven't had to recompile anything after upgrading GCC.


Not C++ compilers tho' - the issue is not the ABI, it's the name mangling. Tho' I believe Clang and GCC have decided to use the same convention, ICC might not have (and that by all accounts is a great compiler).


http://sourcery.mentor.com/public/cxx-abi/abi.html#mangling

I'm 99% sure that ICC is compatible with GCC.


When the switch between GCC 2.x and 3.0 happened, the big new thing was 'industry standard' C++ ABI (called Itanium ABI, as el_presidente mentioned).


you have to write those rules down in meticulous detail,... and at that point you're asking your contributors to study and internalize a large and complicated list of what is and isn't allowed

What about a short list of what is allowed, with a strict whitelisting policy? I recall from years ago a version of the Taligent C++ conventions that was fairly compact. (Which was also the subject of Bjarne Stroustrup's quip about trying to use C++ as "a poor man's Smalltalk.")

Automated tools that embody the blacklists might be useful here as well, provided that they can be scripted quickly. I'm not sure if there are parser-based tools for C++ that would fit the bill. The article's problems with non-empty constructors seem amenable to automated blacklisting. Unfortunately, even with such tools, this strategy entails lots of work up-front by project organizers.


To save someone else the trouble of googling:

http://pcroot.cern.ch/TaligentDocs/TaligentOnline/DocumentRo...


These aren't the conventions you are looking for. (The ones I remember call for all member functions to be virtual.)


In C++, if you run into an error and you know how to handle it, you handle it. This is exactly the same as C. Exceptions are only there for when you don't know how to handle an error. Instead of calling exit(), you throw an exception and hope that someone further up the stack can do something useful. By default, your program terminates, just like exit... but all your destructors get called, so you don't leak database handles or locks or other resources. Nice.

I've read the Google C++ style guide and it's basically a joke. At least it was a joke two years ago. The guide even contradicted itself in multiple places. Google bends over backwards to avoid using exceptions and the result is that C++ isn't C++ anymore.


I agree with this entirely. Exceptions and errors aren't necessarily the same, i.e. errors aren't always exceptional cases. When errors can be dealt with locally they should be. The problem comes when an error can't be dealt with locally. Then as stated, exceptions can be used to propagate the condition upwards in the stack where some other component may be able to deal with it.


GCC is finally switching from C to C++, and they are actually avoiding exceptions and RTTI.

http://gcc.gnu.org/wiki/CppConventions

I salute them on following strict coding conventions.


I'm not C++ savvy, but I know from reading Google's C++ style guide that they also disable exceptions. The guide leaves it at that and doesn't say anything more about how to deal with errors. Does anyone here know how these big Google C++ codebases handle errors instead?


[The following is speculation: I have no access to Google's code.]

I can't think of any other way to handle errors apart from using return values. Methods return error codes which are handled by the caller or bubbled-up to its caller. This tends to result in lots of duplicated error handling code, and you have to either dedicate the return value to success/fail (which means you have to return non-status data some other way) or use special values to indicate failure.

The Go language (from Google) doesn't have exceptions and allows functions to return a value and an additional error return value [1]. I'd guess that this is inspired by their approach to C++ coding.

[1] http://code.google.com/p/go-wiki/wiki/Errors


Isn't it possible to fake this with C++ templates? As an example, I created https://github.com/rb12345/cpp_annotated_return, which uses a templated struct to allow multiple value returns.


Yes, you could do it that way. Personally I'd restrict the error code to always be an int, which makes for a terser syntax as you don't need the second template parameter.

You could also extend it to support multiple non-error return values, like Python does. But this introduces problems with expressing the contract between caller and called (e.g. [1]) and I think the best way to address this might be to extend the language syntax.

I think one of the most interesting design choices in Go is that it intentionally omits conventional exceptions. It does have defer/panic/recover [2] though.

[1] http://stackoverflow.com/questions/354883/how-do-you-return-... [2] http://blog.golang.org/2010/08/defer-panic-and-recover.html


Exceptions are indeed not the silver bullet to error handling. Handling errors upstream can be useful as catch-all, but when the entire stack has been unwound, it can be hard to recover the error usefully. Also it is hard to make a sensible inheritance hierarchy, and every library chooses its own, generating a forest of different-but-equivalent exceptions to handle.

A fully-fledged condition system (like Smalltalk, CL) would allow for a lot of scenarios where exceptions currently give trouble. They would allow recovering the problem where it happens, retrying operations, and so on. Too bad so few languages have that.

However I don't think the right solution is to use an even more primitive language. C seems simple, but you end up doing everything yourself that would otherwise be done by the compiler, which is a lot more work, bug-prone, makes the code verbose, and generally inconsistent (unless you're very disciplined; forget about it in a team).


His reasoning is bogus. For his C equivalent, he writes:

> There are only two states. Not initialised object/memory where all the bets are off and the structure can contain random data.

He can get exactly the same effect by mimicking fstream interface: use constructors and destructors as they were meant to and introduce bool operator! to check whether the object has been successfully initialized.

In this scenario, only destructor needs to know about the "fail-constructed" state. Other methods don't need to implement the state-machine; calling them on "fail-constructed" object would be undefined, exactly as in his C version (e.g., calling foo_bar without calling foo_init and checking that it succeeded)!


Just to quickly note here: Certain very popular game console does not allow use of C++ exceptions - the underlying runtime / abi / whatever simply does not support it.

Not that you would ship ZeroMQ code in it (it's GPL), but still.

Oh, and I love ZeroMQ, and I even love it more, that the "C" interface was the one being public, not the "C++".

Also good article. I love exceptions in higher-level languages - lisp, lua, etc. - but I prefer error codes in lower-level ones, and I consider C++ as such.


This may just be because of when I learned to program, but I find that writing object oriented code makes everything easier to understand. The stuff you need to do with C where you're passing around structs and state and pointers usually looks pretty ugly to me.

Error handling is a pain in any language. "This thing that should NEVER fail just failed, now what?" If you're not in a recoverable state, realistically, what can you do?

I know that constructors can fail, but it's pretty abnormal for that to happen. I like the way objective-c handles it. Since everything is dynamically allocated and you can just see if the object exists to find out if it works. Lots of methods have an extra NSError parameter that you can use if you need more information.


Interesting read but the problem is not C++ here. The problem is the author's usage of C++ and specifically exceptions. If he does not like exceptions then just implement more traditional error handling while keeping all the other good things that C++ provides.


>Now consider what happens when you add inheritance to the mix. C++ allows to initialise base classes as a part of >derived class' constructor. Throwing an exception will destruct the parts of the object that were already successfully initialized with this

which object is he talking about here ? if foo inherits from bar, object foo is not considered a valid object till it's constructor completes. so there is no question of destroying parts of objects which were already constructed.

similarly, during object destruction, as soon as the destructor is invoked, the object is in an invalid state.

i guess there are some issues in understanding of object lifetimes and exceptions thrown by it's constructors...


The one advantage of C over C++ is simple: what you see is what you get.

If you have to wonder about what a statement really might mean depending on a bunch of state that was fed to the compiler then that really spoils things.


The reasoning is very poor. I thought he was going to say something along the line of portability. Nope. It was about exception.

Granted, exception is problematic. I don't use exception for similar reasons. However, given the nature of this project, it should have been clear that exception should not have been used in the first place. Instead, be very explicit about error handling. This is really a poor design choice. How is it the conclusion that the answer is C, rather than "C++ without exception?".


I agree fully with the author - exceptions are a problem for software that wants to avoid unexpected behaviour. I noted something similar in http://tratt.net/laurie/tech_articles/articles/a_proposal_fo... and sketched a possible compromise, more to emphasise the point, than as a serious proposal.


C++ exception specifications are worse than nothing. Unlike Java's (controversial) checked exceptions, C++ exception specifications are not compile-time checked. Instead, the compiler generates run-time checks when calling function functions with exception specifications. If the function throws an exception that does match its specification, the program calls abort()!


As with most experiments in writing object oriented code in C, this looks a lot like Objective-C.


Exceptions shouldn't be used as gotos. Instead of letting every exception bubble to the top, handle them at the appropriate level in the stack where you can recover from the error they representing.




Oh well. It was probably faster and easier to code in C++ initially. And allowed him to beat all the competition to market.


looking forward to crossroad i/o. This step will drive the two projects even further apart. But zeroMQ is already established, and its name is more catchy. Good luck.


"First, it's important to take into account that ZeroMQ was intended to be a piece of infrastructure with continuous uptime. If should never fail and never exhibit undefined behavior."

There is one language/platform[1] I'm aware of designed for this type of system, and which achieves it beyond all others: erlang.

If Uptime is important, how do you handle it when you want to update your software under C++ or C? You have to bring the service down. Erlang supports updating software while its still running.

If exception handling is important (to prevent crashes) how do you handle it when there is a bug? Erlang has supervisors that not only help you manage exceptions but keeps the service going when an unhanded exception causes things to crash.

More importantly, intrinsic to the nature of the way you write erlang code, you eliminate a great deal of the possible errors. In C code you have to check to make sure things are in the right state while running your code. In erlang, your code doesn't run (the function isn't called) if the state isn't right to handle it. It flips this problem eliminating many classes of errors. Further there is OTP support for many types of processing (like state machines) that makes writing these types of solutions much easier.

Erlang is great for making sure no undefined behavior happens. Erlang is also great (as I understand it, this I haven't gotten into) for creating unit tests that can fully exercise the code across all possibilities.

Where does erlang suck? I don't think it sucks anywhere because it has solutions for all of its own problems. For instance, single node performance of an erlang program (which runs in the BEAM VM) is going to be lower than single node performance of a C program... but in erlang you can increase capacity nearly linearly simply by adding more nodes... while this is nearly impossible with almost all C/C++ programs without spending a lot of time working on it.

But where erlang is weak is that single node performance which might be really important[2], and if it is, you can then write your performance critical bits in C.

Thus the things that do the real work can be fast and in C but the things that keep the system going and distributed (where performance of this kind is much less critical, but performance of the operational kind is paramount) can be written in erlang.

So, I think the second mistake he made, and is still making, is thinking that the project has to be written in a single language.

If you use the best language for the job, then sometimes that should mean using two languages right?

Erlang is not a hipster language. No hipsters use erlang. It is 25 years old and boring and %99.9999999 uptime. Its ugly. Its not hot and fresh and new. But it is the right language for the job, when you've got a system that need to run across cores or nodes, or have very high uptime.

And really, it doesn't take but a couple weeks of learning. Then the syntax will be gorgeous and you'll be a better programmer for it.

[1] I say "I'm aware of" for a reason. There may be others as well suited when this is the primary goal, possibly even better suited. I don't know all languages. But I do have a pretty good survey started in the days when new languages were as common as YC batches and much more experimental than they are now. My point isn't to bash other languages but to promote one that isn't understood correctly by many people.

[2] I think this is the case a lot less often than people think. People benchmark things on a single node even when they know they are going to build a cluster of machines to run them, and then pick them based on this. Redis is really fast. Is redis distributed? Riak has a fully distributed in memory database (which means 10 32GB nodes means you can store 320GB in RAM if you want, rather than have 10 nodes all storing the same data in a cache, and thus much less data cached for the same number of machine with something like Redis.


Erlang is not a language for writing libraries that can be integrated into other runtimes. It is opinionated about concurrency, memory management, types, message passing, etc. That isn't inherently a bad thing, but it's the wrong choice for a library that is trying to offer in-process APIs in lots of different languages.

ZeroMQ is software that is more at the level of the Erlang VM itself. It's not an application, it's infrastructure. You wouldn't say that the Erlang VM should be written in Erlang.


Exactly, this makes no sense at all. 0MQ is a low-level library that higher level languages link into (see all the [bindings][1]); it's pretty much a veneer over plain sockets that adds a bunch of features. Why would you, or better, how could you write something like this in a language that has to run in a VM?

[1]: http://www.zeromq.org/bindings:_start


It is possible for C programs to update themselves with zero downtime. See nginx[1], "Upgrading Executable on the Fly". The trick is that fork()'ed processes (can) inherit open sockets from their parent, even after exec()'ing a new binary. This seems a bit delicate and involves some communication during the hand-off from the old to the new, but it does work. It's also possible to kill the "new" if there were any problems and instruct the "old" to take over again.

This is not to take anything away from Erlang, which is a very interesting system.

[1] http://www.nginx.org/en/docs/control.html


> This seems a bit delicate and involves some communication during the hand-off from the old to the new, but it does work.

MUDs routinely did this back in the day. Here's a README excerpt from Erwin Andreasen's widely used patch:

"Basically, for each playing descriptor, it saves the descriptor number, the player's original name as well as the host name (so we don't have to find that again). Then, it closes fpReserve, and exec's the MUD again. As exec preserves open file descriptors, players do not lose link. The MUD is executed with some extra parameters, so the new copy knows that it should reload some player files. It does that, and puts in the players at their old places."

http://www.andreasen.org/ftp/copyover-7.tar.gz


Just logged in to say this is the most awesome thing I've read in weeks. Why did I not know this earlier?!


> Where does erlang suck? I don't think it sucks anywhere because it has solutions for all of its own problems.

This is a very novice thing to say, for any language. I've found it takes me about 3-6 months of regular use to start seeing the warts of a language (and a few more to learn how to work around them).

Erlang seems pretty boss (I haven't used it myself) but this post reads like "just buy a Mac and everything will be easier!!!" Unfortunately that doesn't take into account the user's experience and needs. Good luck switching to a tiling window manager on a Mac.


Some of my reflections on Erlang's warts:

http://journal.dedasys.com/2007/09/22/erlang

It's from nearly 5 years ago, so they may have fixed some of those things.


If Uptime is important, how do you handle it when you want to update your software under C++ or C? You have to bring the service down. Erlang supports updating software while its still running.

Nginx has a hot update feature and is entirely written in C.


For that matter, Erlang is written in C.


I'm glad it has that feature, but you have to write it yourself. I wasn't saying it was impossible to do in C. Erlang has it built in, along with frameworks in OTP to make it easy to do.


If you had 1) looked up what ZeroMQ does, and 2) ever actually implemented a system in Erlang, you wouldn't dream of making a recommendation like this. It is completely irrelevant to the author of the article.

Further, Erlang makes no attempt to avoid undefined behavior. It provides mechanisms to restart pieces in a usable state to bring the system back into line rather than try to avoid or fix incorrect states.

Please don't post opinions about languages you have never done real work in. Your weekend project doesn't count.


And I think Erlang is unique in the respect that fault tolerance and uptime was the driving feature behind its development. It basically started with that -- they way I heard Ericsson would have contract that specified that anything beyond 4 minutes downtime would be billed at $10k / minute or so.

Then hot code reloading, supervisor structure, isolation of faults, actor model all sort of fell out of that.

And as Joe Armstrong also said in respect to single node performance is that "there is no free lunch". In other words at some point you'll have to sacrifice some performance if you want fault isolation.


> Erlang is also great (as I understand it, this I haven't gotten into) for creating unit tests that can fully exercise the code across all possibilities.

Specifically, Erlang has a good (and professionally supported, IIRC) port of Haskell's QuickCheck for exactly this sort of testing.


That's QuviQ's quickcheck. There's also proper: https://github.com/manopapad/proper


Erlang is cool. RabbitMQ core is written in Erlang. But I don't think zeromq could be written in Erlang and still meet the needs for which it was invented.


Yeah I wish people would just use Erlang instead of reinventing this stuff over and over again in their favorite language/framework/vm etc. When I think of all the work going into Node instead of Erlang it makes me want to cry.


I think for some project uptime is not as badly needed or not taken seriously enough as it was for Ericsson at the time. So for example during prototyping some engineer saw how Erlang was 5 times as slow as C. They told the manager and the manager said "oh well, clearly we should use C then".

If uptime is not the driving feature of the project. It will be very hard to add it later as the language chosen and the libraries have to have also been built with uptime in mind.


Not that this detracts at all from your point, but Erlang is 25 years old, and has been publicly available for 14. So it is relatively new compared to most of the other ones in current use.


However it has 25 years of real world use and that's what is important. I think Erlang used it for a serious industrial grade product -- AXD 301 switch (and maybe others I don't know about).


Yeah, I meant to be exaggerating for effect but it didn't come across that way at all, so I updated the post.


you are missing the point of ZMQ, it's a library not a service, writing it in erlang would completely defeat its purpose as comparatively very few people would be able to use it


The article is about undefined behaviour. Erlang won't help much with that.


Depends how you write your code. Actually Erlang is pretty good at avoiding undefined states (which can be thought of as undefined behaviour) by putting everything in an OTP framework. There's no custom "I'll just handle this earlier here in a tricky way" - if you stick to OTP you'll have message handlers and state machines. That's really awesome if you're programming network daemons. You can pretty much follow the state machine diagram and make sure that unless some progression is possible, it will not occur in practice.

Testing based on OTP service trees also makes the verification fit the possible transitions in a much closer way than trying to cover all the branches in an OO app.

So yes, I'd say it's actually quite a good response to an "exception handling all over the place" problem. You simply don't do that. Instead your exceptions finish at the FSM level and progress you to one of the error states on your diagram. You know exactly what state you're in at that point so the behaviour is pretty well defined at almost every step.

However zeromq is a library that was supposed to be portable, rather than a daemon itself, so from that perspective, it's a very bad fit for ZMQ.


What worried me about Erlang was that when a process dies and gets restarted, the message queue for the old process gets thrown away. Neither the sender(s) nor the new process can tell how many messages were lost.


If you care about reliability of the delivery, you can do a number of things: do a single call, wait for response and retry if needed, or store the messages in mnesia and only notify there's something to pick up (pull scenario with ack), or do a number of other possible things.

Lost messages are a fact of life really - the same will happen with any other system - either you persist the message and ack the reception on every stage or you risk dropping the queue.


Yes, that situation is usually handled by using an ETS table to store messages (essentially, you create an internal message queue for the supervision tree).

On the other hand, there's no guarantee the message arrived in the first place (especially when using multiple nodes) unless you use ack patterns.


Well, yes and no. I know more about OCaml than Erlang, but pattern matching and guards let you be very sure that a function is going to handle every possible input, and static typing means all code paths can be verified at compile time - there's no way into or out of a function without the program being in a valid state.


What about, say, a function that puts ten elements into a list that needs to get sent a receiver that only expects nine ? Or a network call that returns an unparseable reply?


In the first case, I wouldn't use a list for that, but a record/struct/class with 9 fields. In the second, I would use an Either/Maybe type, or raise an exception (these work differently in OCaml than in C++)


Firstly the article is about the use of C++ vs C in ZeroMQ (crossroads). Then the article is about systems having a high uptime and how C++ is not ideal for that.

Here is the quote:

> First, it's important to take into account that ZeroMQ was intended to be a piece of infrastructure with continuous uptime

So it is about uptime, that's the supposed goal of someone using ZeroMQ. C++ has undefined behavior and this, the author claims, can lead to more crashes and reduce uptime.

Then nirvana suggested that if uptime is needed, there is a better language and platform designed for it.


Impractical fanboyism that doesn't understand the software in question.

Poor attempts at proselytization like this do more to harm Erlang than help.


on the exception handler part, i don't think you're forced to use c++'s try-throw-catch, you can still safely use the C-style exception methods, after all, c can be used as a subset of c++, though not 100% of the time.


wat?


The background made that post unreadable for me. Minimalism done wrong.


Try readability.com.


Even easier in Firefox: View -> Page Style -> No Style




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact

Search: