Hacker News new | ask | show | jobs
by betterunix 4636 days ago
"I don't see why people feel that C++ needs to be replaced"

Here are some of my reasons:

1. It is impossible to write high-level code without dealing with (and often getting bogged-down by) low-level issues in C++. Why should I be forced to choose between different "smart" pointer types? Why should I be forced to decide how variables should be captured by a lexical closure? Sure, such decisions might make sense when you want to squeeze out a constant-factor improvement in performance, but they do nothing to help you get things done in the first place.

2. Error handling and recovery is needlessly and pointlessly complicated. You can throw exceptions, except for the places where you cannot, and once caught you there is not much you can do to fix the problem. It is so bad that the C++ standard library actually requires certain errors to not be reported at all.

3. Extending the language is impractical. Look at what it took just to add a simple feature, lexical closures, to the language: modifications to the compiler. At best C++ gives you operator overloading, but you do not even have the ability to define new operators. Lisp, Scala, and numerous other high-level languages give programmers the ability to add new syntax and new features to the language without having to rewrite the compiler.

I am not familiar enough with Go to say that it addresses any of this, but I know why I stopped using C++ and why I have not regretted that decision. All the above make writing reliable code difficult. I actually switched away from C++ when I needed my code to scale better, because improving the scalability required a high-level approach and I did not have time to debug low-level problems. Even C++ gurus wind up having to deal with dangling pointers, buffer overflows, and other needless problems with their code -- that takes time and mental effort away from important things in most cases.

"When I think about C++ I know that a code written in C++ will take me 100% of the way - even if it takes longer to write."

The same is true of any programming language if the amount of time spent on the program is irrelevant. I am not sure what sort of work you do, but for what I have been working on, getting things done is considered higher-priority than squeezing out a constant factor improvement. Nobody complains about faster code, but everyone complains about late, buggy, and incomplete code.

3 comments

C++ does not force you to use smart pointers.
-- Why should I be forced to choose between different "smart" pointer types

If you don't want to decide then write all your types with value semantics and pass by value. How types are going to behave when passed should be decided before you write 'class{}'. It's a semantic decision. For types that you're borrowing, and not writing yourself, pass a shared_ptr or refer to the documentation.

-- Why should I be forced to decide how variables should be captured by a lexical closure?

Same thing applies. Auto capture[=] everything by value. If you're type doesn't have any (sane) value semantics, use a shared_ptr or a reference.

-- You can throw exceptions, except for the places where you cannot, and once caught you there is not much you can do to fix the problem.

You can throw an exception anywhere safely in correct code. The default assumption in the language is "anything can throw, any time, anywhere", so if your code doesn't at least provide the basic or weak exception guarantee you're swimming against the tide. Doing so usually improves the encapsulation and structure of code imo anyway.

-- once caught you there is not much you can do to fix the problem.

Exceptions are more like hardware exceptions or page faults than typical error states. You should only throw when you cannot reach a state expected by the caller. Ultimately, it comes down to API design, not philosophy.

    // Clearly the only sane thing to do here if you 
       can't stat() the file is throw an exception.
    size_t get_file_size(string filename);

    // Some flexibility. Could probably avoid throwing. 
    optional<size_t> get_file_size(string filename);

    // Better still, and easy to overload with the above
    optional<size_t> get_file_size(string filename, error_code&);
... the better your API the better you can avoid having to throw. This isn't a new problem either, if you look at the C standard library there are many deprecated functions that provide no means of reporting an error at all except to return undefined results.

-- extending the language is impractical.

Writing STL-like generic algorithms is trivial. Writing new data structures is trivial. Existing operators can be overloaded to augment scalar types or, more ambitiously, re-purposed to create DSLs. You have user-defined literals. Initializer lists and uniform intialization.

How would you like to extend further without it being completely alien to the existing language?

-- I actually switched away from C++ when I needed my code to scale better, because improving the scalability required a high-level approach and I did not have time to debug low-level problems

You should write more about this.

-- C++ gurus wind up having to deal with dangling pointers, buffer overflows, and other needless problems with their code

Not really bad pointers and buffer overflows these days. More slogging through pages and pages of compiler errors and hunting for library solutions to problems that should really be solved in the standard library (For me lately: ranges, matrices, more complex containers).

In any case, all languages have their share of friction. Look at that new Bitcoin daemon written in Go that hit the front page a few hours ago. The author had to debug 3 separate Go garbage collector issues.

Correct me if I am wrong - but exceptions should not leave destructors. So you can throw them in destructors but you have to catch them there too - I think that is what the commenter you are replying to is getting at. So it has to be RAII because the destructor will not communicate up a hierarchy. This imposes a lot of overhead writing the destructor.

Further to the general theme of the thread - C++ compile times are bad, Go compile times are quite nice - I think this is significant when prototyping and testing units of code.

Combining different libraries with their respective memory management and error handling ideas is one of my biggest issues with C++. You have to keep so many things in mind to use every API according to its own peculiar rules. One slip of the mind and you're in big trouble.

Also, getting all those libraries to compile with a particular compiler/stdlib combination is a big hassle. Things break in non obvious ways because of weird implicit template instantiations that are basically untestable by the creator of the library.

These types of integration issues are never going to go away and therefore C++ will occupy a stable niche forever but will never become mainstream for application development again, regardless of any language improvements.

Mainstream again? What's the majority of code being written in nowadays?
Most new application development is done in Java and C# (and perhaps JavaScript).

C++ is used for a long list of specialized tasks like embedded systems, games, in-memory computing, database systems, high performance scientific stuff, some low latency trading algos and a few core UI things like browsers. I don't consider any of those mainstream application development.

"The same is true of any programming language if the amount of time spent on the program is irrelevant."

No it's not. That's what the original post was arguing (I think successfully).

It most certainly is true, if you can spend an unlimited amount of time coding. Python is too slow? You can isolate a subset of Python and write an interpreter that emits very efficient machine code for that subset, then write all your code with that subset. Sure it will take a long time, but so what? -- we started with the premise that the amount of time we spend writing our programs is irrelevant.
Yes, you can call a binary C/C++ function from Python.

The important point is that C++ does it for me, automagically.

So you can write a really efficient program in Python if you change the definition of what Python does (that's what you're doing by rewriting the interpreter)? I think you're moving the goalposts here.
Why would you need to change what Python does (i.e. the semantics of the language)? All I said was that you would find a subset of Python that is useful for your project, then put together a Python compiler that is very good at optimizing that subset. This is not at all unprecedented: Microsoft does this with the C compiler used for Excel and ITA software did this with Lisp when they modified CMUCL's code generation routines (CMUCL and the SBCL fork are particularly interesting here, as they expose their code generation modules to the programmer, reducing the effort needed to hack your compiler).

The point is that these things require a substantial time commitment, which is what I said in the first place. If you have unlimited time to work on your project, you can use whatever language you want -- your time is unlimited -- and still meet all functional and performance requirements.

Of course, time is rarely unlimited. Usually you need to meet a deadline, and that means that you need to prioritize, and your priorities will guide your language choice. In my experience, functional requirements are typically higher-priority than performance (with a reasonable margin). Getting the right answer slowly is usually better than getting the wrong answer quickly; getting all features to work is usually more important than producing a fast system that implements only half the features.