10 ms·
On the evolution of programming style: How far C++ has moved from its C roots
- Impossible 15y agoIt's interesting that with a modern C++ compiler all of these programs are equally valid. This is both the biggest strength and the biggest flaw of C++. I imagine the size differences are overhead from bringing in STL and maybe template instantiation and are relatively fixed? I would have liked to see performance benchmarks as well as executable size to see if the program has gotten slower, faster, or remained basically the same.
- JoachimSchipper 15y agoTemplates typically expand to one chunk of code per type handled, so no, executable size does expand if you use a lot of them. (A C++ fan would counter that they are faster than pointer-based generic algorithms like C's qsort and that you'd get the same code size manually typing it out.)
- ajax77 15y agoWhether or not you are a C++ fan, templatized algorithms ARE often faster than their C-based, generic counterparts. Any decent C++ compiler should be able to inline generic code while C often relies on function pointers. I'm not saying this is always the case, but often. std::sort beating qsort is a typical argument, though I'm not sure the two algorithms are equivalent excepting language-specific constructs (http://stackoverflow.com/questions/4708105/performance-of-qsort-vs-stdsort http://stackoverflow.com/questions/4708105/performance-of-qs...) As for size, you're absolutely right, though for many scenarios that appears to be less and less a pressing concern. Perhaps the biggest drawback is compilation time.
- calloc 15y agoCompilation time is becoming less of an issue as well with compilers getting faster and faster. For example the same codebase using GCC 4.6 and clang 3.0, clang is almost 20% faster at compiling the same codebase. Even so, making heavy use of templates I have not yet found any major issues or that compile time has increased so drastically that it became a concern for the project.
- ajax77 15y agoThankfully you're right; compilers have come a long way and Clang in particular has done a lot for easing the pains of template programming in a number of ways. I'm knee deep in the development of several header-based, fully templated libraries, so I still certainly feel the pains of compilation times. And while the tests/samples I deal with tend to be somewhat extreme in their (ab)use of templates, I think you'll still find that the average Boost user would give a lot for yet speedier compilation.
- JoachimSchipper 15y agostd::sort is stable, IIRC. Otherwise, they are similar; and yes, std::sort is typically faster. Do note that instruction caches are not unlimited, though: bloated code does have a performance cost (sometimes, caches are hard.)
- SamReidHughes 15y agostd::stable_sort exists and is the stable one.
- papaf 15y agoI agree with what you're saying but C can be persuaded to make template like code: http://attractivechaos.awardspace.com/ksort.h.html http://attractivechaos.awardspace.com/ksort.h.html
- cygx 15y agoAny decent C++ compiler should be able to inline generic code while C often relies on function pointers. What makes you think that a decent C compiler can't inline runtime-generic code using void* and virtual functions? Sure, erasing types only so that the optimizer has to figure the information out again using constant propagation is far from optimal, but the underlying issue is actually source code inclusing vs modular compilation -- most of the speed gain of templates comes from the fact that it only works with the former, whereas C-code traditionally does the latter.
- greyfade 15y agoNo, the majority of that overhead is from the use of `iostream`s (which are known to be pretty bloated compared to `printf` and friends), the use of exceptions (try/catch is a morass of stack-unwinding code), and the added bloat of class information (RTTI and symbols in particular). If it were compiled with full optimizations and the final binaries stripped, they'd be much closer in size. Moreso if exceptions are disabled or if the class is replaced with a simple function taking/returning tuples.
- alextgordon 15y agoNot only is iostream bloated, but it often injects a static constructor into every translation unit (which visibly angers the – usually calm – LLVM style guide[1]). Oh and it's really godawfully slow unless you turn off a magic flag somewhere[2]. Friends don't let friends use iostream. [1]: http://llvm.org/docs/CodingStandards.html#ll_iostream http://llvm.org/docs/CodingStandards.html#ll_iostream [2]: http://stackoverflow.com/questions/9371238/why-is-reading-lines-from-stdin-much-slower-in-c-than-python/9371717#9371717 http://stackoverflow.com/questions/9371238/why-is-reading-li...
- Suncho 15y agoThere's a huge difference between using exceptions and littering your code with a bunch of try/catch blocks. When exceptions are used properly, you see very little error handling code.
- alextgordon 15y agoLet me give one other way of doing it: "C++ without classes" std::pair<double, double> findRoot(double a, double b, double c) { if (a == 0.0) throw std::invalid_argument("a has value 0.0!"); double root1 = (-b + sqrt(b*b - 4*a*c)) / (2*a); double root2 = (-b - sqrt(b*b - 4*a*c)) / (2*a); return std::pair<double, double>(root1, root2); } It seems too many C++ programmers see a problem and think "Hey, let's use classes!".
- matthavener 15y agoReminds me of one of my favorite all time John Carmack tweets: "Sometimes, the elegant implementation is just a function. Not a method. Not a class. Not a framework. Just a function." https://twitter.com/#!/ID_AA_Carmack/status/53512300451201024 https://twitter.com/#!/ID_AA_Carmack/status/5351230045120102...
- marshray 15y agoExactly. The function is perhaps the most powerful and useful abstraction in all of computer science. I always found it absurd that some languages force you to write "Math.cos(...)".
- slowpoke 15y ago>I always found it absurd that some languages force you to write "Math.cos(...)". I disagree. Namespaces are a good thing. The "Foo dot bar" syntax is just commonly associated with class member access. I think it's a lot better to have to explicitly refer to where a function comes from (as long as it doesn't devolve into retarded Java-style verbosity, ie "Foo.bar.baz.spam.eggs..."). Though it's nice to have a way to omit the namespace, ie C++'s "using namespace Foo" or Python's "from Foo import bar". I just think that the sane default is to use explicit namespaces.
- marshray 15y agoI don't think everyone should always use explicit namespaces everywhere. But in this case "Math" isn't even a namespace! It's a class of which cos() is a static member. http://docs.oracle.com/javase/6/docs/api/java/lang/Math.html#cos%28double%29 http://docs.oracle.com/javase/6/docs/api/java/lang/Math.html...
- ajross 15y agoThe subject matter is interesting enough. But if it's a discussion on style, why is the code presented so unbelievably bad? Needless duplication of code; simple one-line declarations (i.e. a struct with three doubles) spread out over 5; empty constructors with trivial initializer lists defined outside the class header; needless empty destructors defined (again, outside the class declaration) for classes without virtual functions; "} else {" spread out across three lines (pet peeve of mine). The disaster of an Error enumerant deserves a paragraph all by itself. It's got only two values, "OK" and "FAIL". Guess which one evaluates to boolean truth? (And yes, I'm aware of how errno and the like work -- but error codes tend to be something other than "FAIL"). This looks like code generated by an IT employee at a local soap manufacturing company, not something to be seriously considered by people interested in C++ evolution.
- hythloday 15y agoGuess which one evaluates to boolean truth? The one that is an error?
- ajross 15y agoNo. If there is more than one error condition that needs to be handled by the caller, then sure. Enumerate them and provide a "SUCCESS" value. But if you are seriously arguing that "enum { OK, FAIL };" is good code, I weep. edit to avoid stringy flame thread: but it's a discussion on STYLE. If this was an example of how to write an error API, I wouldn't care (I probably wouldn't read it either). But it's a discussion about how C/C++ is written by presumably talented authors, and by inference how other people should write code. And the code sucks, sorry. And I'm horrified at your identification of dead code and needless whitespace as "syntactic fluff" not worthy of consideration as an important part of style.
- hythloday 15y agoTo clarify, I assume you mean that FAIL is the one that enumerates to true, but that you dislike this idiom. "For the purposes of the exercise I’ve deliberately limited the amount of error checking; this isn’t meant to be industrial-strength code (so don’t gripe to me that I haven’t covered all the error cases!)" I think it's pretty clear (at the very least, it's charitably inferrable) that the author is accustomed to errno-flavour error codes. Everything else you gripe about is just syntactic fluff* that makes no difference whatsoever to the analysis, and is dialect-invariant anyway. * Edit: by syntactic fluff I mean that it has both the properties of being easy to mechanically change and that the given mechanical change wouldn't turn bad code into good. One can have strong feelings about the density of white space in code or the proper place of constructor definitions for a class not defined in a header file (I don't, but whatever), but when the solution to it is to run it through a program for a perfect result, I think it becomes not worth discussing.
- michaelhoffman 15y agoI know the point is to discuss style, but I wish he had done it in a way that didn't include a textbook example of floating point programming that can yield inaccurate results. An accurate way wouldn't be so difficult: http://en.wikipedia.org/wiki/Quadratic_equation#Floating_point_implementation http://en.wikipedia.org/wiki/Quadratic_equation#Floating_poi...
- deleted 15y ago[deleted]
- ajax77 15y agoIt should be noted, it's very clear his executable sizes are for a Debug compilation. I tossed the same code for the final example into VC10, and sure enough in release mode the compiled size is 10KB, and in debug it's just less than the size he stated. This is for 64-bit. I'm not sure what point it serves to compare debug mode executable sizes, but that should be clearly stated in the article. As other people stated, the final example really ought to lose the wrapping class in the spirit of C++11 brevity and functional decomposition.
- deleted 15y ago[deleted]
- malkia 15y agoPointless... Because at the time you want to suck out performance of any of those, you would have to stick your guns to the provided macros or inline functions from Intel, ARM, IBM (PowerPC/AltiVec), etc. - and reimplement this using them - SSE2, AVX, whatever. This is not even funny.... And packing three doubles in structures - good programming technique? Okay.. That's enough (it might be good sometimes, but not always)
- bfrog 15y agoI think the overusage of classes is directly related to schools teaching Java instead of C or lisp or... well anything else.
- KonradKlause 15y agoLooks like the author of the C solution has never written a lot of C, but too much C++. ;-D
- cheez 15y agoThis is a dumb example. Should have used a root finder. C sucks with those.
- azth 15y agoPage not found :/ Does anyone have a cache of the article's text?