C++11 is the new standard of C++ that was released last year. Yes, I know that is now 2012, but compilers are just now starting to catch up and implement everything, though AFAIK there is not yet a fully compliant compiler.

With a combination of C++11 and the Boost library, I think that it is possible to write code in a style that is almost as painless as in a modern dynamic language like Python. I also think that is not so well known how much C++ has changed for the better, outside the C++-community. Hence this post.

As an example, I have taken the first interesting excercise from Dive into Python, fileinfo.py, and converted it to C++, trying to remain as faithful as possible to the original code.

fileinfo.py implements a simple MP3-metadata printer, which is built so that it will be easy to add other metadata printers in the future. For the Python code, see the previous link and for my commented C++ code see here:

//Having to include so many different header files to do basic things like open a file, use strings, vectors and tuples, etc, is still annoying. #include <fstream> #include <vector> #include <map> #include <tuple> #include <string> //To use C++11 lambdas with Boost lambdas we define this. Warning, bugs in Boost < 1.51 and with compilers that are not standard compliant. Gcc 4.7+ should work. In Boost 1.52 and later we may not need the define at all. #define BOOST_RESULT_OF_USE_DECLTYPE #include <boost/format.hpp> // String formating #include <boost/algorithm/string.hpp> // Join list of strings #include <boost/filesystem.hpp> // Iterate over files and directories #include <boost/range/adaptors.hpp> // Adaptors! #include <boost/range/algorithm/copy.hpp> // We just need one algorithm //Like from ... import * in Python. Beware namespace collisions. Don't open namespaces directly in a header file. using namespace std; using namespace boost::filesystem; using namespace boost::adaptors; typedef map<string, string> propMap; // Modelling mp3 metadata String Key/Val typedef map<path, function<propMap(string)>> extLookup; // Extension to FileInfo functor const int kTailSize = 128; // Size of Mp3 metadata section. Located at the end of the file. string stripnulls(string s){ using namespace boost::algorithm; // Example of local "using" erase_all(s, "\0"); //Remove the null bytes trim(s); //Trim whitespace in both ends of the string return s; } string ord(string s){ int i = static_cast<unsigned char>(s[0]); //Chars are signed by default, so we need a cast to treat them as unsigned, like the Python code return (boost::format("%1%") % i).str(); //Boost format is a type safe version of sprintf, which also avoids the problem with preallocating a buffer } //In C++11, passing functions around is much easier. Everything that behaves like a function from string to string can be stored in a function<string(string)>, including functors (objects with overloaded call semantics) typedef function<string(string)> StrToStr; //In the olden days we could not initialize a map inline like this //Key -> metadata mapping. Unfortunately tuples cannot be {}-initialized nested. const map<const string, const tuple<int, int, StrToStr>> TagDataMap { {"title" , make_tuple( 3, 30, stripnulls)}, {"artist" , make_tuple( 33, 30, stripnulls)}, {"album" , make_tuple( 63, 30, stripnulls)}, {"year" , make_tuple( 93, 4, stripnulls)}, {"comment" , make_tuple( 97, 29, stripnulls)}, {"genre" , make_tuple(127, 1, ord)}}; propMap Mp3FileInfo(string p){ propMap ret {{"name", p}}; ifstream f(p, ios::binary); if(f.fail()) return ret; string sbuf; sbuf.resize(kTailSize); f.seekg(-kTailSize, ios::end); f.read(&sbuf[0], kTailSize); if(sbuf.substr(0,3) != "TAG") return ret; int start, length; StrToStr mapfun; //for loops over collections are finally convenient to use. for(auto td : TagDataMap){ tie (start, length, mapfun) = td.second; // "tie" is tuple deconstruction and assignment, just like in Python ret[td.first] = mapfun(sbuf.substr(start, length)); } return ret; } vector<propMap> listDirectory(string directory, extLookup exts){ directory_iterator startd(directory), endd; auto files = make_iterator_range(startd, endd); vector<propMap> retmap; for(path p : files){ auto x = exts.find(p.extension()); if(x != exts.end()){ retmap.push_back(x->second(p.string())); } } /* I actually think that for loops are too general, and should be used as seldom as possible. Range algorithms like filter and transform tell the reader (and compiler) exactly what I am doing to my data and leaves less room for bugs and misinterpretation. I know this is heresy to many C++-ers, though. The following is how the above for loop would look in a functional list-comprehension style, featuring Boost's range algorithms and the new C++11 lambda expression. boost::copy(files | filtered([exts](path p){return exts.count(p.extension());}) | transformed([exts](path p){return exts.find(p.extension())->second(p.string());}), back_inserter(retmap)); I decided not to go with this anyway, since in this case the original for loop is so succinct. */ return retmap; } int main(int argc, char* argv[]){ //A map from file extension to function, replacing the brittle Python introspection method extLookup exts = {{".mp3", Mp3FileInfo}}; //Get a property map for each file in the given directory for(propMap pm: listDirectory(argv[1], exts)){ //"join" is like join in Python and "pm | transformed" pipes the property map through a mapping function that makes strings from the properties. cout << join(pm | transformed([](propMap::value_type pv){ return (boost::format("%1%=%2%") % pv.first % pv.second).str(); }), "

"); cout << "



"; } }

If you remove my gratuitous commenting and the includes, you will notice that it is more or less as succinct as the Python code.

Perhaps more importantly, it is also as safe. No pointers to point at bad places and leak memory and no buffers to overflow. There is much more to why modern C++ is more safe and less memory leaky than it once was, besides the added emphasis on a more functional style, for example raw pointers are actively discouraged (use smart pointers instead) and arrays (not vectors!) are now finally in the standard library, so we use array<int, 10> instead of int array[10].

This program is actually the basis of a presentation I gave at work, which had more explanations on the features used. It only touches the surface on what you can do with C++11 You can read more about the new features in C++11 in the biggest changes in C++11 and why you should care and of course on Wikipedia.

Herb Sutter, who is chair of the ISO C++ standards committee and thus perhaps not completely unbiased, had this to say on the relase of C++11:

Now with C++11’s improvements that incorporate many of the best features of managed languages, modern C++ code is as clean and safe as code written other modern languages, as well as fast, with performance by default and full access to the underlying system whenever you need it.

I think it is almost there. For example, the language is still too large, so in a new project you have to have a good code standard, that decides well on which parts should be used and how. The standard library, even including Boost, is still a bit weak in some areas, like downloading an URL. Also the lack of a proper package manager, like pip/PyPi, hurts.