Is there any reason to use C++ instead of C, Perl, Python, etc.?

  • As a Linux (server side) developer, I don't know where and why should I use C++.

    When I'm going for performance, the first and last choice is C.

    When "performance" isn't the main issue, programming languages like Perl and Python would be good choices.

    Almost all open source applications I know in this area have been written in C, Perl, Python, Bash script, AWK or even PHP, but no one uses C++.

    I'm not discussing other areas like GUI or web applications, I'm just talking about Linux, CLI and daemons.

    Is there any satisfactory reason to use C++?

    I presume "server-side" in this context means headless services, and not the kernel itself?

    I only consider C++ because of STL.

    Yes, just about CLI shell apps.

    I learned C++, because the best job offer I received required it.

    So something that can be done using C, perl and python together can be done using C++ only. And you are asking why to use C++?

    « When I'm going to performance, the first and last choice is C. » yeah sure :D This is an unproven, and trivially wrong assertion.

    @deadalnix: I wouldn't say that. C++ has complex rules that might backfire on optimizer, because it's not allowed to do some things. And it's super easy to step into invisible performance killers. It's pretty much axiomatic, and therefore true :D Still in reality C++ code will sometimes be faster because you'll be using more effective algorithms and data structures, and noone actually optimizes C code anyway. So when done correctly, C++ is safer and more effective C, and you should pick C++ over C when there are no compatibility problems, or requirement for 100% availability software.

    The best reason, not considered in the posted answers, directly relates to OP's question. DEPENDANCIES!!!!, Not that your average system lacks the c++ libs, but an embedded system might not have them available. The only way to to get your program in every system, is to write your program in regular C. Everything else is just debating why you should, or less represented, should not use C++. None of that addresses why C++ is not used more often, and regardless of merit, the reason is dependencies.... O and also Linus's famous c++ rant.

    I found C++ sometimes better for command-line utilities than python because it's a lot faster in both startup time (if the binary has relatively small number of libraries, but it may become similar to python if it's linking a lot) and processing time (e.g. when there are hundreds of millions of records to process). However for lightweight tasks or generation of web-pages python is definitely better.

    @TechZilla: Uhm, why should I want to get my program into _every_ system? For what definition of "every" is that? Oh, just recently I did some C code for Arduino. Guess what: Some dependencies per the standard were not there. Your argument is therefore unclear and/or BS.

    @phresnel: You're asking why should *you* want something? Why should I need to tell you what you'd want?! As for your feckless post-modernist deconstruction, "every" was clearly referring to GNU/Linux Systems. OF which can be expected to support POSIX C. Arduino is a microcontroller, the expectation would no longer be reasonable.

    @TechZilla: Because "every system" is superhero rockstar BS. It's impossible, unrealistic, inprofitable and unresearched. That's why I asked for the "why". And: If "GNU/Linux Systems" was "clear" or obvious, can you tell me where you _clarified_ that? Clearly, you said "every system", not more, not less. Be more precise, and refrain from reacting like an impudent kid that uses terminology out of its control.

    @phresnel: Only your own deconstructed definition is nonsense, once you add the context of the OP's question ... "As a Linux (server side) developer" . Every clearly means every "Linux" system, and yea consider that researched, check kernel.org for my sources. Amazing how your sociology class teaches you to stop understanding how the working public communicates.

    @TechZilla: Unfortunately, your line of argumentation becomes void if you add the second part of the statement you quoted: "As a Linux (server side) developer, **I don't know where and why should I use C++.**". And, what is a "deconstructed definition"? And, why are trying to impose a badass dude on me? I bet in Real Life you're a friendly dude that couldn't kill a fly.

    @phresnel, I never stated that I could answer the OPs question, this was/is a comment and not an answer. So now we've now concluded my mere consideration, may be reasonable depending on individual circumstance? Your interpretation of who you believe me to be, is irrelevant to my original consideration, I never asked others to share my concerns. I simply mentioned a concern I had, which was not represented... It was you who had the problem

    @TechZilla: It's just the comment sections. You talked unsolicitedly, so accept that others will, too. Your initial comment stated something about "every system". I raised my concerns that this is unrealistic. And you basically agreed with me by refining your original comment to the GNU/Linux subset of "every system" afterwards; note: **subset**. Even tho you claim that was "clear", it actually wasn't. Yes, I had a problem: That you have problems with being precise, and then claim "clarity" where there isn't any. But we agree on laying this down, now. Let's drink a beer when we meet in RL :)

    Except that you didn't reject the actual thought process which explains the Pro-C behavior we sometimes encounter, you took my words out of context when they clearly were in relation to OP. The purpose was to essentially prove clarity itself is unrealistic, and thus my prior commentary meaningless. Did you actually misread my commentary?, I don't think so, I think you purposely misused language to invalidate and dismiss a conclusion you found unfavorable. Marxists and Post-modernists don't have beers, we dislike each other for the correct reason.

    You can use C++ as C-with-sugar and then the reason is obvious (you want to use the sugar).

  • When I'm going to performance, the first and last choice is C.

    And that’s where you should back up. Now, I cannot, at all, speak for server development. Perhaps there is indeed no compelling reason to prefer C++ over the alternatives.

    But generally speaking, the reason to use C++ rather than other languages is indeed performance. The reason for that is that C++ offers a means of abstraction that has, unlike all other languages that I know, no performance overhead at runtime.

    This allows writing very efficient code that still has a very high abstraction level.

    Consider the usual abstractions: virtual functions, function pointers, and the PIMPL idiom. All of these rely on indirection that is at runtime resolved by pointer arithmetic. In other words, it incurs a performance cost (however small that may be).

    C++, on the other hand, offers an indirection mechanism that incurs no (performance) cost: templates. (This advantage is paid for with a (sometimes hugely) increased compile time.)

    Consider the example of a generic sort function.

    In C, the function qsort takes a function pointer that implements the logic by which elements are ordered relative to one another. Java’s Arrays.sort function comes in several variants; one of them sorts arbitrary objects and requires a Comparator object be passed to it that works much like the function pointer in C’s qsort. But there are several more overloads for the “native” Java types. And each of them has an own copy of the sort method – a horrible code duplication.

    Java illustrates a general dichotomy here: either you have code duplication or you incur a runtime overhead.

    In C++, the sort function works much like qsort in C, with one small but fundamental difference: the comparator that is passed into the function is a template parameter. That means that its call can be inlined. No indirection is necessary to compare two objects. In a tight loop (as is the case here) this can actually make a substantial difference.

    Not surprisingly, the C++ sort function outperforms C’s sort even if the underlying algorithm is the same. This is especially noticeable when the actual comparison logic is cheap.

    Now, I am not saying that C++ is a priori more efficient than C (or other languages), nor that it a priori offers a higher abstraction. What it does offer is an abstraction that is very high and incredibly cheap at the same time so that you often don’t need to choose between efficient and reusable code.

    I do now know enough about C++ to know whether you are spot on or wrong. But also want to add you only received 1 downvote so relax. This is the internet and downvotes happen. Great answer though if its technically sound!

    Actually, modern compilers such as MSVC are capable of the nifty trick of cross-module inlining.

    I wonder about Exceptions, do they come with overhead?

    *no performance overhead at runtime* - that's not always true. If you look at STL vector implementations you will see that they don't take the advantage of using realloc() (they can't because of pointers, long story) whereas all higher-level languages that I know can and do use realloc() in vector impl's. This is just one example that it's not that obvious and all black-and-white.

    @Manuel: that’s indeed a nifty trick but this optimization (applied to function pointer inlining) is highly complex (it basically requires a flow analysis to show that the pointer never changes) and cannot in general be relied on. I’d be interested in numbers, though. Do you have any?

    @mojuba: they can’t because of *destructors*. Apart from that, please tell me which higher-level languages do use realloc. I don’t know of any that do. I know that most don’t – but then, comparing with managed memory is always difficult. A custom allocator can in fact help, though a custom allocator cannot take advantage of `realloc` either. What it *can* do, though, is emulate `realloc` on a higher level. In that way, they can achieve a very similar behaviour to garbage collected storage (if that’s what you were referring to).

    upvoted, though I would suggest that Ocaml is an example of a language that can get very close to C++ performance while still allowing highlevel abstractions (though these are generally different abstractions from those available in C++ - things like function composition, functors, etc.) as well as well as niceties like type inference, garbage collection and even stronger type safety than what's available in C++.

    > "C++, on the other hand, offers an indirection mechanism that incurs no (performance) cost: templates." obviously you haven't heard of the instruction cache.

    @mojuba, I'm not sure, but I think C++0x will be able to address mojuba's concern. I think I read that is_pod will be available, and in the case of a POD type, you could use realloc safely.

    @Neil G, but that would require a change to `std::allocator`. It would need a `reallocate` function that container classes can take advantage of.

    @Charles, Yes, good point. In fact, that's a much better solution. reallocate could use C++0x's move semantics in the case that the type is non-POD and the new block is in a different place.

    Templates can be prone to bloat, which is a big run-time cost. It's not just that the program takes longer to load, but also that a smaller proportion fits into the highest cache levels at any one time. A well written template may be a typesafe thin wrapper around non-typesafe underlying code, but then you re-introduce more obvious abstraction overheads. Templates are worthwhile primarily for type safety reasons - not high-performance abstraction reasons - though there are exceptions to that.

    @Neil G: unfortunately it's not only POD vs non-POD. The problem is that in C++ a vector element can contain pointers to locations that potentially may be affected by realloc(). The only way of keeping these pointers up to date is copying elements (during resizing of a vector), and calling copy ctors. There is no way in C++ for the vector implementation to know if a vector element in fact contains such pointers or not.

    @Konrad Rudolph: please see my ohter answer above, but actually you may be right regarding garbage collectors: I'm not sure they can handle reallocations of pointers. But then it depends on the GC. Some very simple ref-counted GC's can do fine (PHP comes to mind).

    @Jaroslaw, @Steve: but the same (= code bloat, instruction cache misses) is true for hand-optimized code which is adapted for each data type (see Java’s various `Arrays.sort` implementations). Only you lose the advantage of high abstraction. This isn’t a specific disadvantage of templates, it’s a disadvantage of code duplication in general. Furthermore, this tends not to matter since in tight loops, it’s usually always the same code that is loaded.

    @Steve314: Konrad has explained one very common case where template improve performance. How can you say this isn't usually the case?? Enter template-meta programming, where very complicated code often compiles down to a few machine instructions. If you want to see what TMP can do, have a look at http://www.templog.org, which boils down a complex logging statement to nothing (tested on VC, with logging disabled) or one `if` to check whether a log message should be shown. Show me one other language where syntactic elegance combines with tremendous efficiency in such a great way.

    @mojuba, yes, and why isn't it possible for a reallocate function to call the copy constructor (or move constructor in C++0x)?

    @Neil: the problem is that `realloc` either reuses the old memory, or returns a new pointer and *scraps* the old memory. Without calling any destructors, of course. This gives the calling code *no* chance to call destructors: if it does so before calling `realloc`, it might destroy memory prematurely. If it does so after calling `realloc`, then the destructor calls are executed on freed memory => UB.

    @konrad, yes, but we're talking about adding a reallocate function to std::allocator. That function could easily invoke the move constructor if necessary.

    @Jaroslaw: the funny thing about the instruction cache is that *it is a cache*. That is, it does not try to store *everything*, only *recently used code*. Templates might generate lots of similar code for different types (a `vector.push_back` for `vector`, and another for `vector`, but while working with a `vector`, there is little reason why the `vector` code would be in the instruction cache. So I don't see how that really matters. Every *individual* template instantiation is highly optimized and typically *more* compact than generic catch-all implementations

    ... because much of the code can be inlined or optimized away. A `sort` for integers may be able to avoid generating code for a lot of special cases that are only relevant for other types, so while there may be a lot of code paths, each individual code path is certainly not bloated just because you use templates. Often, the opposite is the case.

    @sbi - template metaprogramming in C++ is ugly and fragile. It is also powerful, but that only overrides the ugliness and fragility in a few special cases. There are some big wins for C++ templates, and sort certainly stands out - but even that can be bloat prone at times. If you sort lots of vectors of basic types you win - but if you sort lots of vectors of different classes you probably lose. Typesafe container classes need a lot of care to avoid bloat. Last I checked, standard container implementations generally don't bother with bloat-avoiding measures.

    @jalf - a lot of data in many of my applications is in database-table-like form - containers of record-like items. Sometimes hundreds of different record-like types, and inner loops generally do work with several tables at once. In short, there are good reasons to believe that I can benefit by using the same underlying code to search/insert/whatever in a container irrespective of the record-type it contains - it very often is already in the cache. I use my own container library in part because it *is* a thin typesafe wrapper over non-template code, and saved on bloat in the past.

    Of course. It depends on the workload. Show me an implementation strategy that is optimal in *all* cases. I'm just saying that templates do not *automatically* imply bloat. It depends on how they're used, and in some workloads, templates might lead to *less* bloated code than a single general implementation.

    @Steve314: What you call "bloat" is what is done manually for C and Java. In C++, it can be automated, and compilers can be as smart as vendors dare to make them to avoid bloat. If I have to decide between either being slow (as in C) or use duplicated code (as in Java), having the compiler doing the duplication (and maybe being smart about it) seems pretty good, no?

    What about Ada's generic packages (and other generics) --- I think they're handled at compile-time in a similarly efficient way.

    @Jason Indeed, the same that counts for C++ also counts for Ada, D, Ocaml and perhaps a few other oddball languages.

    One problem with writing high-performance C++ code as opposed to C (which I don't see anyone mentioning) is that in C it is comparatively easy to get a rough idea of what code the compiler is going to generate. In C++ this is in general very difficult, mainly due to RAII.

    @JesperE It’s even easier in assembly. It’s true – but that’s not an argument.

    @KonradRudolph Why not? It affects how easy it is to understand what kind of code is going to be executed. Doesn't that matter?

    @JesperE To some extent. However, the idea here seems to be that C++ requires *more* understanding to produce equally well-performing code, and I don’t believe that: sure, if you write high-level code without understanding performance implications then chances are this will be slow(er). But all C++ does is tip the balance in favour of efficient abstraction. It doesn’t change the fact that in order to write a given system (whether in C or C++) you need to understand it. To get efficiency in C, you’d write complicated low-level code.

    C++ does more than just "tips the balance". C is closer to the metal, while C++ provides better abstractions. When you want to squeeze the last cycle out of the hardware, C++ complicates things by hiding many things behind templates and other abstractions. In some cases that may still be beneficial for performance, but in other cases not.

    @JesperE That is simply not a correct distinction. *If* those abstractions get in the way, simply don’t use them. But in cases where they can be used efficiently, C++ allows writing simpler code than C would.

    JesperE, for a competence c++ programmer, they know what kind of c++ codes would bring them peformance penalty when compare to c solution.The difference between competence c programmers and competence c++ programmers is like the following post(ttsiodras post it) said, competence c++ programmers know how to write no run-time overhead, more safety and less codes when compare to c programmers, you will never know how could this be done if you don't study c++ carefully.Stop acting like a FUD programmer like linus who criticize c++ without trying to understand c++

    @StereoMatching Tone it down a bit please, this is a Q&A for professional software developers, not really the best place for flame wars and rants.

    Bjarne Stroustrup examines the efficiency of C `qsort()` vs. C++ `std::sort()` in some detail in part 3 of _Five Popular Myths about C++_. Turns out C++ is going to be quite a bit more efficient.

    Implicit behavior makes the code harder to track and understand. It can also lead to bugs, unexpected behavior, etc. While C++ does a lot for you, this is not always beneficial. It's great to have abstractions but not leaky abstractions (and for performance, abstractions tend to leak). qsort may be slower than std::sort, but not that much slower and it doesn't really matter because often you'll end up writing your own anyway. If we consider standard library problems as drawbacks of the language, then I would mention std::list.

    @martinkunev I agree with some things you’ve said, except that it simply doesn’t apply here. *Every* function call obviously represents an abstraction, and leaky abstractions are fundamentally unavoidable. Regarding `sort`, I’ve encountered situations where the difference was non-negligible, and your suggestion to write your own general-purpose `sort` function is utterly impractical: writing a fine-tuned `sort` is *really, really* hard. You don’t do this. You use a proven implementation. — And yes, `std::list` is terrible. But the standard library algorithms aren’t.

    More of a philosophical than technical debate. Unless you minimize leaky abstractions, some'll end up causing more problems than they solve. You may save time with C++ std. or you may waste time until you realize you've made a bad decision. Then too much may already depend on your choices. When C++ std doesn't do what you need, you'll have to write (or modify existing) code anyway. Writing a general-purpose fine-tuned sort is indeed really hard. That's a reason to use domain knowledge to tune sorting for what is needed in the project. Anyway, I don't think std is enough of a reason to use C++.

    @martinkunev If you don’t think it’s a strong enough reason then you probably work in a domain where that’s true. If, however, you work in a domain where a strong and finely tuned algorithms library is necessary, believe me, this *is* a good enough reason — I work in bioinformatics, and it’s crucial here.

    An interesting case for Java where the JIT can inline small function calls. Is it smart enough these days to recognize that the comparator parameter is always the same so it can be inlined?

    @ThorbjørnRavnAndersen It *isn’t* always the same: it’s only the same for a given call to the `sort` method. But if the user calls `sort` again, even with the same generic types (which are type-erased anyway), they might pass a different comparator.

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM