Settings

Theme

Why do C++ folks make things so complicated?

johndcook.com

36 points by prog 15 years ago · 33 comments

Reader

microtonal 15 years ago

If you want to make such a distinction, Qt is probably what would come close to top-C++. It offers dynamic binding via signals/slots, Java-like iterators, simple multi-threading/parallelization, signal/slot-driven networking, database access, and some automatic memory management (deletion via parent widgets, and auto-pointers). It only misses the proposed compiler warnings to indicate that 'bottom-C++' is used.

Still, for the average programmar 'top-C++' makes a miserable language. Want to write a simple class? You have to make a separate header file and decide what methods to inline/make virtual, etc. Want (binary) compatibility? You'll have to worry about things such as PIMPL and d-pointers.

It's a low-level language, and low-level issues will always leak into higher-level subsets. And why should one go through the effort? The old mantra "write in a higher language, rewrite what is too slow in C/C++" works fine.

  • blub 15 years ago

    Performance is a cross-cutting concern, you should factor it into the design of the application and not remember it when doing field tests and noticing that the software is too slow.

    This doesn't mean that you must use C or C++, but assuming that you can always rewrite the slow parts in C or C++ is inviting disaster.

    • jerf 15 years ago

      We all know the caveats on http://shootout.alioth.debian.org/u32/which-programming-lang... , but I would point out over the last few years, more and more languages are creeping up into the < 3x range on C. Go and Haskell I have some hopes that within a year or two they may well show up in < 2x, Scala's there, and Mono's putting up credible numbers in C# and F#. Given how much even "performance critical" code is still IO-bound nowadays, and how much not-IO-bound code is moving to the GPU anyhow, the age of "We have to use C++ for the performance" should be rapidly coming to a close. The last bit missing is just the people bit, now.

      I don't know what the first non-C practical OS will look like, but I hope I live to see it. I can't believe in 2011 my OS is still getting buffer overflows and memory mismanagement and all of the foibles that despite everyone loudly declaring is all the programmer's fault and not the langoage's fault, still seems to follow the language around like dog poo stuck on its shoe.

      • coliveira 15 years ago

        As long as memory management is not performed by the hardware, a C-like language will be needed to write operating systems. What I think is possible is to write more of the operating system in a higher level language, with only a small part of it in C. Or, even better, have a smaller operating system with everything else written in user space. This is the original idea of the UNIX designers, of course, so nothing really new here.

        • karolist 15 years ago

          I remember an attempt to write operating system in Python primarily, it was called "unununium", apparently the project died, but the site is still up http://unununium.org, though it now talks about ASM, not Python.

          A quick google finds this post mentioning a release http://objectmix.com/python/179041-unununium-os-0-1-rc2-rele...

          "implemented features include a fully functional Python interpreter, floppy, ATA, and ext2 drivers written entirely in Python."

          Would love to find source code for that actually.

    • jpablo 15 years ago

      Doesn't that go against all we have learned in CS? Early optimization being the root of all evil. And don't optimize what you think will be slow, but what you have measured to be slow?

      • lallysingh 15 years ago

        Not really. There are two sorts of optimizations:

        1. Structural - designing the top-level system to perform its work with minimal activity.

        2. Local - Calling conventions, component-local algorithms, etc.

        Now this is fuzzy for the same reason that one man's strategy is another man's tactics. But the gist is simple: you don't tighten the bolts on the system until you have it in the right shape.

        Local optimizations are easy to do b/c you can usually pull them out of a textbook, or figure it out by looking at one or to parts of code. Structural optimizations require an understanding of the system performance as a whole -- possibly allowing some suboptimal local behavior. Sadly the way I've mostly seen is super-tightening the bolts and having the system slowly distort from something suboptimal but flexible into a contorted, performs-ok-but-dont-touch-it terrible beast. That's the sort of optimization that's the root of all evil.

        Example: I have a webserver, with a table of regexs mapping to handlers. The handlers can be static assets in files or dynamically generated in code.

        Local optimization: I notice that after URL pattern match, my static assets are read through normal stdio file I/O. I use sendfile or whatever's the new hotness on my platform to make it fast.

        Structural optimization: I notice that the majority of my hits are static for a small number of files. I write a new webserver (say static.foo.com) for them specifically, that looks up the URL in a hash table for an in-memory copy of the file. I just ping it with SIGHUP when I want to invalidate the hashtable's values.

      • groby_b 15 years ago

        If we had learned something, we'd have learned the FULL quote, wouldn't we?

        "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil"

        Note, "small" and "97%".

        That means that you should make the right large-scale decisions before you code. Or measure.

        • jasonwocky 15 years ago

          Yes. Another way to put this is that there's a difference between "designing to be sufficiently performant" and "optimization".

          • groby_b 15 years ago

            It also means "don't be boneheaded about local performance". That's the other 3%. I wish it didn't need to be said, but there are quite a few projects I've seen where people blatantly ruined performance, all the while quoting Knuth.

    • cullenking 15 years ago

      With most modern languages having the easy capability of writing an extension in C, this is poor advice. You can 100% quickly mock up whatever in ruby, python etc, and when you need to, write a module in C that is plug and play. I have done this several times for calculating metrics on geodata. I wrote a ruby class to do the work, used it in production until we got a good enough amount of users (load), then rewrote it in C. My main code wasn't even touched since I had already abstracted the calculations into their own class. All this requires is you think ahead and pull out any heavy-lifting code into its own class, and you can just swap that out down the road.

  • Derbasti 15 years ago

    I have written applications both in Qt and PyQt and found out that in the real world, Pythons performance is more than apt for creating a UI.

    Even more so, the PyQt application had a NumPy backend and again, it performed flawlessly. The Qt application had a C++ backend that did similar computations as the Numpy backend, but with a much more painful programming process.

    So in that line of thinking, I am a big proponent of having a dynamic language as frontend for low level libraries. Both Qt and the Numpy core are plenty fast for what they need to do and having a dynamic language like Python as a frontend makes programming very convenient. The best of both worlds, really.

    I have never experienced Python being the bottleneck of my algorithms using these libraries.

cageface 15 years ago

C++ already contains "top" and "bottom" C++. You can write boost-style code, with heavy use of smart_ptr and STL algorithms & functors, or you can stay pretty close to the "C with classes" layer.

  • 16s 15 years ago

    That's the thing I like most about C++. I can do any type of programming in it (OOP, functional, procedural, templates, etc.). I can roll my own data structures or use the built-in STL containers while working with low-level C-like code. I'm not sure why so many people complain about it. Once I began using it, it quickly became my primary programing tool on Unix, Windows and Macs.

    • derleth 15 years ago

      "Beware the Turing Tarpit, where everything is possible but nothing of interest is easy." -- Alan Perlis

      C++ is, for many people, a Turing Tarpit: Ask yourself why you wouldn't write something in assembly, and that's why they wouldn't write it in C++.

      In specific, C++ doesn't have built-in support for real garbage collection. This means a lot of the idioms I take for granted in languages like Common Lisp and Haskell, to pick two very different languages that have true gc in common, are effectively impossible in C++.

      For example, I can't reliably pass complex data structures around using function composition (think f(g(h(x)))) because I can't rely on them existing from one function call to the next; an allocation may have failed somewhere up the line and now what? Adding in all the code needed to handle that isn't just a pain, it obscures the algorithm to the point I'm not programming the same way anymore. I'm not even thinking the same way anymore.

      Now for a purely personal anecdote: I quite like C. I can do certain things in C about as fast as I can think of them (mostly, things involving the POSIX API and no really complex data structures). I occasionally get the notion to add some C++ to my bag of tricks; not C-With-Classes, but C++ with Boost and the STL and whatever else g++ can handle these days. I might as well learn to swim by jumping in 500 feet upstream of Niagara Falls; there's "hitting the ground running", there's "hitting the ground doing back-flips and double-somersaults", and then there's "hitting the ground juggling sharpened sabres and gently convincing hungry wolves to not eviscerate you." Honestly, it wasn't this hard to learn how to write macros in Common Lisp. I don't know if the problem is just me or if it really is that bizarre in C++-land.

      • cageface 15 years ago

        C++ is the wrong choice for many applications, and I'll reach for a higher-level language if I can, but if you need the low-level control of C and need some tools for better abstraction C++ is your only real option.

mrich 15 years ago

C# comes to mind when he is talking about top/bottom C++. They got this right, plus added some neat high-level features.

  • JoachimSchipper 15 years ago

    Actually, the commenters are arguing about what languages are best used in those roles; e.g. C as "bottom C++", Python as "top C++" - or, indeed, C# as "Top C++".

    C# is nice, but I wouldn't want to write a kernel in it.

    • profquail 15 years ago

      C# is nice, but I wouldn't want to write a kernel in it.

      Why not? :)

      http://singularity.codeplex.com/

      When C# is combined with Code Contracts / static verification, you get fairly clean/performant code and strong safety guarantees. I think that'd be quite nice for kernel programming.

  • rch 15 years ago

    Not so sure about that... Consider the Template Numerical Toolkit: extending it would be Bottom while using it would be Top (reference counting, overloaded for for both row-major or column-major array access, etc.).

    C# takes things several steps further down the road.

    http://math.nist.gov/tnt/

    • eru 15 years ago

      By the way, why do C++ people seem to think that reference counting is a good idea?

      • Silhouette 15 years ago

        It is a useful idea, because it allows a certain amount of automation/defensive programming but without the complexity and potentially unpredictable performance of using a full garbage collector. For the kind of job where C++ is still a good choice of programming language, that might be the right balance.

      • arghnoname 15 years ago

        It's fairly easy to do in C++ with constructors/destructors and smart pointers.

      • gaius 15 years ago

        Because in some situations you can't tolerate GC pauses.

        • gigamonkey 15 years ago

          But you can tolerate ref counting pauses? (When you release the last reference to the last object keeping a large tree of objects alive and have a lengthy cascade of objects being freed.)

          • NickPollard 15 years ago

            If you use reference counting for a full tree hierarchy of objects, then you have to expect that.

            The point is that sometimes in C or C++ you have a small, flat selection of objects that you want to dynamically create and destroy, whilst everything else falls into fairly stable patterns than can easily be handled manually.

            An example from my field; computer games: It's quite common for game engines to use reference counting for resources such as textures. These days, it is very common for textures to be streamed into and out of memory during gameplay, either because it's a large game-world with whole sections being streamed, or simply for LoD (level-of-detail) reasons - objects in the distance have low resolution textures, when they get closer they become higher resolution.

            To handle this, it's common to have a pool of textures that use reference counting, and every model/renderable that uses a particular texture increments or decrements the refcount as appropriate. The texture itself is just a flat blob of data, so when a texture is freed, there's no worry of large cascading frees.

            This approach is very simple and easy to implement when you only want it on a small selection of the program.

          • sambeau 15 years ago

            Generally, that will still be far smaller than copying every object pointed to on the stack.

            • eru 15 years ago

              Maybe, but what does that have to do with the discussion? Only because there are copying garbage collectors, doesn't mean you have to use one.

          • rch 15 years ago

            When it matters, I'm usually keeping a pool of de-referenced objects around anyway, so I can just reinitialize rather than allocate... memory is explicitly managed, not blindly RCed/GCed.

          • blub 15 years ago

            If speed requirements are so tight you should preallocate the memory. GC is completely out of the question.

      • rch 15 years ago

        In TNT the goal is memory-efficiency. Maybe I should have said weak-reference assignment instead?

michaelfeathers 15 years ago

I think that the main reason that C++ is so complicated is because they've had to maintain compatibility with C's model. The declaration/definition and value/reference dichotomies double up a lot of work. When you add the fact that they wanted to base STL on pointeresque semantics (iterators) and people started using template meta-programming to deal with the lack of reflection, well, it all got complicated.

dedward 15 years ago

Simply a combination of it's nature - it's C++, and It's been around a long time. It's easy to make messy stuff with it.

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection