It'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.
Templates 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.)
Whether 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-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.
Compilation 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.
Thankfully 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.
Any 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.
No, 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.
Not 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].
There'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.
>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.
Thanks for that info. Anyone know if C# has something similar?
From the linked docs: So when should you use static import? Very sparingly!
To me, it sort of confirms the point that these inelegant features are necessary because the language doesn't support fundamental primitives like plain functions and named constants without a superfluous object definition.
The CLI itself actually supports unadorned functions, but C# doesn't provide a way of writing them, or using directly them without resorting to reflection. I'm pretty sure C++/CLI lets you use and write standalone functions in managed code, and come to think of it I imagine F# would too. Also it's not an exact equivalent, but C# extension methods can often be used instead of static methods.
In a recent article on Hacker News regarding the evils of classes, one of the valid uses of classes were for makeshift namespaces. I would have to agree with this. The model of Java requires all functions to be inside classes. If you have a class full of statics, it functionally becomes a unique namespace. It's semantics, but at that point, they're interchangeable.
Except that you can't import a class using "using" or "import", so you have to write Math.cos() everywhere.
EDIT: I see below that Java has added "import static", but at that point you're in the situation where you have two things that are almost the same but not quite, which is not a particularly clean design in my opinion.
You are conflating namespaces (a general and abstract concept) with Java packages. While it's true that static methods can have private static (essentially global) data, in in the Java standard libraries static methods largely exist as a mechanism to keep function names from polluting a common global namespace.
And Math is a particularly special case of this. Ponder this: have you ever had to say
Ha! I was going to post a Perl 6 version of this to show how much shorter it is when you don't go through all the rigamarole, but of course you remind us that it's that short in C++ if you do it in a sensible manner. Bravo.
Surely alextgordon's point was that defining a solution in terms of a class where a function was more appropriate was bad, not that use of existing abstract types is to be avoided.
Not sure how we got to Scala, but in (non-varargs) C linkage the whole metaphor of "funtion arguments" is inherently a "tuple" to begin with. They only exist in tandem with each other, never in isolation. There's no real value I can see to trying to double-abstract it.
With constexpr, if the parameters are constants, the whole computation can happen at compile-time. The line is somewhat harder to read for people not used to functional programming, but unfortunately, constexpr functions are required to have a single expression.
"The rules for conditional operator are quite sophisticated and one of them says that if one of the sub-expressions is a throw expression, the type of the entire conditional operator is that of the other sub-expression." [1]
The linked page say "The typical C solution is to create a structure to hold the quadratic equation parameters." That seems like sticking 'C with classes' into something which doesn't need classes in the first place. Why wouldn't the standard solution for C use pointers to pass the return parameters? Something like:
This style is quite in line with a number of math packages I've used in C. Since the author used a numerically unstable solution, I suspect there's a lack of domain inexperience in what library users expect from a math package. Eg., why would I go through the overhead of copying my input parameters to a temporary struct when I could just pass them directly to the function?
Make it a template (parameterized with coefficient and solution type) and you'll get very generic and efficient solution across all mentioned 'dialects' of c(++). It has the added bonus that this routine can be converted to vector form almost without breaking the interface: just make a, b, c pointers and specify length. And you can specialize templates when you'll want to rewrite float32_t-version in assembly.
Numeric code is really inappropriate for demonstration of new C++ features, as good (fast!) numeric code is most often C with templates.
A slight variation with neither error codes nor exceptions.
template< typename T >
auto findRoots(T a, T b, T c) -> pair<maybe<complex<T>>, maybe<complex<T>>> {
typedef complex<T> C;
typedef maybe<C> Root;
typedef pair<Root, Root> Roots;
if (a == 0) {
if (b == 0)
return Roots(nothing, nothing);
else
return Roots(Root(-c / b), nothing);
} else {
auto d = C(b*b - a*c*4);
auto two_a = a*2;
return Roots(Root((-b + sqrt(d)) / two_a),
Root((-b - sqrt(d)) / two_a));
}
}
maybe is simply a templatized std::pair wrapper that lets you check if the returned value is valid (either via maybe.valid() or if(maybe)). All imaginary results are handled automatically via std::complex. nothing decomposes into an invalid maybe value of the appropriate type.
The minor nit first: the original specification did not support complex numbers, so adding them here is even less relevant than my return codes.
More importantly, your implementation is numerically less correct than my version, which itself is less correct than it should be.
Consider when b is near sqrt(d). In that case, b-sqrt(d) can lose precision. That's why I used the copysign function, so that I'm always adding two numbers of equal sign and size.
Mine is incorrect if b2 is near the size of 4ac since there too b2-4ac loses precision. Ideally this intermediate result should be done in quad precision if the input is in double.
Question: How does C++ handle copysign() (vs. copysignf() for floats), and support templates which want to use an higher precision intermediate value?
My reply wasn't meant as a numerically superior solution, simply as a fairly simple alternative without error codes...
As for "original specification", I'm not sure why you even bring that up. complex is certainly in the C++11 specification, against which my snippet is compliant.
Oh, I see I didn't explain well enough. I'm curious about how one would write the "numerically superior solution." That is, does C++11 have a templated copysign function which takes different numeric types (at least float, double, and quad)? If not, then there's some unneeded type promotion (or downcast) going on.
And if the template uses a float, how do I get the type with double precision to use as my intermediate? (And the same where the template uses a double and I want the intermediate to use a quad.)
There must surely be a way to handle these, but I haven't done C++ programming for over a decade and I don't know the modern way of doing things.
As regards "original specification" - I mean the original article from feabhas.com, which has since disappeared. As I recall, it only supported real roots, and not imaginary ones.
Honestly, given the problem context, a function is obviously the way to go. There isn't enough other context to give you a guideline for having an object model.
In a larger context, you might want to actually have an object/struct that represents a quadratic equation. The weird thing though was having an object for a quadratic equation and not having another object for its root (or more likely, either have a binomial class or just one generic polynomial class). I'm trying to think of a context where that makes any sense at all... and not finding it.
The 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.
No. 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.
To 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.
For clarity: having a symbol in a program named "FAIL" which evaluates to boolean truth is simply wrong, without room for reasonable disagreement. The errno trick is accepted because of the need to enumerate the "failing" space, it is never used in situations where there is only one kind of failure. And even if there were, it should (again, without argument) be called something specific (e.g. "NO_REAL_ROOT").
As to the latter point about a hypothetical machine I could use: I'd be very surprised if you had a tool which mechanically removes empty destructors, as that changes the semantics of the code (e.g. they can start being POD objects which get different initialization semantics). But in this code they were verifiably needless, which is bad style. Are you in the habit of writing other noop functions "just because?".
And I'm amused at your "not worth discussing" point, given that you jumped into a discussion about exactly that. :)
Which moral calculus allows you to call someone else's work "crap", where I can't cast aspersions on the quality of technology adoption in the soap manufacturing industry?
To be clear: I was just pointing out the hypocrisy in pulling out my (admittedly) mildly ad hominem slur comparing the OP author with an IT robot and calling it "crap". You can't reasonably demand civil discourse if you aren't willing to practice it.
But to be honest: I don't think my quip was really that bad. Did I hit too close to home? Do you do soap automation?
So my crime is the use of a mild (and very common) ad hominem in a context where I would expect most people would see the humor, not being uncivil? And it's OK to call things "crap" just so long as I don't make an ad hominem argument in doing so?
Sorry, your argument is steaming crap. The idea that one can't invoke the pervasive metaphor of bad IT-generated code (or, I guess, imply that the local soap industry might do it) but that it's OK to flame away at the author who does is just dumb.
And for the record: I don't believe I expressed any repentence. :)
I 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:
It 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.
Pointless... 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)
74 comments
[ 1.9 ms ] story [ 186 ms ] threadI 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.
(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.)
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.
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.
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.
Do note that instruction caches are not unlimited, though: bloated code does have a performance cost (sometimes, caches are hard.)
http://attractivechaos.awardspace.com/ksort.h.html
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.
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.
Friends don't let friends use iostream.
[1]: http://llvm.org/docs/CodingStandards.html#ll_iostream
[2]: http://stackoverflow.com/questions/9371238/why-is-reading-li...
"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/5351230045120102...
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.
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...
http://docs.oracle.com/javase/1.5.0/docs/guide/language/stat...
From the linked docs: So when should you use static import? Very sparingly!
To me, it sort of confirms the point that these inelegant features are necessary because the language doesn't support fundamental primitives like plain functions and named constants without a superfluous object definition.
static class Math { static int Pow(int a,int b) {...} }
EDIT: I see below that Java has added "import static", but at that point you're in the situation where you have two things that are almost the same but not quite, which is not a particularly clean design in my opinion.
And Math is a particularly special case of this. Ponder this: have you ever had to say
why should they?
[1] http://akrzemi1.wordpress.com/2011/05/06/compile-time-comput...
Numeric code is really inappropriate for demonstration of new C++ features, as good (fast!) numeric code is most often C with templates.
More importantly, your implementation is numerically less correct than my version, which itself is less correct than it should be.
Consider when b is near sqrt(d). In that case, b-sqrt(d) can lose precision. That's why I used the copysign function, so that I'm always adding two numbers of equal sign and size.
Mine is incorrect if b2 is near the size of 4ac since there too b2-4ac loses precision. Ideally this intermediate result should be done in quad precision if the input is in double.
Question: How does C++ handle copysign() (vs. copysignf() for floats), and support templates which want to use an higher precision intermediate value?
As for "original specification", I'm not sure why you even bring that up. complex is certainly in the C++11 specification, against which my snippet is compliant.
And if the template uses a float, how do I get the type with double precision to use as my intermediate? (And the same where the template uses a double and I want the intermediate to use a quad.)
There must surely be a way to handle these, but I haven't done C++ programming for over a decade and I don't know the modern way of doing things.
As regards "original specification" - I mean the original article from feabhas.com, which has since disappeared. As I recall, it only supported real roots, and not imaginary ones.
BTW, _Complex is also in C99.
That's usually a result of people coming from Java doing C++ work.
In a larger context, you might want to actually have an object/struct that represents a quadratic equation. The weird thing though was having an object for a quadratic equation and not having another object for its root (or more likely, either have a binomial class or just one generic polynomial class). I'm trying to think of a context where that makes any sense at all... and not finding it.
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.
The one that is an error?
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.
"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.
As to the latter point about a hypothetical machine I could use: I'd be very surprised if you had a tool which mechanically removes empty destructors, as that changes the semantics of the code (e.g. they can start being POD objects which get different initialization semantics). But in this code they were verifiably needless, which is bad style. Are you in the habit of writing other noop functions "just because?".
And I'm amused at your "not worth discussing" point, given that you jumped into a discussion about exactly that. :)
Too bad you go and ruin a perfectly reasonable comment with that crap.
I'm sorry I slandered your slander.
But to be honest: I don't think my quip was really that bad. Did I hit too close to home? Do you do soap automation?
You were insulting people, based on their jobs.
And now you're attacking mseebach. In truth you don't sound very repentant.
Sorry, your argument is steaming crap. The idea that one can't invoke the pervasive metaphor of bad IT-generated code (or, I guess, imply that the local soap industry might do it) but that it's OK to flame away at the author who does is just dumb.
And for the record: I don't believe I expressed any repentence. :)
http://en.wikipedia.org/wiki/Quadratic_equation#Floating_poi...
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.
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)