Skip to content

Instantly share code, notes, and snippets.

@alexpana
Last active September 17, 2024 06:47
Show Gist options
  • Save alexpana/91493a72b374651ab9ae to your computer and use it in GitHub Desktop.
Save alexpana/91493a72b374651ab9ae to your computer and use it in GitHub Desktop.
C++ is a hack

Various 'features' of C++ that show the hacky / inconsistent way in which the language was constructed. This is a work in progress, and currently contains some of the reasons I can remember why I've given up on C++. If you want to contribute, leave your favourite "hack" in the comments.

  1. (in)Visibility: C++ allows changing the access modifier of a virtual function in the derived class. Not only does C++ have no notion of interfaces, it actually allows subclasses to hide methods declared public in the superclass.

  2. Operator over-overloading: One of the increment operators takes a dummy int parameter in order to allow overloading. Can you tell which without googling? (hint: its postfix).

  3. Exception unspecifiers: C++ has two types of exception specifiers: throw() and nothrow. The first is deprecated (because 'we screwed up, sorry, let's forget about this terrible mess'). The second one guarantees it's contract by terminating the application when violated. That's because functions declared nothrow can still call legacy functions that don't declare anything (backwards compatibility and such), so the compiler can't validate your claim, choosing to explode in your face at runtime.

  4. Dependency hell: Without adding another layer of indirection via the PIMPL idiom, changes to the internal structure of the class propagate to all it's users, making compilation painfully / unecessary slow. Granted, the C++ object model requires this when the modified class is used as a value field inside another class, but C++ has no way of distinguishing between use cases. This is what you get for using a text preprocessor for handling dependencies.

  5. Binary incompatibility: Linkers are not standardised, so binary compatibility must exist between compilation units when linking. Not only are compilers incompatible, the same compiler used with different flags can generate incompatible obj files. Write once, compile everywhere.

  6. Multithreading: C++ did not have any standard multithreading implementation until C++ 11.

  7. RValue References: C++ has value semantics, which means it generates a copy constructor to deep copy value fields (convenient huh?). While the standard committe thought it was pretty cool, they eventually realised how many times objects were unecessary or accidentally copied behind the scenes (return values from functions, array operations, etc). So they invented 'move semantics', 'move constructors', and a new synthax for overloading on ephemeral 'rvalue references': &&.

  8. Name mangling: The C++ compilator was originally a preprocessor for C (CFront), and still maintains binary compatibility with C. C doesn't allow function overloading and doesn't have namespaces, so any function can be uniquely identified through its name (to avoid collisions, most libraries use a prefix when naming functions, such as gl*). Since C++ has function overloading, namespaces and classes, it allows multiple functions / methods to share the same name. The C++ compiler solves this problem by generating a unique identifier for each function. This is called name mangling, and uses the function's name, parameters, return type, namespace and class to create a unique name. The result is a horrible abomination that makes linker errors a pain to descipher. It's also non-standard, so every compiler has it's own name mangling scheme, making them incompatibile with each other. - https://isocpp.org/wiki/faq/mixing-c-and-cpp

  9. Inheritances: Since there are multiple access modifiers (public, protected, private), why not multiple inheritance(s)? There's even a virtual inheritance! While their meaning is beyond the scope of this rant, suffice it to say there are aspects that make them non trivial to understand / use. Scott Meyers wrote in his famous Effective C++ book: "protected inheritance is something whose meaning eludes me to this day". And he's a C++ guru.

@eclab
Copy link

eclab commented Feb 16, 2016

Nitpick: the word is "its", not "it's"

@diegoperini
Copy link

I respect all the criticism but I humbly believe that calling the entire language a hack is kind of misleading.

@lpcvoid
Copy link

lpcvoid commented Feb 16, 2016

I personally don't think a lacking multithreading implementation is a reason to disregard a language. How did that stop python from becoming large? Also, there are many well working multithread libs out there.

@alexpana
Copy link
Author

I respect all the criticism but I humbly believe that calling the entire language a hack is kind of misleading.

It's not misleading, it's outright wrong. It's also a metaphor.

@philmiller-charmworks
Copy link

Re Name Mangling: return type isn't actually part of the mangled symbol.

@alexpana
Copy link
Author

Re Name Mangling: return type isn't actually part of the mangled symbol.

While this would make sense, since the return type is not used for overloading, according to http://www.agner.org/optimize/calling_conventions.pdf it is (or was). It's non-standard, so any compiler is allowed to use anything, but I'm not up to date with the actual implementations.

@AkramAlhinnawi
Copy link

C++ allows changing the access modifier of a virtual function in the derived class.
This is not quite 100% two ways, you can only change from public to protected to private, you can't do the opposite. This gives you the flexibility to hide certain data/method members, which is, of course a powerful tool if you know how to use it and when to use it.
Not only does C++ have no notion of interfaces
This is also not true at all. I think this will a mislead the reader. There is a structure called pure virtual class where you use it in an inheritance hierarchy when it makes since to do it. It's exactly as an interface in Java or C#. You can't instantiate an object of it, but instead you can instantiate a pointer that points to any object to it's subclass or children and it's widely known and used in polymorphism where the creation of methods is done in the run-time not in compile time, it's the father of delegation in Java and C#.
I don't want to go to replay for each and every single point. But long story short, nothing can replace C++, especially in coding ICs, OS, device drivers like VGA, motherboard ...etc. drivers. powerful game engines.. etc. and a lot of implementations that basically needs the flexibility, low level programming and full control over the devices.
Basically, If you know what you're doing, and you want the most powerful control of the devices, network streams, VGA, graphics, sounds.. etc.. you have to go with C++. I started with C like 23 years ago, then I used Java, VB and now I'm C#. C++ is the most addictive, flexible, powerful lang. you can ever used, but not the easiest one. Like Spiderman movie: with great power, comes great responsibility. This is what my Prof. of C++ started his first session in the beginning of my C++ course and I still remember it. LOL
Hope this is useful buddy.

@alexpana
Copy link
Author

But long story short, nothing can replace C++, especially in coding ICs, OS, device drivers like VGA, motherboard ...etc. drivers. powerful game engines.. etc. and a lot of implementations that basically needs the flexibility, low level programming and full control over the devices.

I can't speak for windows, but linux device drivers are written in C. Also, I'm waiting for Rust to mature so we don't have to trade performance to maintainability.

@alexpana
Copy link
Author

Not only does C++ have no notion of interfaces

This is also not true at all. I think this will a mislead the reader. There is a structure called pure virtual class where you use it in an inheritance hierarchy when it makes since to do it.

From the perspective of a C++ compiler, a pure virtual class is in no way different from any other class. You can make an 'pure abstract' class in Java, but that doesn't make it an interface.

@mudpedal
Copy link

I personally don't think a lacking multithreading implementation is a reason to disregard a language. How did that stop python from becoming large? Also, there are many well working multithread libs out there.

When saying standard, you cannot bring other libs such as boost::thread into the argument. While there are great non standard implementations, not having a standard specification for such an important thing really humbles me. I think the article doesn't disregard the language, it just ilustrates bad points about it.

@jrziviani
Copy link

What is the real reason behind? Are you trying to convince yourself that the problems you have been facing with C++ are on the language only? Are you trying to identify problems to suggest changes? what is it?

There is no perfect language, no perfect company, no perfect country, no perfect person. It's possible to write flaws about anything as well as advantages. But, what is the point?

@bel1k0v
Copy link

bel1k0v commented Feb 16, 2016

I'm not using C++ right now and for a long time - i'm working with web technologies. But don't you think that every program may be a little hacky. Had you even write hacks in your code? I suppose you did. IMHO C++ features one day became a requirement from other programs. Remember that C++ is programming language, but first of all - it is a program.

@alexpana
Copy link
Author

What is the real reason behind? Are you trying to convince yourself that the problems you have been facing with C++ are on the language only? Are you trying to identify problems to suggest changes? what is it?

What's the reason of life? Is there any absolute reason for anything? Should I feel obliged to come with solutions just because I'm presenting problems?

I might use this list the next time someone asks me why I'm not recommending C++ (or even prove myself not to turn back). I might show it to someone who brags they know everything about C++ and it's an easy language. Making a list of things wrong or funny about something isn't meaningless, it's comedy!

@Pomax
Copy link

Pomax commented Feb 16, 2016

C++ is the hack to end all hacks: it's such a good one that it's still firmly relevant today, and overcoming a frustration over "but why do ..!? It's idiotic given what we know/do today" lets you participate in amazing projects, and can land you fantastic jobs.

So if I might ask: why is this list a work in progress? We all know C++ is insane, there's literally books on just the subject of C++ quirks, so why duplicate information everyone already has access to, other than out of personal spite against something that has no feelings? (C++ is just an inert spec, and while it might tick off less experienced C++ developers, anyone who's actually serious about C++ will look at this list and go "yup, and then some" and then go back to using C++ anyway)

@azm-gh
Copy link

azm-gh commented Feb 16, 2016

For beginners, it would be nice to give examples so it becomes also educational piece. Otherwise I see no reason why you should not keep your notes about C++ quirks public.

@FrankHB
Copy link

FrankHB commented Feb 17, 2016

  1. This is not a hack. Almost every language other than C++ does wrong. The visibility (of identifiers, not symbol visibility, the latter is related to linkage) is purely concerned with scoped names, that is, it has nothing to do with whether a name is denoting a function or not. So it has nothing to do with overriders. There is essentially no room to talk about virtual at all. This design allows the language specification split the name resolution rules in general (in C++ they are name lookup rules) away from the mess of inclusion polymorphism, which depends on particular subset of the type system (often badly designed for so-called OO languages). OTOH, languages like Java may probably have more obscure rules here. (What is the heck of "shadowing" vs. "hiding"?)
  2. Yes, this is an unintuitive hack. But if you don't want strangeness on not allowing overloading of postfix operators, you have no better choice. This is the sin of father (the C language). Think twice: why providing postfix ++?
  3. You've got it wrong. There is throw (ugly half-way copy of Java's throws) as well as noexcept (not "nothrow"). The reason of the "hack" is you cannot force every implementation perform inter-procedural optimization unconditionally without a standard of intermediate representation. (This is also one reason of why export is awkward.) Will you bother to put everything in headers?
  4. The hack is the standardization itself. It only specified (what is a) standard-layout class without any portable way to extend it.
  5. This is not related to C++, but almost all "native languages". There is even no single ABI standard covering all major platforms for one architecture. For C++, one notably de facto standard is the Itanium C++ ABI used by G++ and Clang++ broadly, but it is not compatible with "standard" MS toolchains on Windows.
  6. This is inaccurate. The correct one is: C++ has no threading model until C++11. (Nevertheless, so does C, until C11, which copies a lot from C++11.) You can still use pthread or some other platform-specific APIs for your C++98/03 programs.
  7. This is not a hack. A hack is to emulate the feature in C++98/03, as AA did in his "mojo". However, using of some special rules depending on it is a hack. The xvalue is a hack. Standalone (not migrated into the type system) value category (with some insane rules of reference types) is a hack. This is essentially the sin of forefathers again. In C, standalone lvalueness is a hack. In the B language, lvalueness as a grammatical feature is a hack.
  8. This is again not related to the language itself. C++ does not requires it. However, almost all practical implementations do in this way. Languages without standalone standardization process will be worse, e.g. Rust. It is not significant for these languages because they are lack of multiple competitive implementations. This is not the guaranteed future.
  9. Providing inheritance but not the general way to specify partial ordering among types is a hack. However, providing MI itself is not. On the contrary, lack of MI lead to ugly hacks when implementing mixins. Virtual base is not hard to understand and to use, you just have too many risks to use it wrongly, and it may cause ABI or performance issues which need you to use other hacks.

@hansderhase
Copy link

C++ is not the perfect language. It's designed to certain goals and practical considerations. Just like any other language. If the desgin goals don't fit your needs chose another language. You can't have it all in one language. That's a fundamental principle of language design. It's that simple.

Most if not all the "hacks" you list are rooted in the design goals of C++.

Glance over http://www.stroustrup.com/hopl2.pdf if you want to learn about how/why C++ came to be.

BTW: C++ perfoms pretty good in the language arena for a "hack", don't you think? Maybe there's more to the practical relevance of a language.

@agauniyal
Copy link

I would respectfully ask you to compare latest versions of c++ language to your choice of language for fair comparision. Otherwise it seems you're desperately trying to convince others to pick a language of your choice.

@Findecanor
Copy link

I would not say that C++ is a hack, but I would say that the raw language together with the standard library encourages a style of programming that indeed does consists of "clever hacks". Only that many of those hacks are not called "hacks" in the C++ community - they are called "idioms".
Programming should not be about clever hacks. Having to decipher existing code written by programmers who thought they were clever gets in the way of getting stuff done.

@FrankHB
Copy link

FrankHB commented Aug 30, 2017

@Findecanor Idioms have nothing to do with hacks. Hacks can live in any layer of a detailed design, while idioms are quite limited. A featured use of the language is an idiom when a) it identify a typical solution of some well-known problems, and b) the common implementation techniques required are not "dense" enough to be a pattern. Idioms are intended, by design. There are so-called anti-patterns, but almost no "anti-idioms".

As op shows, it is doubtful that average programmers (who only have experience on programming, but not designing of the language) have sufficient judgement of hacks in a language. Many features (e.g. pointers in C++) are essentially hacks in a general-purposed language, but users often ignore such facts. To complain against probably existed "hacks" in their minds does not get the stuff done at all. Having to decipher existing code written often clearly shows there is something wrong, but still not enough to attribute it to the language.

@alexpana
Copy link
Author

I would respectfully ask you to compare latest versions of c++ language to your choice of language for fair comparision.

Unfortunately, however shiny the latest version is, there are still lots of legacy codebases using old versions. Not to mention any C++ version is still compilable with the latest compiler. People still write C++ like it's 1990 today. There's just more ways to do the same thing or worse, to apparently do the same thing, but instead do something else. See https://herbsutter.com/2013/05/06/gotw-1-variable-initialization-or-is-it-310/

Otherwise it seems you're desperately trying to convince others to pick a language of your choice.

I really don't care what languages people are using, I'm not advocating for any other language. I'm just making a list of all the quirks I dislike about C++

@rsn8887
Copy link

rsn8887 commented Apr 13, 2018

This is a great list showing how hacky, crufty, inconsistent and kludged together c++ really is. And it makes it easy to just direct people here instead of explaining it every time. Thanks!

@fgeeyhd
Copy link

fgeeyhd commented Jul 25, 2022

ed

@fgeeyhd
Copy link

fgeeyhd commented Jul 25, 2022

``356436

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment