Modern C++ is a very different language though it can still almost completely interoperate with quite old code. C++11 and C++14 already addressed most of the things brought up, and contemporary C++ (obviously most code is not in it yet) even supports generic template functions with a straightforward syntax (i.e. use auto instead of <..>).
Being generous:
* There's now `std::string_view` to address some of the problems with `std::string`, but the rest are still there. There are some attempts to specify the encoding now, at least.
* Lambdas and `std::function` pretty much solve the function pointer complaints, with some added complexity.
* Containers still do silly things when you use `c[..]` syntax with no element there. (Both when trying to insert and when trying to retrieve!)
* The general level of language size and complexity, especially around templates, has only gotten worse. Concepts will finally help in some ways here.
The author, after a long and dubious appeal to authority, claims that:
* C++ exceptions are outright evil and should be avoided
* C++ string class "is so utterly godawfully stupid" because it has no garbage collection or refcounting, and in short doesn't precisely match Python's implementation. He also felt personally insulted by the fact std::string is a template class.
* He feels C function pointers are fine but believes function pointers in C++ weren't extended to support pointers to member functions, which he then backtracks and says they actually exist but they are "so horrendously ill-conceived that absolutely nobody uses it for anything", thus showing his ignorance and lack of experience.
* For some reason he criticised std::map for the way it's std::map::operator[] returns a reference to the mapped value, eventhough that's what it is supposed to do by design and by the principle of least surprise.
* The author made other claims, but the text grows so much in the style of "foaming from the mouth" that it's just better to stop reading after a point.
In short, this article is just a rant where someone vents that a programming language other than Python is not Python at the eyes of the author. It's a pretty unconvincing rant and full of logical and rational holes, but a rant nonetheless. So the author loves Python and his personal take on C++ does not match his view of Python. That's fine. It's just weird how this sort of text floats up in a technical forum after over a decade after it has been published, as if it's expected to add or say anything of value.
FWIW, I find std::map operator[] creating an object extremely convenient and it is very annoying having to use defaultdict to get the same behavior [edit: in python]. So ugliness really depends on what you are used to.
> The general level of language size and complexity, especially around templates, has only gotten worse. Concepts will finally help in some ways here.
variadic templates and type deduction greatly help. A lot of complex template metaprogramming is suddenly not needed anymore (I've used boost::mpl a lot in the past, but not once in the last 10 years). constexpr functions also helped get rid a lot more use cases. Finally if constexpr made a lot of sfinae hacks obsolete.
(In old man voice) We ended up rolling out own stuff and marking std:: verboten. Why? Stl was too slow, too verbose, and too hard to grok the stack when debugging. We ended up with less memory fragmentation, less dangling ptrs, etc etc. In the rare case there was something in STL that was actually cool (or faster, which was very rare), we'd gut it and reef out the part that was cool to use in our implementation.
I presume these comments aren't popular (sorry about that, but this is during 90s and early 00s when dev cycles were clearly different). Eg. We had string classes in all different flavours, some would interop, some wouldn't. Eg. We had tree and hash classes that, while templateable, had a few core implementations that made compilation fast. We had various ptr management systems (ref counted, stack based, etc).
We made STL between verboten in all APIs because we been burnt so many times using (/trying to use) other ppls APIs that exposed STL in its library. (We were exclusively a windows shop, if that helps understand my confusion... PS. I'm retired these days and have been out of the c++ game >10yrs)
This is surprising for Java devs, but given that value semantics must be supported, there's no way to avoid it.
In that respect, the author probably wouldn't be happy with modern C++, either. For example, C++ is never going to be a garbage-collected language (by default, at least). Modern C++ gives you better tools to deal with that, but the core design concerns that make C++ the way it is haven't gone away.
”Garbage collection (automatic recycling of unreferenced regions of memory) is optional in C++; that is, a garbage collector is not a compulsory part of an implementation. However, C++11 provides a definition of what a GC can do if one is used and an ABI (Application Binary Interface) to help control its actions.”
Whether that feature is a good idea or not and to what degree, is the question.
You can write your code in modern c++ and ignore prehistoric carbuncles (some of which have been deprecated and eliminated FWIW). And you can call external code written in older dialects without having to look into its source code.
This is not what happens in the real world though.
While I agree that you have to deal with whatever is in your codebase at a given point, it also doesn't imply that you have to use everything and that everything can be useful for your project.
As standards keep coming and features get added, it's still increasingly prevalent to see guidelines and awareness around the topic of choosing your own subset of C++ to work with.
The main drawback is mostly that it incurs a cost in terms of brain power to discipline yourself to keep your work within a restricted set of language and standard library features.
It's extremely rare (and even debatable) whether a single person masters all the aspects and features of C++ (and if there's one, it's probably Alexandrescu).
That makes as much sence as claiming that to develop software in, say, Python on the real world you have to learn and deal with all of its standard library.
That is not the case. That has never been the case ever for any programming language, be it large or small. Specially in C++, where since it's inception the standard approach is to, at best, adopt a subset of all features and stick with it.
Maybe C++30 will have fixed everything...
If not, then... the above statement does not stand.
Before I started refactoring I decided to make everything 'less ugly' by moving it to C++17. Which would also help me get back into the code, after eight years.
I spent two days on this. Then I decided: fuck it. I will RIIR™.[1]
It will mean that it will take me at least two months to get a beta of the product with feature parity vs. maybe a week to port the old codebase to the new API.
But on the other hand the C++ code is littered with stuff that 'just works' but can explode in your face and which eventually needs some safety net written around it. Which will likely take at least two months to do properly.
The truth is: after one and a half years of Rust C++ feels painfully ugly.
For context: I started with C++ with Borland's TurboC++ (moving from C) when I was 15. I used C++ for almost 30 years by now. It's about time.
[1] Yes, I did read http://adventures.michaelfbryan.com/posts/how-not-to-riir/
using Callback = std::function<int(int)>; // or whatever
Callback cbk = [&](int i) { return instance.method(i); };
I've also seen some real evil hacks that rely on casting 0 as a pointer to a class and relying on the ABI's spec for vtable layout to calculate the pointer of the function as an offset from the `this` pointer. Because that's easier to remember than the syntax for pointer-to-member functions.Using various bind equivalents just requires you to know what you're doing:
boost::function<int(int)> cbk = boost::bind (&SomeObject::some_method, instance_of_some_object);
...
cbk (22); // invoke callback
In addition, the author's complaints about nobody using ptr-to-method is absurd. Even in 2010, anyone using libsigc++ or its (few) equivalents was using them, which meant that any GUI app written using GTKmm was full of them. What's not to love?
Callback cbk = std::bind_front(instance, Instance::method);
in C++20. I'm going to ignore std::bind as it has weird conventions. :)Here's the D<->C++ intercompatibility project: https://wiki.dlang.org/Calypso#Current_status
the irony is that in 2010, many std::string implementations were in fact reference counted (including libstdc++). This was generally considered a major mistake (because it doesn't work well with threads and when it does is a major performance pitfall) and prohibited in C++11.
https://twitter.com/apenwarr/status/1232848468156256256?s=21
[The [] operator] is an absolute
failure of engineering!
Do you want to know what real
engineering is? It's this:
map_set(m, 5, "foo");
char *x = map_get(m, 5);
So like map::insert and map::at? Did these not exist in 2010?This is the power and achilles heel of Lisp as well.
Mindshare - Lisp has its zealots. C++ is tolerated rather than adored, because it's more of a Katamari Damacy of stray CS than a language with a coherent focus.
How many other languages have a Turing complete sublanguage built into them just to handle templating?
> But in python, it works perfectly (even for
> user-defined types). How? Simple. Python's parser
> has a little hack in it - which I'm sure must hurt
> the python people a lot, so much do they hate
> hacks - that makes m[5]= parse differently than
> just plain m[5].
>
> The python parser converts o[x]=y directly into
> o.setitem(x,y).
The name for this is "generalized variables", at least in Lisp land. The idea is to allow complex assignment left-hand side (LHS) expressions and turn assignments into calls to setter functions, including whatever complex data structure traversals might be needed.Lisp has generalized variables via "setf macros", which turn assignments into the right set of calls to setter functions. Setf macros do this at compile time and generically for any getter/setter functions that have been registered with a bit of ceremony.
(Lisp also has destructuring-bind, which lets you write a data structure with variable symbols in it such that the corresponding variables will be bound to the data find in the corresponding places of a real data structure value. The two features, destructuring and generalized variables, are similarly magical.)
jq can do crazy generalized variable assignments like '.a[].b[0] = 1', but this is a run-time thing. (The LHS is evaluated in a context that allows only "path expressions", and in a reduction, while the RHS expression is run in the body of the reduction to update the input value at each path matched by the LHS.)
Icon implements generalized variables by letting procedures return "places" -- references to assignable locations --, so you can assign to the return value of procedures. This may seem quite surprising when you see it, but it works beautifully.
If you ever design a language you should hope that it gets popular enough that people bitch about it this hard. That’s a real triumph.
> If you've heard anything about C++, you've probably heard that there's no standard string class and everybody rolls their own.
Is this still true? std::string seems perfectly reasonable now, especially now that they've given up on supporting ropes and integrated the small string optimization. Yes it doesn't specify an encoding.
> No garbage collection? Check. No refcounting? Check. Need to allocate/free heap space just to pass a string constant to a function?
Nothing else in the standard library is garbage collected or refcounted by default. Why would std::string be the lone exception? You can opt into refcounting for any type using std::shared_ptr.
The objection about allocating/freeing heap space is about APIs that take const std::string& being passed C-string literals. Legitimate complaint, but it's addressed by std::string_view now.
> ...rant about lack of []= operator...
[] mutating the map does surprise people, so that's definitely a legitimate complaint. And it is annoying to have to use find() in a const context...
but simple operations like counting are simpler and more efficient in C++ than in Python because the +=, -=, etc operators work:
for value in list:
counts[value] = counts.get(value, 0) + 1
vs for (auto& value : list)
counts[value]++;
> So actually C++ maps are as fast as python maps, assuming your compiler writers are amazingly great, and a) implement the (optional) return-value optimization; b) inline the right stuff; and c) don't screw up their overcomplicated optimizer so that it makes your code randomly not work in other places.This is just comical. Python is not playing in the same performance league as C++, regardless of whether inlining or RVO happens. RVO of course is a general optimization for returning objects by value from functions, not a special case to optimize setting map items. It's still relevant, but less important since C++11's move semantics.
"You can't make C++ beautiful, but you must try."
I don't see why not? You'll have to decide the ownership semantics if they're dynamically allocated but that's it?
Not ugly is more of a less strong "pretty" and can't not is more of a "should" (rather than "can"), so the title can be read as "you can't make c++ pretty, but you should try (to make it pretty)".
I would rather use C++ because i would have the option to use abstractions like std::map, std::string, std::vector, std::any etc. as they would save a lot of time and code complexity.
IMHO the worst thing about using C/C++ is getting other libraries to play with your project well even if you are using cmake or vcpkg, its not enough. You have to have solid knowledge of each operating system class to get everything working nicely over the long-term.
That depends on how you write it. It’s possible to write C++ that bears no semblance to C whatsoever. (I have done so for effect; I once assigned some students to write a C program but wanted to provide a reference implementation with source so I gave them one in C++ that wouldn’t translate directly at all.)
Why would you ever want this? Gives me Java nightmares.
(I wonder how many of these posters are real or fake. Given the karma and post of this poster I'm inclined to... wait. Last comment was in 2017. Yet... hasn't too many posts since that, I think it's about 4. Probably a bit out of the loop, buy maybe not malicious.)
Please see https://news.ycombinator.com/item?id=22429925 below.
[...] Please don't post insinuations about astroturfing, shilling, brigading, foreign agents and the like. It degrades discussion and is usually mistaken. [...]