11 ms·
C++11 and Boost - Succinct like Python
- unwind 14y agoGreat post, it's always fun to see the cutting edge of C++ revealed. It's such a strange land. :) To me, this post would have been 10X more interesting if there was some elementary benchmarking included. If it's about the same speed as the corresponding Python code, which I'd bet is easier to write for more programmers, then I don't quite see the point being as clearly proven. If it's 100 times faster (or whatever), then it gives more credibility to the idea of writing code like this in C++ to begin with, and to learning all the new language features that makes it safe while being that much faster than Python.
- michaelfeathers 14y agoThe simplifications are nice, but the problem is that the language still carries all of the heavy syntactic machinery that makes it happen, and there's nothing that prevents people from dipping down into it.
- potkor 14y agoEspecially if the benchmark measured programming and debugging time.
- daivd 14y agoI wrote this post. The program is I/O-bound, so the only speed improvement comes from not having to start up the Python interpreter. If the task was CPU bound, you would get a great performance boost (sometimes 100x over CPython), but that is well known. There can be other reasons than performance for writing in C++. Used well, the strong static type system can catch many bugs. I suspect (but I cannot prove) that it could be almost as good as Haskell. I use Python for most things, though, so I don't really advocating switching to C++ for every little scripting task.
- unwind 14y agoD'oh, it (of course) makes total sense for this to be I/O bound, I didn't read the code too closely or I hope I would have figured out that much. Thanks for the clarification.
- alpatters 14y agoI do both python and C++ in my job. They're both great at what they do IMO. Often, they are a good match for each other; several of the boost libs have been designed similarly to python equivalent. boost python also makes writing python extensions very easy. For other reasons to write in C++ (or any static typed lang), I'd add type dispatching: calling different methods based on type. I also hate having a typo in a method name throwing at runtime. I also often convert python implementations to C++, often using boost, usually for performance in one part of the code. You can quite often get the same implementation in a similar number of lines, especially since C++11 with boost.
- deleted 14y ago[deleted]
- pcwalton 14y agoIt won't be as good as Haskell for memory safety. For example, this program, using only C++11 idioms, crashes: #include <iostream> #include <vector> int main() { std::vector<std::string> v; v.push_back(std::string("Hello")); v.push_back(std::string("there")); for (auto ii = v.begin(), ie = v.end(); ii != ie; ++ii) { v.clear(); std::cout << *ii << std::endl; } return 0; } Preventing this sort of thing requires strong guarantees about aliasing (to ensure that "v" can't alias the vector being iterated over), which the C++ type system can't help you with.
- evincarofautumn 14y ago“C++11 idioms” include a preference for immutability, which leads to natural factorings. You don’t have to worry about things like iterator invalidation that way. #include <algorithm> #include <iostream> #include <iterator> #include <vector> using namespace std; template<class T> void print(const T& v) { copy(begin(v), end(v), ostream_iterator<string>(cout, "\n")); } int main(int argc, char** argv) { vector<string> v{"Hello", "there"}; print(v); } Also, Haskell is only so safe—at work we’ve taken to rejecting non-total functions in review, because they cause more hassle than they’re worth. By non-total, I refer more to “error” than non-termination, of course.
- viraptor 14y ago"you will notice that it is more or less as succinct as the Python code." ??? I hope that this person writes better python than this: const map<const string, const tuple<int, int, StrToStr>> TagDataMap { {"title" , make_tuple( 3, 30, stripnulls)}, {"artist" , make_tuple( 33, 30, stripnulls)}, ... it will be succinct when the whole type can get reduced to "auto". Python would just do: TagDataMap = { 'title': (3, 30, stripnulls), 'artist': (33, 30, stripnulls), ... Ord implementation is also crazy: int i = static_cast<unsigned char>(s[0]); return (boost::format("%1%") % i).str(); considering it doesn't even handle different string encodings. I don't want to complain about C++. It's a great language and has its uses. But it's not succinct and it's far away from what scripting languages provide. Claiming otherwise is close to fanboyism. Not being succinct is not a bad thing. There are many other things that C++ does for you that Python doesn't.
- daivd 14y agoYou misunderstand the purpose of ord. The mp3 metadata uses one byte to encode an integer between 0 and 255, which represents genre. It is not a string.
- viraptor 14y agoI did misunderstand it. But then the first point is even stronger. Those two lines are definitely not as short/clean/readable as "str(ord(s[0]))"
- daivd 14y agoI agree, it is much uglier. Part of it is that Python thinks a character is unsigned, whereas C++ thinks it is signed. This arbitrarily works in Python's favor here. Part of it is that Python just handles this better.
- ajross 14y agoNo, because they have some extra tokens in the syntax (mostly to deal with strong typing). But they're semantically identical, which I think was the author's point. You declare the data instead of assembling it, and you do it in the same order and in the same manner. There's more typing but the idiom is the same. This is something that I think a lot of people miss about modern C++. They make the mistake of assuming you still program it using C idioms, when often you don't have to. They assume you have to "manually manage memory" when obvious and simple RAII techniques have basically obsoleted that for probably 90% of cases. It's just not like that. If you have a problem that is "easy to express" in Python/Perl/Ruby/Javascript it is expressable in essentially the same way in C++. The gotcha to my mind is more that the C++ environment is unforgiving -- a novice python programmer making a syntax error generally bangs around and gets it to work, maybe with some odd thought errors someone else will have to clean up. A novice C++ programmer is lost until they find an expert to explain the problem. So I don't think web engineers need to worry about losing their jobs to C++ hackers quite yet. But at the same time, the intuition that C++ is "hard" is mostly wrong. A true expert can solve the same problems the scripting languages do, arriving at essentially equivalent code, in very similar time and with very similar (potentially higher due to typesafety) quality.
- lbolla 14y agoMy eyes just exploded! ;-)
- huhtenberg 14y agocout << join(pm | transformed([](propMap::value_type pv){... I'm sorry, but as "succinct" as it is, this is basically an unreadable bullshit. Reminds me strongly of a macro abuse in C.
- daivd 14y agoIt is only unreadable bullshit the first time you read it. If you use ranges, filtered and transformed for these kinds of tasks, it looks idiomatic.
- morsch 14y agoYep. It looked idiomatic to me -- and I don't even know C++11! I don't care for the unqualified access to join and transformed, though, even if it is more Pythonesque.
- alpatters 14y agoyou might not like the syntax of this particular boost lib, but it bears no relation to macro abuse in C. For one it is strongly typed. Of course it is unreadable if you don't know C++ or boost range. But it is not more complex than a python map or filter with a lambda, if you don't know python.
- lmm 14y agoIt is visibly worse than python. '|' - I thought that was bitwise or? - rather than an (admittedly technical) English word like 'map' or 'filter' (and '<<' has the same problem). 'transformed([](pv){...})' may be necessary for compatibility, but the brackets are harder to follow than 'transformed(lambda pv: ...)'. And the type declaration as "propMap::value_type" is just noise - it doesn't tell the reader anything about what the actual type is, and it isn't necessary for static typechecking either (it would be optional in a modern statically typed language e.g. Scala).
- pif 14y agoThe only point that finds me in disagreement is the need for a code standard that decides well on which parts should be used and how. I don't understand: why limiting ourselves? Why choosing such a well-tested instrument and choosing NOT to use some of it?
- tomjen3 14y agoIt is because C++ is a large language and it has some features (e.g. macros) that can be horribly misused.
- bcoates 14y agoSome parts exist either for backwards compatibility with existing code or to serve very narrow use-cases and should be used sparingly or not at all in new development. There's also a ton of redundant libraries and language features, resulting in a lot of decisions being arbitrary choices between equivalents. Having the same decision made throughout the code makes everyone who has to read the code's life easier.
- songgao 14y agoWell, I liked C++11 a lot. But now I think I'll replace it with Go whenever possible. I don't really mind so much the verbose syntax. What matters is how difficult it is to write efficient code. I recently rewrote one of my C++ projects(which uses C++11 features and Boost.asio) in Go. It took me half the time, less than 2/3 lines of code compared to the C++ version. This is expected. But most surprisingly, despite that I tried my best thinking about how to make it as efficient as possible in every detail when writing C++ version, and that I'm quite new to Go, the Go version still achieves nearly 100% higher throughput than C++ version. Maybe my C++ skill sucks. But I guess I'll just let it suck.
- daivd 14y agoGo is certainly a fine language. One common instance where I would prefer C++, though, is when you are writing libraries for others to use. A language that runs on a VM can not easily be used as a library inside a language that uses another VM. You cannot easily write a library for Python in Go or vice versa. If you use a VM-less language like C or C++, your work can be used (through various wrappers, like SWIG or that Apache thing) by everyone.
- ifij775 14y agoThe changes to C++ are too little, too late. These changes should have been made years ago, and C++ has lost momentum and credibility. Is anyone comparing Python to c++? nope, only the other way around.
- goostavos 14y ago>C++ has lost momentum and credibility. Could you expand on this a bit? I'm not a C++ guy, but I was thinking of picking it up. By what metric do you mean it has "lost credibility?"
- sremani 14y agoC++ does not need the credibility. Hey, in what language is your Python compiler written in C/C++. When performance is the criteria C++ will shine. The world runs on C/C++, this comes from a C# developer.
- Peaker 14y agoCPython is written in C, not C++. Two very different languages.
- mattgreenrocks 14y agoToo little, too late? There's no zero sum game here. Perhaps you're conflating Internet-popular (which doesn't mean shit) with useful?
- daivd 14y agoInternet popular means libraries on github and good tutorials, means useful.
- LnxPrgr3 14y agoAre you suggesting C++ lacks available libraries? Because for all of its faults, lack of libraries is not one I've run into. Many desktop and server applications are still written in C or C++, including the Web browser I'm writing this on. Reports of their deaths are exaggerations.
- ctrlaltesc 14y agoWhat I find with C++ is that there are many languages inside it. Put 5 C++ programmers in a room and you'll probably end up with 6 different styles. That the language may have some Python-esque tendencies (given very particular language, compiler and library versions, and a depth of knowledge not required in the Python equivalent) doesn't seem that interesting. Especially when you consider that 9/10 C++ programmers you meet don't actually code in that style and don't intend to.
- arctangent 14y agoEquivalent code written in D would look much more like the Python code than this.
- abyx 14y agoSuccinct - I don't think that word means what you think it means
- abyx 14y agoAlso, s/Succinct/Python/
- activepeanut 14y agoIs it possible to build Boost for iOS with c++11 enabled in llvm-clang?
- gilgoomesh 14y agoOn iOS 5 and newer (required for libc++), using clang 3.0 and newer (required for decent c++11 support), yes.
- activepeanut 14y agoCan you explain how, or point me to a resource describing how to do it?
- danieldk 14y agoThis post shows that C++11 is actually quite a comfortable language. Programming in C++98/03 was often a necessity for me (good for making performant cross-platform software), while I disliked the language quite a lot. The introduction of type inference, closures, range-based for loops, and uniform intialization makes many things that used to be tedious much simpler. That said, I guess we have to live with C++03 for many more years. E.g. in one application that I co-developed, we use a library that can currently only be compiled without a substantial amount of effort on Visual Studio 2005 or 2008. The DLL compiled with older versions is not compatible with 2010 or 2012. So, we are basically stuck in 2005 or 2008 land for now.
- Kurtz79 14y agoIt's succint, all right (well, sort of). But Python's major strength is readability, even more than coinciseness, or better, to provide both at the same time. C was born as a terse language, sacrificing readability for coinciseness (the original examples in K&R are incredibly succint, almost elegant, but far from readable). I can't see many improvements in C++ (a language that arguably has worse coinciseness than C, without gaining in readability in the process)in that regards, even with the new version. I work with C/C++ and I could easily understand a Python script even when I wasn't that familiar with the language, the same cannot be really said for that code in C++. BTW, I'm not bashing C++, that has a lot going for it,just not readability and coinciseness.
- ajross 14y agoI call shenanigans. That's true of simple scripts, I'm sure. But you can't seriously claim to me that you can understand decorator idioms, iterables or list comprehensions without a deep understanding of the language. What you say might have been true for Python c. 1998, it certainly isn't true today. Broadly: reading code is just hard. It's much harder than writing code. There are no non-trivial codebases that can be considered "easy to read" (if you think there are, then you're fooling yourself), and the choice of language makes only mild difference. (edit: I'm amused at the multiple people who had to jump in to explain "decorators aren't hard!" instead of responding to my core point. First, I know what a decorator is. Second, no one who isn't a reasonably proficient python programmer is going to have any clue what @classmethod does or why all the old code doesn't break without using it. It's non-standard, language-specific syntax in exactly the same way that an STL iterator is, and the only reason you think one is easy and not the other is because you know Python and not C++.)
- TazeTSchnitzel 14y agoDecorators aren't difficult to understand. It tells you that the function is modified in some way. Iterables and list comprehensions are also easy to read, and I do not have a deep understanding of Python at all.
- 14y ago
- talloaktrees 14y agoI know c and python, but not c++. While reading the code section, i totally thought this was a wonderful satire of c++
- daivd 14y agoBut that is sort of the point. You should not write C++ like C. If a C-programmer thinks your code is nice and readable, you are not using C++. The C-stuff was just added to lure C-programmers over.
- cjdrake 14y agoNice try, Bjarne. I'll stick with Python :).
- mitchi 14y agoNo thanks. I'll use D if I want a ton of features for free. This is not beautiful code. I could work around it I guess because I've seen much worse coming from C++ but it's really not my ideal of C++. But this is a small example, the source code of the STL offers much more madness. Not to mention the compiler messages you get for using templates...
- drivewaygate 14y agoI want to know more about C++.
- j_baker 14y agoI think people who jump on the "C++11 is as good as Python/Ruby/Javascript/other-language-of-the-week" bandwagon are missing the point, and that is that limitations are a good thing. Can C++ be made as concise and easily-readable as one of the above languages? I wouldn't doubt it. C++ can be made to do lots of things, and it doesn't achieve that by being elegant. It achieves that by trying to do everything possible under the sun. Because of that, C++ is and always will be much more complex than the other languages.
- daivd 14y agoIs there really such a bandwagon?
- pmr_ 14y agoYes, it apparently has. The largest problem I see in this new trend is portability. Certainly, if you have full control over the environment and require everything to be bleeding-edge you are fine. If not... Well, just have a look at boost source code and you will see how concise nested workarounds in #ifdefs really are. If your environment is solely your own and you don't care about having your users build something, fine.
- 16s 14y agoI find C++ much easier to read than Python. Here's how to reverse a string in C++: string s = "string"; reverse(s.begin(), s.end()); And here's how to reverse a string in Python: s = "string" print s[::-1] In my opinion, the C++ version is far easier to read and just makes more sense. Edit - This is just one small example, however, I find it holds true for the entire languages in general. Also, I do a lot of Python and C++ systems programming so I use both frequently and while I prefer C++, I think Python is the best scripting language available today.
- BoppreH 14y agoOr you could use the "reversed" built-in function: s = "string" reversed(s) It also has the plus of creating a generator, saving memory from a new array allocation.
- wting 14y ago`reversed(s)` returns an iterator. The much slower equivalent of `s[::-1]` is `''.join(reversed(s))`. It's odd how Python has list.reverse() but not str.reverse(). Probably a conscious design decision sometime ago.
- ot 14y agolist is mutable while str is not. So reverse() should return a new copy, but it would duplicate the functionality of s[::-1] (which is quite idiomatic, although a bit obscure)
- solox3 14y agoThat's python 2 syntax.
- newobj 14y agoActually, how to reverse a string in Python is just "s[::-1]" -- you omitted the printing from the C++ version. In terms of readability, they both beg the question - is the reversed string ever actually held in memory at some point or can it occur lazily via latent reverse iteration? I mean is the alleged readability of either language really answering any important questions?
- muyuu 14y agoI think this comparison is completely misplaced. A language like C++11 simply doesn't even aim at being succinct like Python.
- nimrody 14y agoI work with C++ daily and it still has many rough edges: * Horrible error messages * Even simple programs take ages to compile due to massive header files. LLVM/Clang help on both fronts but it's still quite difficult. D2 seems much more promising if you can do without the libraries.
- deleted 14y ago[deleted]
- bitcracker 14y agoIn other words: C++11 boosts Python :-) I think only a hardcore C++ developer would claim that the author's sample is "succinct". Honestly, C++11 is still far behind the easiness of Python (or Scheme), even with Boost. Funny, a decade ago Ada 95 (the "military" language for high-critical applications) looked like a monstrous over-designed beast when compared to C++. Today Ada 2012 looks elegant and even "small" when compared to C++11. How times have changed :-)
- drivebyacct2 14y agoI hate to do it (who am I kidding, no I don't) but the battle in this thread for static typing, succinctness and an EASY language that is easy to wrap your head around... if those are things that make you happy to program in a language, please give Go a shot. Here, take 10 minutes: http://tour.golang.org http://tour.golang.org
- bcoates 14y agoThis is neat and C++11 is pretty exciting, but one thing that C++ doesn't need is the further propagation of tuples into non-generic code. Requiring make_tuple instead of allowing shorthand was the right decision. Tuples in python are a reasonable tradeoff between not wanting to declare anything and the hassle of anonymous structure. This doesn't apply C++ where the equivalent is the POD struct: //In the olden days we could not initialize a map inline like this //Key -> metadata mapping. Unfortunately strutcts cannot be declared //inside a template declaration. struct TagData { int start; int length; StrToStr mapfun; }; const map<const string, const TagData> TagDataMap { {"title" , { 3, 30, stripnulls}}, {"artist" , { 33, 30, stripnulls}}, {"album" , { 63, 30, stripnulls}}, {"year" , { 93, 4, stripnulls}}, {"comment" , { 97, 29, stripnulls}}, {"genre" , {127, 1, ord}}}; Creating a named struct pays off when it's time to use the Map, no extra locals or tie() needed to write clear code: //for loops over collections are finally convenient to use. for(auto td : TagDataMap){ //C++ created a horrible precedent by making the data type of //a map pair<K,V> instead of struct ValueType { K key; V value; }; auto tdd = td.second; ret[td.first] = tdd.mapfun(sbuf.substr(tdd.start, tdd.length)); } http://liveworkspace.org/code/bcd52515fb7161858e974b7ff3c0aac5 http://liveworkspace.org/code/bcd52515fb7161858e974b7ff3c0aa...
- daivd 14y agoYes, of course a POD is better, but that would be cheating, since this is supposed to show that you can do the Python stuff, including tuples and tuple deconstruction, just like in the linked Python original. Perhaps I should have mentioned that, though.
- simmons 14y agoI've been using both C++11 and Python lately. While the new C++11 features add a lot of value, I still find it frustratingly verbose for some things. For example, finding an element in a collection: std::string type = "foo"; auto it = std::find_if( channel_widgets.begin(), channel_widgets.end(), [type](const std::shared_ptr<Widget> &w){ return (w->getType() == type); } ); if (it == channel_widgets.end()) { std::cerr << "widget not found" << std::endl; } else { std::shared_ptr<Widget> target_widget = *it; std::cout << "widget found." << std::endl; } versus (for example): type = "foo" matches = [x for x in widgets if x.get_type() == type]; if matches: target_widget = matches[0] print "widget found:",target_widget else: print "widget not found" I may be missing out on some C++11 feature for doing this better. (Or even some Python feature for doing this better!)
- reinhardt 14y agoCan't comment on C++11 but the Python version is better written as: type = "foo" try: target_widget = (x for x in widgets if x.get_type() == type).next() print "widget found:",target_widget except StopIteration: print "widget not found"
- kzrdude 14y agoYou can also use the `next(x for x in ...)` form where you can even supply a default value.
- dfbrown 14y agoboost::range can makes a bit less verbose: std::string type("foo"); auto range = channel_widgets | filter([type](const std::shared_ptr<Widget> &w) { return w->getType() == type; }); if (range) { std::string target_widget = *range.begin(); std::cout << "widget found." << std::endl; } else { std::cerr << "widget not found" << std::endl; }
- stinos 14y ago
- Jabbles 14y agoIs it too late for me to bang my Go drum? http://play.golang.org/p/53jSv32wSF http://play.golang.org/p/53jSv32wSF (You can't access the filesystem on the playground, so it doesn't run.) * Look at that error handling. Mmmmhmmm, clear and explicit. If something goes wrong, I'll know about it. * Apart from, of course, errors that I ignore, such as when converting the year from 4 chars to an int (line 54). I don't care if I can't parse that. * Note the difference (lines 54, 56) between parsing the track (which is a byte), and the year, written as 4 digits. The byte can just be cast to an int, the string needs to be parsed by the strconv package. * I have a little bit of defensive programming in the form of a panic(), which would tell me if something that I thought was impossible has happened. * defer on line 19 makes sure the file closes if we managed to open it, regardless of when we return. * type casts are explicit, even from int to int64 (line 20) * I don't specify which interfaces a type implements, the compiler handles that all for me. * Parse() returns map[string]interface{}, which allows me to store anything (in this case just ints and strings) in a key-value store. * A type-safe printf! "%v" (line 67) uses reflection to look at the type of the argument and deduce the natural format. So I can pass it an int or a string and it works :) * On line 86 I pass this weird new type to a printf function and it goes ahead and uses the String() method that we defined. If we hadn't defined that we'd get a printout of the type, which in this case would be the 128 bytes we read.
- unoti 14y agoAt the end after line 24 do we need another line of code that reads data into the newly created byte array? If so, that says a lot about the readability of the code since I'm not a Go developer...
- jamesaguilar 14y agoAnd then you do an accidental implicit conversion from the iterator type (const pair<...>) to the function type (pair<...>), return a reference to one of the fields and BOOM, everything explodes.
- malkia 14y agoAnd here goes your productivity - the real killer application for your link times.
- SiVal 14y agoPeriodically throwing some new ingredients into old soup to freshen it up can only extend the life of leftovers so far. There comes a time when, even with a handful of fresh veggies, it's no longer good soup. You need to toss out the leftovers, scrub the kettle, and start a fresh batch. You will still need C++ for dealing with all the legacy C++ out there, but if you are starting a new project, you ought to be able to get the small, fast, efficient runtime advantages of a legacy monster like C++ from a much smaller, simpler language with a modern standard library. Maybe it will be Go, but even if not, we need a simple, safe, productive language with modern features pre-installed (unicode strings, safe arrays, lists, maps) and a modern standard library that statically compiles to small, fast, native executables. C++ with its "if you use the most recent 10% and pretend the old 90% doesn't exist, it's a great language" ethos is not what we need for new code.
- sodiumphosphate 14y agoI keep wishing for this magical language to manifest, like a C++ compatible Boo. I only wish I were capable of building one myself.