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?

15 of 757 comments (clear)

  1. 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?

  2. Re:How much is it C++ and how much the compilers? by RailGunner · · Score: 3, Informative

    A better developer who understands what it is he or she is trying to accomplish would help more.

    I'd say I've seen just as much piss poor code written in Java and C#, but that'd be a lie. I've seen FAR worse code written in Java and C# than I have in C++ because the level of entry is much lower.

    When I was in college, not understanding pointers means you didn't graduate. Now... I interview "C# Developers" who claim 10+ years experience who can't tell me the difference between pass-by-value and pass-by-reference, and don't know the difference between the heap and the stack.

  3. 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?

  4. C++ is hard by Prien715 · · Score: 4, Informative

    C++ was the first popular fast OO language. As such, there's a lot of confusing cruft left behind. Consider overloading the && operator or || operators. You should never do this*. But someone will come along and do it anyway. You can't get rid of the feature because of backwards compatibility and yet it's miserable. We can go down the list from polymorphic arrays to calling virtual functions during constructors. All things one should never do, but the language keeps them there for the sake of backwards compatibility.

    Languages like Java fix some of these problems by explicitly not allowing operator overloading (which is heavy-handed) but enforces some readability.

    As others have said, using good 3rd party libraries like Qt makes the language tolerable, but in the legacy applications I've supported, there's no shortage of programming faux pas made possible by the language (like assumptions about the order of static variable destructors -- which is compiler dependent). As a programmer, it can be fun and productive since simply using the better parts of the language can make programs easy to write and read. As a maintainer, it's a smorgasbord of bad programming practices which the language makes no attempt to prevent.

    That said, Linus really likes the new version of Subsurface based on Qt. So there;)

    * Scott Meyers More Effective C++ p.35

    --
    -- Political fascism requires a Fuhrer.
  5. 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.
  6. Re:depends upon what you're making by swilly · · Score: 3, Informative

    I haven't read Linux's rant against C++ for a while, but he is correct that C++ isn't a good choice for an OS kernel. The only major kernel written in C++ that I know of is Windows NT, and it uses only a subset of C++ language features. In particular, it disables exceptions, disables RTTI, removes new/delete, and it doesn't have the standard library. Microsoft wrote their C++ compiler with this in mind, and there is a compiler flag to disable kernel unfriendly features (documented here). For everyone else, it's easier to just say that the C++ subset for kernel development is C (minus the standard library).

    For non kernel use, C++ is superior to C in the hands of an expert programmer, but mediocre programmers who don't understand the language tend to write absolutely horrible code. And you can't take an expert C or Java programmer and expect them to write expert C++ code with just a few weeks practice. C++ is one of those languages that you have to dedicate a lot of time to, but it can be worth it if you require highly optimized code, have low latency requirements, or have low space requirements (areas where higher level languages like Java don't do well).

  7. Linus was full of it by Anonymous Coward · · Score: 2, Informative

    Languages run the gambit from script kiddie interpreted languages that were designed for children to cut and paste examples from the internet to esoteric languages meant to be used only by doctoral students doing research. C++ is a nice middle ground that requires some expertise to use correctly, but offers immense power if you take the time to develop that expertise. I've written 3d accelerated GUIs with it, I've written mathematical algorithms with it, I've written real-time embedded applications with it. All to great success. Of all the languages I've encountered in my 14+ years as a software engineer, C++ by far covers the broadest spectrum of possible applications.

  8. Re:Write-only code. by Carewolf · · Score: 3, Informative

    Sounds like that goes back to the "lack of standard ABI" problem. C lets you specify exactly what you want explicitly, but C++ has no consistent ABI, you get different results depending on which compiler you use.

    Not anymore. At least not on Linux. Intel wrote a C++ ABI for IA64 (Itanic), since it was the first of its kind it got adopted to a defacto ABI for all other architectures as well. This is what g++ has been using for over a decade, and what every other compiler is mimicking to stay g++ compatible.

    Even a failed architecture can sow the seeds for good things in the open source world.

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

  10. 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."
  11. Re:depends upon what you're making by jcr · · Score: 4, Informative

    The only major kernel written in C++ that I know of is Windows NT,

    Apple uses a subset of C++ known as "kernel C++" for drivers in OS X and iOS. Back in the NeXTSTEP days we used Kernel Obj-C, and it's very sorely missed.

    -jcr
     

    --
    The only title of honor that a tyrant can grant is "Enemy of the State."
  12. Re:Write-only code. by spitzak · · Score: 4, Informative

    The sample code will copy a and b twice, once to put them in the lambda closure, and then to pass them as arguments to do_something. Some may consider this wasteful (the easiest fix is to modify do_something to take the values as const references).

    This is the general problem with C++, in that the shortest code is often the slowest. Adding const references to the declaration of do_something would remove the useless extra copy. The slowness is visible in C, where you would probably allocate a structure containing the copy of a and b, and have pthread_create call a function that copies them to the arguments to do_something and calls it, then deletes the temporary structure. This is equivalent to what the C++ compiles into but all the inefficiency is visible. This is the primary complaint about C++ verses C.

    PS: I use C++ all the time and prefer it over C. But you do require a good knowledge of what it turns into, often stated in a pseudo-C, to figure out how and why things work and why slight variations (try using [&] instead of [=] in your lambda!) break in horrific ways.

  13. 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;
    }

  14. Re:Write-only code. by goose-incarnated · · Score: 3, Informative

    (Don't bother responding if your code can't meet all of the boldface conditions...

    Challenge accepted, see here for my first go that satisfies all the bolded conditions. Any further conditions you impose now will make you look even more like an ass than you already do for posing such a fanboyism challenge. Note well: there are literally only four lines of code to achieve this - I posted the other lines to give the readers some context.

    struct foo_t {
    size_t one;
    size_t two;
    };

    void *do_something (struct foo_t *array)
    {
    printf ("%zu, %zu\n", array[0].one, array[1].two);
    }

    #define THREAD_CREATE(fptr, type, a, b) do {\

    // Start of challenge
    type array[] = { a, b };\
    pthread_t tmp;\
    pthread_create (&tmp, NULL, (void *(*) (void*))do_something, array);\
    pthread_detach (tmp);\

    // End of challenge, you're welcome
    } while (0)

    int main (void)
    {

    ...
    struct foo_t first, second;
    ...
    THREAD_CREATE (do_something, struct foo_t, first, second);

    ...
    ...
    }

    in the real world, you can't simplify the system requirements to meet the deficiencies of your coding language)

    As you can see above, no deficiency found - in the actual real world, as opposed to the one populated by inexperienced fanboys like yourself, the above solution would not be discarded from the solution set if it meant changing the codebase from C to C++.

    You can now be an adult about the whole thing, accept that what you can do in C++ can be done in a readable manner in C as well. I don't think you will, though - I have a feeling you'll come up with another idiotic "C++ can do this, can C do this?" question, at which point I will once again be happy to smack you in the face with a solution like I did just now.

    Consider yourself schooled.

    --
    I'm a minority race. Save your vitriol for white people.
  15. 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