Slashdot Mirror


Was Linus Torvalds Right About C++ Being So Wrong?

Nerval's Lobster writes: Perhaps the most famous rant against C++ came from none other than Linus Torvalds in 2007. "C++ is a horrible language," he wrote, for starters. "It's made more horrible by the fact that a lot of substandard programmers use it, to the point where it's much much easier to generate total and utter crap with it." He's not alone: A lot of developers dislike how much C++ can do "behind the scenes" with STL and Boost, leading to potential instability and inefficiency. And yet there's still demand for C++ out there. Over at Dice, Jeff Cogswell argues that C++ doesn't deserve the hatred. "I've witnessed a lot of 'over-engineering' in my life, wherein people would write reusable classes with several layers of inheritance, even though the reusable class wasn't actually used more than once," he wrote. "But I would argue that's the exception, not the norm; when done right, generic programming and other high-level aspects of C++ can provide enormous benefits." Was Linus going overboard?

21 of 757 comments (clear)

  1. Write-only code. by mellon · · Score: 5, Insightful

    The problem with C++ is that it's way too easy to write write-only code, because the language has so many features that nobody but language experts understand all of them. So we all program in different dialects, and then scratch our heads when we read other peoples' code.

    1. Re:Write-only code. by Anonymous Coward · · Score: 5, Interesting

      Maybe that's what it is. I write a lot of C code, some of it pretty good, but I just can't "get" C++. I appreciate the class stuff. Heck, I pretty much do the same thing using structs in C but past that, the rest of C++ just loses me.

    2. Re:Write-only code. by Carewolf · · Score: 5, Insightful

      The problem with C++ is that it's way too easy to write write-only code, because the language has so many features that nobody but language experts understand all of them. So we all program in different dialects, and then scratch our heads when we read other peoples' code.

      Less so than C, especially as used in the kernel. Seriously read some of the Linux kernel and compare it with any good C++ project. The kernel loses BADLY. The manually implemented virtual classes are not pretty and not type safe, and neither are all the ugly macros needed to do things that would safe, automatic and easy to read in C++.

    3. Re:Write-only code. by mellon · · Score: 5, Interesting

      You can find some really elegant C++ code out there. I am quite fond of the Qt libraries, for example. But Qt is its own C++ dialect. The company I work for codes a lot of our software in C++, and it is really nice, clean, maintainable code. But we have a style guide that everybody has to follow, and that's how we pull it off. Essentially, we are not writing in C++. We're just using a C++ compiler to compile NomLang.

    4. Re:Write-only code. by KermodeBear · · Score: 5, Insightful

      The same applies with Perl. And PHP. And Java. And Go. And Ruby. And Python. And Javascript.

      I've also seen good C/C++, PHP, Java, Python, etc.

      You can write crappy convoluted code that uses odd, unusual frameworks and features in every language. It isn't the fault of the language - it is the fault of the people (ab)using it.

      --
      Love sees no species.
    5. Re:Write-only code. by Anonymous Coward · · Score: 5, Interesting

      You can find some really elegant C++ code out there. I am quite fond of the Qt libraries, for example. But Qt is its own C++ dialect.

      Ditto on Qt. Qt can be a beautiful thing sometimes... but then comes pre-compile, which turns it into an anus-puckering nightmare for those who don't know you're using it ;) .

      The company I work for codes a lot of our software in C++, and it is really nice, clean, maintainable code. But we have a style guide that everybody has to follow, and that's how we pull it off.

      That should be the norm in any competent shop, though.

      Mind you, I've seen shops where styles are horribly mixed-up. That usually happens when Company A buys Company B's codebase and nobody cleans up the stuff from Company B. OTOH, I've seen instances where different teams in the same company decide to become special snowflakes and go their own way, making life into a giant shit sandwich for DevOps, QA, or SCM to eat - especially when they have to point to something and say "...fix that, because it's breaking Jenkins/Tito/unit tests/etc... and WTF man, we have artifacts for a reason!"

      But then, I just came out of a Java shop where they were using Geronimo, Jetty8, Jetty9... Java 1.6 and 1.7... all in the same 'effing overall product (just that the components sit on different servers.)

      Long story short, it takes discipline to keep everyone rowing in the same general direction - enforced styleguides are a part of that. Discipline also keeps things from becoming an unmanageable mess, if the head coder has the spine for it.

    6. Re:Write-only code. by Darinbob · · Score: 5, Informative

      Right now I'm doing just C and assembler (and the occasional scripting in something else). Sometimes it feels a bit archaic compared to a stripped down C++ style I'm more used to, but on the other hand there just aren't as many political fights over style so it's less stressful. The interesting thing about C is that newer standards don't tend to lots of new and experimental features, which is sort of the norm with C++. C tries to be stable.

      Also a big part of my job when doing C++ was decoding the obtuse error messages for coworkers, and that almost never happens with C...

    7. Re:Write-only code. by Rei · · Score: 5, Interesting

      STL and lambda are my main reasons for using C++. They're bloody awesome.

      Here's my standard challenge for C people - I've given it many times in these sort of threads and not once gotten a real response that meets the specs. Show me the code (emphasis: show me actual code, don't just say "... this is how I'd do it" and a rough description), full code (emphasis: full) for a program launching a detached (emphasis: detached) thread, such that it can happen an arbitrary number of times with no guarantee that other threads will be done (emphasis, there can be more than one thread at a time), to run the function do_something(Foo a, Bar b) (emphasis: two arguments, arbitrary size) - where the values passed for a and b are variables local to the context that launches the thread (emphasis: local), so they need to be passed by copy, not reference.

      This is not at all some sort of esoteric task - launching threads with nontrivial local arguments is pretty basic, there's millions of use cases for something like this. Here it is in C++11:

      std::thread([=](){ do_something(a,b); )).detach();

      Little short line of code. Surely for such an obvious, non-esoteric task, C can't be much harder, right? Any takers?

      (Don't bother responding if your code can't meet all of the boldface conditions... in the real world, you can't simplify the system requirements to meet the deficiencies of your coding language)

      --
      "Are you hungry? I haven't eaten since later this afternoon." -- Primer
    8. Re:Write-only code. by spitzak · · Score: 5, Informative

      The sample code will copy a and b twice, once to put them in the lambda closure

      Only without optimization flags enabled. Otherwise the lambda will be inlined in most cases.

      BZZT! Wrong! Think: what happens if a and b are local variables and the function creating the thread returns before the lambda runs? They must be copied to somewhere that is not destroyed by the caller returning. That copy is not in the correct location because they are created before the thread stack, so another copy is unavoidable. The only way to fix it is to make do_something take const references. Though it is true that if do_something was inline it would probably fix it.

      Show me the code

      Yes it is ugly and I never claimed otherwise. The problem is that C++ compiles into the equivalent of this and it is hidden behind the scenes. Here is is pretty obvious that I must not pass a pointer to a or b, not so clear in C++:

      #include <pthread.h>
      #include <stdio.h>
      #include <malloc.h>
      #include <unistd.h>

      void do_something(int a, int b) {
          printf("do_something %d %d\n", a, b);
      }

      typedef struct {
          int a, b;
      } lambda_args;

      void* lambda_run (void *p) {
          lambda_args* v = (lambda_args*)p;
          do_something(v->a, v->b);
          free (v);
          return 0;
      }

      void thread_do_something (int a, int b)
      {
          lambda_args* v = (lambda_args*)malloc(sizeof(lambda_args));
          v -> a = a;
          v -> b = b;
          pthread_t thread;
          pthread_create (&thread, 0, lambda_run, v);
      }

      int main()
      {
          thread_do_something(1, 2);
          thread_do_something(3, 4);
          sleep (1);
          return 0;
      }

    9. Re:Write-only code. by Rei · · Score: 5, Informative

      BZZT! Wrong! Think: what happens if a and b are local variables and the function creating the thread returns before the lambda runs?

      BZZT! Wrong yourself! And let this be a lesson to you to not be so haughty when replying. What you describe never happens. The lambda is built synchronously, but called asynchronously.

      To prove it, try out the following code:

      #include <iostream>

      void do_something(int a, int b) { std::cout << a << b; };

      int main(int argc, char** argv)
      {
      int a = 12, b = 23;
      ([=](){ do_something(a,b); })();
      }

      Compile it with "g++ --std=c++11 -S -O4" and check out the .S file (compare it to the -O0 version too). In the -O4 version you'll see the following:

      main: .LFB1262:
      leal 4(%esp), %ecx .LCFI6:
      andl $-16, %esp
      pushl -4(%ecx)
      pushl %ebp .LCFI7:
      movl %esp, %ebp
      pushl %ecx .LCFI8:
      subl $12, %esp
      pushl $23
      pushl $12
      call _Z12do_somethingii

      You see that? It's pushing the local values directly onto the call stack. There is no intermediary step for the lambda.

      Okay, so maybe my test case was too simple? Let's complicate it.

      #include <iostream>
      #include <stdio.h>

      void do_something(int a, int b) { std::cout << a << b; };

      int main(int argc, char** argv)
      {
      int a, b;
      scanf("%d", &a);
      scanf("%d", &b);
      std::cout << 34;
      #if 0
      ([=](){ do_something(a,b); })();
      #else
      do_something(a,b);
      #endif
      }

      Try it again with -O4 with the if set to 0 or 1 and compare the two versions together. You'll see that they're exactly the same. In both cases, main is:

      main: .LFB1262:
      leal 4(%esp), %ecx .LCFI6:
      andl $-16, %esp
      pushl -4(%ecx)
      pushl %ebp .LCFI7:
      movl %esp, %ebp
      pushl %ecx .LCFI8:
      leal -16(%ebp), %eax
      subl $28, %esp
      pushl %eax
      pushl $.LC1
      call scanf
      popl %eax
      leal -12(%ebp), %eax
      popl %edx
      pushl %eax
      pushl $.LC1
      call scanf

      --
      "Are you hungry? I haven't eaten since later this afternoon." -- Primer
    10. Re:Write-only code. by serviscope_minor · · Score: 5, Insightful

      because the C++ standards committee got a bee in its bonnet about the latest hot new concept that first came out in 1959 and was forgotten until last year

      Give one example of that ever happening. The C++ standards committee is notoriously conservative which is why the language evolves so slowly. This comes across as you just making shit up when you haven't got a clue.

      The problem with possessing multiple ways to solve a problem is that every developer takes it as a personal challenge to find and use all the different ways.

      That might be *your* problem but it isn't mine. Every decent place (staffed by true scotsmen of course) with project teams has things like coding standards and code reviews.

      If your developers are committing crap code to the mainline then the trouble is that (a) your developers and (b) your process sucks. The thing is it will suck in any language.

      The library is here: Extended C library, libxc, so help yourself (BSD license)

      OK, I'll bite.

      Well, if you think having XVEC_DEREF(some_vector, i, float) for resisable arrays but array[i] for builtin ones is better than having array[i] for both, well, then I guess we're just on completely different pages here.

      The thing is there's massive syntactic overhead to your code. And the overhead is more like 1000% not 150%.

      vector<float> = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9};

      works perfectly in C++, whereas that's 10 lines of C code!

      And I was wondering how it worked underneath, and I had to dig into the code. So you're storing pointers to the elements, not the elements themselves! I never even realised that. Wow, so that means you can't even do:

      xvector_ins_tail(tfar, &1f);

      You have to do:

      some_float_which_has_the_same_scope_as_tfar_and_is_never_reused = 1;
      if(!xvector_ins_tail(tfar, &some_float...))
      { //Do something error related here //probably a longjmp or goto. //Don't forget to free all that memory!
      }

      versus:

      tfar.push_back(1.5);

      It also means you're in nightmare memory management from hell since you now have to manage all those bits of memory by hand.

      It *also* means that you have no memory locality since it's a big array of pointers, so performance will almost certainly be bad as well.

      The equivalent C++ code to your xvector/main.c is something like:

      vector<float> sfar = {1.1, 2.2, ...};
       
      for(const auto& f:sfar)
        cout << f << endl;
       
      eg_t eg[] = {...};
       
      vector<eg_t> teg, sub;
      for(const auto& e:eg)
        eg_t.push_back(e);
       
      for(auto e:teg)
      {
        e.i += 2;
        e.f -= 5.0;
        sub.push_bach(e);
        cout << "Returning " << e.i << " // " << e.f << endl;
      }
       
      //Well, the nativea bit is basically already working in the C++ code since internally, std::vector uses a native array

      And that gets us to line 90 or so. There's more, but you missed the error checking for if something isn't found in the xvector. It also took me a while to verify that all the allocations are matched by the respective free. It would be nearly impossible to be sure for less trivial code.

      So, I'm not convinced you've made a case for C over C++ here.

      --
      SJW n. One who posts facts.
  2. Almost got me by halivar · · Score: 5, Insightful

    I always get halfway through a Nerval's Lobster summary before my anger/indignation/smug validation gives way to the sad realization that Dice has trolled me yet again.

  3. Full Disclosure by mu51c10rd · · Score: 5, Informative

    Could we stop having Dice articles submitted by Nerval's Lobster? Why not fully disclose that the story was submitted by the corporate parent of Slashdot?

  4. Yes, he was wrong... by RailGunner · · Score: 5, Insightful

    If you care about performance, the ability of C++ to "run on the iron" is a valuable tool to have in your arsenal. Add in inline assembler, and IF YOU KNOW WHAT YOU'RE DOING you can write blazing fast code in C++ and still provide a sensible code architecture.

    There's no sense in blaming the language for the abuses developers have written -- you might as well indict English for the horrible spelling and grammar of many Americans...

    If you know what you're doing, C++ is a terrific, powerful language suitable for a plethora of projects. On the other hand, if you don't know what you're doing, well, I guess there's Visual Basic or C#.

  5. Re:How much is it C++ and how much the compilers? by david.emery · · Score: 5, Interesting

    Bad programmers can produce bad code in any language, including one as well/thoroughly specified as Ada. The difference, though, is that what that code actually does is less subject to interpretation by the compiler.

    I've observed that two Ada programmers will argue, "Is this program legal?" If the program is legal, they both -know- what the compiler will do (modulo the rare compiler/optimizer bug, which was usually caught through the stringent compiler validation.)

    Two C++ programmers will argue, 'What will my compiler do with this code?"

  6. Re:No by boristhespider · · Score: 5, Insightful

    A clickbait article about a flamebait rant, commented on by trolls.

    God bless Slashdot.

  7. Mod parent up by amaurea · · Score: 5, Informative

    I just looked through Nerval's Lobster's last 15 contributions. All were article submissions, Nerval's Lobster doesn't appear to comment on anything. Here's the list:

    • Was Linus Torvalds Right About C++ Being So Wrong? [Dice]
    • Do Tech Companies Ask For Way Too Much From Job Candidates? [Dice] [Hiring]
    • 'Chappie': What It Takes to Render a Robot [Dice]
    • Demand for Linux Skills Rising This Year [Dice] [Hiring]
    • Who's Afraid of Android Fragmentation? [Dice]
    • H-1B Visas Proving Lucrative for Engineers, Dev Leads [Dice*] [Hiring]
    • In Space, a Laptop Doubles As a VR Headset [Dice*]
    • What Does It Mean to Be a Data Scientist? [Dice]
    • Which Freelance Developer Sites Are Worth Your Time? [Dice*] [Hiring]
    • JavaScript, PHP Top Most Popular Languages, With Apple's Swift Rising Fast [Dice*]
    • Building a Good Engineering Team In a Competitive Market [Dice*] [Hiring]
    • What Makes a Great Software Developer? [Dice*] [Hiring]
    • The Highest-Paying States for Technology Professionals [Dice] [Hiring]
    • What Will Google Glass 2.0 Need to Actually Succeed? [Dice*]

    Every single one of them is from dice, though only a few of them actually make that explicit (the non-explicit ones are marked [Dice*]. A large fraction of them are related to human resources and hiring people, which I've marked [Hiring]. So its like Nerval's Lobster is using Slashdot as advertising and recruitment channel for Dice.

    The average quality of these submissions was very low in my opinion - lots of vacuous pointy-haired-boss buzzword stuff. Very un-nerdy. How did these get through submission moderation? Were they even subjected to it?

  8. Syntax and typo errors compile by goombah99 · · Score: 5, Informative

    C itself has so many pitfalls. For the best tour review the underhanded C contest. "features" like automatic concatenation of consecutive character strings means that if you leave out a comma in a list, the adjacent array element entries are concatenated rather than throwing a syntax error. That list will now not match the declared array size (one short, so there's a null or random pointer in the last element) but the compiler allows initialization listed mismatched to the array sizes. Character strings have to be declared one longer than the initialization string length (room for the unstated \0) but are accepted by the compiler if they don't giving an unbounded string length.

    it's mind boggling to realize that
    int (*int)[20];
    int *int[20];
    are different things.

    the number of different ways an array argument in a function can be written makes code hard to grasp: is it a pointer, an array, a reference? many work alike but then fail in different ways.

    The most common of all pitfalls and hard to read codes are the in-line initializations that pop up in function arguments and what not. this leads to classic blunder of writing = when you mean ==.

    Perhaps the most insane thing is that If you declare an external function with the wrong prototype then any mismatch in the argument count leaves or takes something off the stack. Holy cow..... I mean what the hell? Why would any language ever ever ever let you leave a orphan argument on the stack, or worse pop one off that was not yours? This is very useful for the underhanded C folks however.

    While I know there's little love for fortran, it's worth noting that none of those things is even possible in Fortran, so its an existence proof that there's not any necessity for those to exist and that it doesn't limit the power of the language to remove them. It's very fair to say that no simple typo will ever compile in fortran (yes very complicated offsetting typos can compile).

    --
    Some drink at the fountain of knowledge. Others just gargle.
  9. STL by SoftwareArtist · · Score: 5, Informative

    You can argue about whether C++ is a horrible language (I lean toward "yes") in itself, but the libraries are what really push it over the edge. STL is hands down the worst collections framework I've ever encountered. Consider just a few examples of how you do some common operations with it, compared to doing the same things in Java and Python.

    1. Check whether a string s ends with a suffix t.

    Java: s.endsWith(t)
    Python: s.endswith(t)
    C++: s.rfind(t) == s.size()-t.size()

    2. Check whether a collection c contains an element e.

    Java: c.contains(e)
    Python: e in c
    C++: c.find(e) != c.end()

    3. Split a string s into tokens based on whitespace.

    Java: s.split()
    Python: s.split()
    C++: ... do you really want to know? Ok, check out http://stackoverflow.com/quest.... There you will find dozens of proposed solutions (many of them quite indecipherable), along with lots of debate about which one is best. The top voted solution has a comment on it (with several hundred votes) saying that it's a bad solution and you shouldn't use it.

    Doing even really basic, common operations with STL requires way too much work and produces absurd, hard to read code.

    --
    "I'm too busy to research this and form an educated opinion, but I do have time to tell everyone my uninformed opinion."
  10. Re:Aren't all (but one) popular languages like thi by Rei · · Score: 5, Interesting

    Honestly, I find a random program written in C to be on average FAR less maintainable than one written in C++, usually because they end up reinventing the wheel about 50 times, usually poorly. The C program that I work on at work is one gigantic mass of poor wheel reinvention over and over again. Its impersonation of objects and inheritance (for sending message) is terrible, utterly terrible, it's almost impossible to build and send a message without messing up in some way due to all of the interconnected pointers. The macros they use to try to "simplify" it only make it worse. Some parts of the code have macros nested literally dozens of levels deep.

    --
    "Are you hungry? I haven't eaten since later this afternoon." -- Primer
  11. Re:Casting by Dutch+Gun · · Score: 5, Insightful

    Modern, properly designed C++ code is absolutely safer than C code. However, because C++ is a superset of C, you can obviously write code that's just as unsafe as C, simply by ignoring the best practices and writing "C with classes" (which many do). A lot of what you can do in C++ exists solely to provide backward compatibility, both with earlier versions of itself as well as with C.

    C++ gives you the ability to create new types using objects, which you can operate on both through member functions as well as logical operator overloading (where it makes sense to do so). For instance, you could create a class for handling file paths (as opposed to using raw character pointers or arrays, or even C++ strings), and when that class is properly developed and debugged, you can then be confident that you no longer have to worry about accidentally creating a security vulnerability or introducing a crashing bug. Moreover, it can handle path-specific things, such as ensuring proper form when paths are concatenated. Even better, when compiled down, it's really no different than code written in C, since C++ still adheres to the "zero-overhead" principle for most features.

    When people talk about C++'s "dangerous casts", they're almost universally taking about "C-style casts", which are discouraged in modern C++. Instead, you should use the more explicit casts, which either use static compile-time checking or even run-time checking as appropriate. Whenever you have to resort to a C-style cast in C++, you had better have a *very* good reasons (in many cases it's just a design failure). Nowadays, that also includes managing raw memory or raw pointers thanks to the addition of standardized smart pointers.

    This is why C++ is almost universally used in the videogame industry (I work as a videogame programmer), because is strikes a reasonable balance between safety, advanced language features, and performance. It's also nicely compatible with C libraries, with which we often have to interface at the OS level, or when using 3rd party libraries. And finally, while "better" alternatives arguably do exist, C++ is also well supported and extremely ubiquitous across the industry. As the saying goes, "quantity can have a quality all it's own". This is important when trying to hire experienced developers, or looking on the web for solutions to a problem.

    --
    Irony: Agile development has too much intertia to be abandoned now.