
C++ is a hack - rootlocus
https://gist.github.com/alexpana/91493a72b374651ab9ae
======
cubano
I've been involved in this debate, professionally, for some 28 years now, and
while I don't claim to have any deep or unique insights into it, I would like
to state that I find almost amazing that it still rages at pretty much the
same level as it did back in 1988.

As I've mentioned before, my first professional gig was writing real-time
games and business apps for the MajorBBS, a fabulous pre-WWW multi-line BBS,
in C.

Since the MajorBBS DevKit was shipped as C source and compiled as protected-
mode DLLs (to of course overcome the 640k memory limit of DOS...yeah that was
a thing at one time), there were several devs that wanted to use C++ "goodies"
to help speed development or whatever.

The debate started when Galacticomm, the makers of the MajorBBS flat-out
refused to consider it and _used many of the same rational as the OP listed_
as reasons why it was not going to ported.

Reading over this list jogged many old memories, which is why I felt compelled
to write this rather long-winded post.

My local opinion? Use whatever gets the job done and can be maintained and
supported at a reasonable cost long after you are gone.

------
athenot
C++ is a multi-paradigm language. Of course that is going to be messy. There
are domains of applicability where you want everyone to use the same model,
the same patterns, the same paradigms. That yields true benefits for some
teams.

But there are other areas where you really want to have a Big Bag Of Tricks to
get things done, because they are areas where efficiency or fine-grained
control are required or it just plain makes sense.

I should note that most successful C++ projects I've come across seem to only
use a subset of what that Big Bag O' Tricks provides. Yet in some corners, if
need be, some specialized trick can be used because it makes sense there. And
it ends up being much less of a hack than if it had to be done in the mono-
paradigm language.

~~~
lfowles
I appreciate it being multi-paradigm as well. There are 3-4 languages in there
with _great_ interoperability. As well as the ability to pull in a C library
to a project with little fuss.

------
giancarlostoro
It is indeed an attempt to extend C while maintaining backwards compatibility,
which can be a bit of a mess on it's own. This is probably one of the main
reasons that spawned D's creation, a language that does what C++ "should have"
done and breaks backwards compatibility while still maintaining the 'power' of
C++. I guess calling it a hack is acceptable, at least to me, maybe not to C++
developers though who feel it is more than just a hack.

Edit: Grammar correction.

~~~
ATsch
I feel like I have some kind of obligation to correct you on the common error
of "should of" instead of "should have" or "should've"

[http://www.elearnenglishlanguage.com/blog/english-
mistakes/s...](http://www.elearnenglishlanguage.com/blog/english-
mistakes/should-have-vs-should-of/)

~~~
giancarlostoro
Thank you, I appreciate the link even more. This is my second language, and I
appreciate any opportunity to improve my writing, even though it is my second
language I've learned it is still my main language I use.

------
thinkpad20
Isn't name mangling a standard thing done by just about every language except
for C that compiles to object code? The lack of standardization between
compilers sounds potentially problematic, but name mangling itself doesn't
seem like a bad thing. The alternative is to force the programmer to do their
own "name mangling" a la C.

~~~
JoeAltmaier
Name mangling is a sad old way of using antiquated object formats while moving
forward with a language. Better solutions are obvious - a new object format
that stores constraints/annotations/scope info would be a good one.

------
exelius
Ok -- but I don't think anyone legitimately holds on to C++ as an example of a
great, well-designed language. C++ has existed for a long time, and the
corporate world has increasingly moved _away_ from C++ because it doesn't
offer enough object-oriented goodness while tying you down to a certain memory
management style with a difficult to use typing/inheritance system.

Pick any modern OO language: Java, Ruby, Swift, hell, even Python. They are
all far easier languages to write in than C++, have much better library
support, and don't make you dive into memory management hell.

IMO C++ is syntactically very difficult, and the language too far out of touch
with the architecture patterns and styles used by programmers today.

~~~
LanguageGamer
C++ is a mess, but Java, Ruby, Swift and Python aren't viable options if your
software is performance sensitive.

~~~
austinsharp
Java is pretty viable if you are performance sensitive but not HFT or video
game performance sensitive. It's certainly far ahead of Ruby and Python, at
least.

It's not as fast as C++, but C++ isn't as fast as assembly, either. They're at
different points on the tradeoff curve - HOW performance sensitive are you?
It's not a binary choice.

~~~
exelius
Java is totally viable for nearly anything; it just doesn't work well for
games because they require low-level access to graphics hardware (which is
definitely not something Java supports).

Also, the state of the JVM on the desktop is kind of sad - also from the
graphics perspective. But Java is probably the most performant server-side
language out there at this point (if you exempt the masochists who write
network code in C/C++).

~~~
albinofrenchy
Java isn't viable for anything that requires low latencies and predictable
throughput. Really, no GC language is.

------
pcwalton
Many of these are legitimate, but a couple aren't:

\- "Dependency hell"—Yeah, it's annoying for sure. But what's the solution?
You can't reasonably have value types and ABI stability in the presence of
adding and removing fields from those value types: it's impossible.

\- "Name mangling"—The legitimate complaint here is that the name mangling
schema is not standardized (though Itanium is a de facto standard in the open
source world). But the fact that name mangling exists isn't something you can
blame C++ for: it's really Unix that deserves the blame for not supporting
namespaces in their object files.

~~~
rootlocus
Object files are not standardized either (as far as C++ is concerned).
Microsoft has it's own format. I can blame C++ for using a hack in order to
keep using the same obj files as C.

------
usefulcat
"[C++] actually allows subclasses to hide public methods from the superclass."

That one doesn't even make sense. A base class (superclass) in C++ never knows
anything about any of its descendants. So it can only ever know about methods
declared in the base class, and what their visibility is in the base class.
Secondly, it's true that a derived class could (for example) override a public
base class method and make it private in the derived class, but that won't
change the ability of the base class to call that method.

~~~
lfowles
I actually tested this one since I had never encountered it in the wild. You
can indeed call and get the expected results from the derived classes (now
private) member function.

~~~
slavik81
Just to be clear, that's not in conflict with what the author claimed. You
can't call that method using a pointer to the derived class. That would be an
access violation, because it was declared private. You need to use a pointer
of the superclass type.

    
    
        struct I_A {
          virtual void foo() {}
        };
        
        struct A : I_A {
        private:
          virtual void foo() {}
        };
        
        int main() {
          A a;
          // a.foo(); // compile error
          I_A& ia = a;
          ia.foo();   // ok
        }
    
    

To be honest, I find this behaviour useful so it doesn't upset me. It's a
decent way to mark off functions that you needed to implement to satisfy an
internal interface as not-for-users.

~~~
lfowles
Question: Is struct inheritance public by default?

~~~
slavik81
Yes. If A were a class, I'd have to specify `public I_A`, but since it's a
struct, that's the default.

------
ansible
My team is about to embark upon a C++ project. The compiler will be gcc 4.8.x,
and supports C++11. So I've bought Stroustrup's "C++ Programming language 4th
Edition", and Scott Meyer's "Effective Modern C++".

I've got some reading to do.

What's the state of tools related to encouraging / enforcing modern C++ style?

Edit: grammar.

~~~
jjuhl
You should check out

clang-tidy: [http://clang.llvm.org/extra/clang-
tidy/](http://clang.llvm.org/extra/clang-tidy/)

clang-format:
[http://clang.llvm.org/docs/ClangFormat.html](http://clang.llvm.org/docs/ClangFormat.html)

C++ Core Guidelines:
[https://github.com/isocpp/CppCoreGuidelines](https://github.com/isocpp/CppCoreGuidelines)

C++ Core Guidelines Checkers for VS 2015 Update 1:
[https://blogs.msdn.microsoft.com/vcblog/2015/12/03/c-core-
gu...](https://blogs.msdn.microsoft.com/vcblog/2015/12/03/c-core-guidelines-
checkers-available-for-vs-2015-update-1/)

~~~
ansible
Awesome, thanks.

------
dman
C is a hack. x86 is a hack. Unix is a hack. Windows is a hack. Products with a
"do what it takes" attitude appear to outrun projects driven by purism in the
short to medium term. Often its too late for a better designed product to
overcome the early traction lead of such projects.

Richard Gabriel has captured many related issues in a series of essays with
far greater eloquence than I am capable of -
[https://www.dreamsongs.com/WorseIsBetter.html](https://www.dreamsongs.com/WorseIsBetter.html)

------
lallysingh
Some of these aren't quite true anymore. There's a C++ x64 ABI, for example.

Still, it's all a big mess.

~~~
angelbob
Did they finally get templates shoehorned into it? That's been the giant,
glaring flaw in all the previous ABI attempts I've seen.

Well, that and getting multiple compilers to use the same name-mangling
standards.

------
amelius
CSS is a hack too. [1]

[1]
[https://wiki.csswg.org/ideas/mistakes](https://wiki.csswg.org/ideas/mistakes)

~~~
nommm-nommm
Yeah, but nobody likes CSS or thinks its well designed.

~~~
pcwalton
I think CSS is overall well designed, though with plenty of flaws. It's
remarkable, though, that everyone agrees that it's bad, but every proposed
replacement I've seen has been significantly worse.

Those mistakes are legitimate complaints. But it's interesting that most of
the complaints about CSS do not fall under that list.

------
rusabd
There is much larger and older list:

[http://yosefk.com/c++fqa/](http://yosefk.com/c++fqa/)

------
pklausler
I've used C++ for many major projects, and as a compiler writer, I understand
the language at a high level.

C++ requires an unprecedented degree of organizational discipline to use
successfully. For most projects, in most organizations, it's a terrible
technological choice -- bloated, inconsistent, dangerous, poorly understood,
and still rapidly changing.

The programming world needs a language with some of the features of C++
(strong typing, easy interoperation with C, polymorphism, destructors, and
control of memory ownership). But it also needs a language that doesn't
perpetuate every single language design flaw that has accumulated since the
Nixon administration while dramatically changing in ways that alter "best
practices" across revisions of the language standard and too frequently take
steps backward in readability.

And I disagree that C++ is "evolving". A process isn't really evolution unless
old things are dying.

------
aback
All third-generation programming languages (and higher) are leaky
abstractions. Why is C++ worse than others?

------
georgehaake
Well if hack is the polar opposite of elegant, then C++ is a hack indeed.

------
kazinator
Misguided right off the bat:

> _Not only does C++ have no notion of interfaces, it actually allows
> subclasses to hide public methods from the superclass._

C++ doesn't have "methods"; it has member functions (static or nonstatic).
Nonstatic ones can be virtual. Virtual member functions are what corresponds
to OOP methods.

To use virtual functions as methods, you call them through a base class
pointer or reference to an object. That's when you're doing OOP dispatch.

Now a derived class in C++ can define member functions of the same names as a
base. These functions lexically shadow the ones in the base. If you use
derived objects as their actual derived type (not through a base reference),
then you're using those functions. This is "statically clear" in a static
language, and it makes sense. If I open up "program.cpp" in my text editor and
edit some function so that a "Foo f" object is replaced by a "DerivedFoo f",
then I want all occurrences of f.func() to now be calling DerivedFoo::f rather
than Foo::f, if DerivedFoo::f exists. I don't want the compiler to complain
that the situation is ambiguous, and I don't want it to keep calling Foo::f,
which is nonsensical.

Lastly, access is orthogonal to visibility in C++; it is not accurate to say
things like "allows subclasses to hide public methods". Identifiers declared
in the derived class scope simply shadow like-named definitions in base
classes, period. That is regardless of access. Access specifiers do not alter
visibility (or any program semantics). If you manipulate the access specifiers
in a C++ program, at best you can make some diagnostic go away, or introduce
the need for one. All permutations of access specifiers which don't require a
diagnostic will behave the same way (and should produce identical code,
really).

> _One of the increment operators takes a dummy int parameter in order to
> allow overloading. Can you tell which without googling?_

Yes, I can tell without googling as can any reasonably competent C++
programmer. Why would you need to answer the question in this form? The
feature is there for when the situation arises that you're writing an
overloaded ++ operator and would like it to be postfix. If you don't care
about overloading ++ or making it postfix, you don't have to be aware that
there is an int hack for this.

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

The class declaration being fully visible to all places which use it allows
C++ to be efficient in its handling of data types. For instance, when you have
a local variable "FooClass x", the compiler knows exactly how large x is down
to the byte and can give it a fixed-size place in a fixed-size stack frame. It
also allows for inline functions which compile to code that accesses the
object directly.

This low-level nature is a conscious design trade off in C++, which makes it
what it is. If you want to eliminate the dependencies, you use that PIMPL
idiom explicitly. In some higher level languages, the equivalent of PIMPL is
implicit everywhere. (For instance, everything is a "heap object"). C++ is one
language you can reach for in situations when you don't want everything to be
a heap object, yet still have some halfway useful abstractions.

> _C++ did not have any standard multithreading implementation until C++ 11._

That was five years ago. C++ users did threading in C++ code for at least
twenty years before that, in platform-specific ways, or using cross platform
frameworks like ACE. Because C++ doesn't hide a lot of behaviors under the
hood, you can let platform threads loose directly into C++ code and the
effects are predictable and manageable, for the most part, modulo issues like
"does unwinding happen if the thread dies" and such.

> _The C++ compilator was originally a preprocessor for C (CFront), and still
> maintains binary compatibility with C._

This point is also inaccurate. C++ has to live in a world with _linkers_ and
_object file formats_ which do not support type information on a symbol. That
is why name mangling is required.

If you want three flavors of a function called foo, with separate external
linkage (rather than, say, some internal runt-time dispatch behind a single
function) then you need three separate entries in the symbol table.

In most object file formats, that means you need three different symbol names.

It has nothing to do with C++ originally having been preprocessed, or
compatibility with C. C itself is simply compatible with assembly language
linkage models. (And so C has failed to promote any improvements in object
file formats and linkers, by imposing no special requirements.)

Some other languages have to do name mangling in their implementations, to
some extent. I used a Modula-2 compiler in the late 1980's which used a
"module$ident" scheme in the object file format. That is to say, function foo
in module bar was turned into the symbol "bar$foo" with a dollar sign.

~~~
pcwalton
> Yes, I can tell without googling as can any reasonably competent C++
> programmer.

As far as litmus tests of "reasonable competence" in C++ go, "having the
signature of every overloaded operator memorized" is among the worst I can
possibly think of.

> If you don't care about overloading ++ or making it postfix, you don't have
> to be aware that there is an int hack for this.

... And it's still a hack.

~~~
kazinator
Overloaded operators do not have "canned" signatures that you have to
memorize.

You generally design the signature. Some are unary and some are binary (or
either), but you pick the argument types as well as return value.

The increment and decrement ones (++ and --) have a hack whereby an extra
_dummy_ argument of type int indicates "this is postfix" and isn't actually an
argument which is making the operator binary! The argument isn't given a name.

Anyone halfway knowledgeable in C++ can be expected to know this piece of
trivia, IMHO.

It's a huge hack, but the argument that it's something hard to remember that
you have to Google for doesn't hold water.

~~~
pcwalton
> Anyone halfway knowledgeable in C++ can be expected to know this piece of
> trivia, IMHO.

I disagree. Let me put it this way: You will not increase the quality of your
C++ programmer hires by quizzing them on whether they know whether prefix or
postfix gets the extra argument, or even how to overload those operators at
all.

~~~
kazinator
Only in the sense that someone who doesn't know C++ well could be a much
better quality developer than someone who does.

If someone put C++ on their resume but doesn't know how to overload operators,
they are failing on _honesty_.

------
vvanders
Maybe, but a very useful hack.

------
autoreleasepool
> 6\. Multithreading: C++ did not have any standard multithreading
> implementation until C++ 11.

How is expanding the standard library a hack?

------
meirelles
It's a great language showing aging signs. Most languages become obsolete
before becoming old.

~~~
angelbob
I learned C++ around 1990. It was a hack then, too. It began life as a weird
preprocessor for C, and some of the seams from that beginning have shown the
entire time.

------
diegoperini
"Why I think C++ is a hack" is a more appropriate title. The stated arguments
have legit points though.

~~~
sevensor
"It's a hack" is also an insufficient reason to avoid using C++, although I
think it's both true and a useful thing to know before getting involved with
C++.

~~~
austinsharp
The very nature of a hack is that someone needed it at some point. Even if C++
is a hack, that doesn't prohibit it from being the right answer in some
situations.

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

\- Bjarne Stroustrup

~~~
stinos
Basically, this!

Most of the OP's points are valid, no denying that. But, looking at the
myriads of properly working, fast, not 'exploding in your face at runtime',
often even beautiful code produced in C++ despite these shortcomings: doesn't
really seem like a poblem if one knows how to deal with it.

~~~
pcwalton
> But, looking at the myriads of properly working, fast, not 'exploding in
> your face at runtime'

Does remote code execution due to use after free fall in the category of
"properly working" and "exploding in your face at runtime"? I would think it
does, and C++ is vulnerable to that like few other languages are.

~~~
dragontamer
Why are you using "free" in modern C++ code?

That was mostly put in there as a transition from C. Modern C++ users
shouldn't be using new / free at all, since shared_ptr (and weak_ptr, its
counterpart) and unique_ptr basically cover all the use cases.

~~~
maccard
I don't think the parent meant free as in "free()" \- more along the lines of.

    
    
      Foo f;
      DoSomething(std::move(f));
      // can still use f here.

~~~
dragontamer
But if "Foo" was composed of unique_ptr (or shared_ptr), then those smart
pointers would be nulled out after the std::move (due to the default move
constructor)

So again, unless Foo f was composed of "raw pointers", even that kind of code
is safe from "use after free" vulnerabilities.

