Kicking the tires on C++0x

  1. The Final Draft of the standard for the next version of C++ (referred to as C++0x) was just completed, it seems. There are other things to do before it's official and completed, but it's a big milestone. So I was curious what was available in the GCC compiler from the new spec. There's a list here:

    http://gcc.gnu.org/projects/cxx0x.html

    Quite a bit, really, in fact. Some useful stuff was available with version 4.4 of gcc, which I had installed on my Linux system. But I soon wanted to see some of the features in the new 4.6 version, so I compiled that. So far, so good.

    I thought it might be interesting for others if I show you some of the features that I like the best so far. But really, initialization lists and ranged for loops would be enough to get me excited. Anyways, this gives you a chance to take a peek at the new features and see an example of how they work. If that doesn't interest you, read no further.

    I installed gcc-4.6.0 in my home directory so I don't mess anything up, and I'll briefly go over how I get my test program to compile and run. It's a super simple setup, so don't expect much. I have one source file called Main.cpp which I compile with this (putting your home directory where I marked, and making sure the CC variable is your compiler binary):
    Code (Text):

    GCC_HOME=${HOME}/gcc-4.6.0
    PATH := ${GCC_HOME}/bin:${PATH}
    LD_LIBRARY_PATH := ${GCC_HOME}/lib
    LD_RUN_PATH := ${LD_LIBRARY_PATH}
    CC=g++-4.6
    CFLAGS=-std=c++0x

    all:
            ${CC} ${CFLAGS} Main.cpp -o Main
     
    I also ran these two lines in the shell before running the program:
    Code (Text):

    export PATH=${PATH}:$HOME/gcc-4.6.0/bin
    export LD_LIBRARY_PATH=$HOME/gcc-4.6.0/lib
     
    To get someone up and running quickly, this is the main part of the test program in Main.cpp:
    Code (Text):

    #include <iostream>
    #include <vector>
    #include <list>
    #include <array>
    #include <tuple>
    #include <algorithm>
    #include <chrono>
    #include <ratio>

    using namespace std;

    void testAuto();
    void testRangeFor();
    void testEnumClasses();
    void testInitializerLists();
    void initListFunc(initializer_list<int> list);
    void arrayTest();
    void testTuple();
    void testTime();
    void testLambda();
    void testNullptr();


    int main(int argc, char *argv[])
    {
       // Add calls to test functions here

       return 0;
    }
     
    Now I'll just go through the explanatory test functions one by one. To run each function, just call it from the previous program's main function.

    Possibly my second favorite new feature is the initializer list. A long time in coming. It's so far been annoying to initialize a vector or similar container with a static list of values. You end up either making an array initialized properly, and using it to construct a vector, or you use push_back() or similar on each element. Yuck.

    Not any more! Behold!
    Code (Text):

    void testInitializerLists()
    {
       vector<double> v = { 1, 2, 3.456, 99.99 };

       cout << "--- Initializer Lists ---\n";
       cout << "v[1] = " << v[1] << '\n';
       cout << "Max of {1, 5, 10, 6, 3} = " << max({1, 5, 10, 6, 3}) << '\n';

       list<pair<string,string>> languages = {
           {"Nygaard","Simula"}, {"Richards","BCPL"}, {"Ritchie","C"}
       };
       list<pair<string,string>>::iterator it;
       for (it = languages.begin(); it != languages.end(); it++)
       {
          // OOOOOOH, a cleaner way to access pair/tuple types, nice...
          cout << get<0>(*it) << " -> " << get<1>(*it) << endl;
       }
       initListFunc({1, 2, 3, 5, 6 , 7, 9});
    }

    void initListFunc(initializer_list<int> s)
    {
       for (auto p = s.begin(); p != s.end(); ++p)
       {
          cout << *p << '\n';
       }
    }
     
    I would also draw your attention to the way I accessed the variables in the pair. Normally, we'd have to use it->first and it->second. That get is also usable in the new tuples, which I'll get to later.

    Also note that max() now accepts initializer lists as well. I'll get to 'auto' soon.

    Which leads into my current favorite new feature: Ranged for. Wow, how I miss it when I use C++. It was one of the nicer new features in java when it was introduced. The normal way of using iterators is a bit verbose and unnecessary, I think.

    This, on the other hand, I like:
    Code (Text):

    void testRangeFor()
    {
       vector<int> list = {2, 42, 53, 137};

       cout << "--- Range for of vector<int> ---\n";
       for (int x : list)
       {
          cout << dec << x << '\n';
       }

       cout << "--- Range for of static ints ---\n";
       for (auto x : {1,2,3,5,8,13,21,34 })
       {
          cout << dec << x << '\n';
       }
    }
     
    Also really handy is the new auto type. Auto works like this:
    Code (Text):

    void testAuto()
    {
       int a = 43;
       double b = 203.3;

       auto x = a * b;

       cout << "--- auto type ---\n";
       cout << a << " * " << b << " = " << x << std::endl;
    }
     
    Though I'll probably used ranged for more than iterators now, auto can clean up an iterator. Look at the one I used to go through the 'languages' list in testInitializerLists(), above. It can be written:
    Code (Text):

    for (auto it = languages.begin(); it != languages.end(); it++)
    {
       ... etc ...
     
    Somehow I get the feeling auto may be abused, though, but that's C++ for ya.

    Enum classes are also of interest:
    Code (Text):

    void testEnumClasses()
    {
       enum EE : unsigned long { EE1 = 1, EE2 = 2, EEbig = 0xFFFFFFF0U };
       enum class Color : unsigned long {red = 0xFF0000, green=0x00FF00,
                                         blue=0x0000FF};
       cout << "--- Enum classes ---\n";
       cout << EE1 << endl;
       cout << "0x" << hex << EEbig << endl;
       Color col = Color::red;
       cout << "Color RGB: 0x" << (unsigned long)col << endl;
       switch (col) {
       case Color::red:
          cout << "RED" << '\n';
          break;
       case Color::green:
          cout << "GREEN" << '\n';
          break;
       case Color::blue:
          cout << "BLUE" << '\n';
          break;
       default:
          break;
       }
       cout << dec;
    }
     
    I do like the way the actual underlying type of the enum can be specified, and the scoping (i.e. It's not 'red' it's 'Color::red').

    And then there's the tuples, which are sure to come in handy:
    Code (Text):

    void testTuple()
    {
       // t will be of type tuple<string,int,double>
       auto t = make_tuple(string("Herring"),10, 1.23);

       cout << "--- tuple ---\n";
       string s = get<0>(t);
       int x = get<1>(t);
       double d = get<2>(t);

       cout << "tuple<\"" << s << "\", " << x << ", " << d << ">" << endl;
    }
     
    Also in my list of top new features is Lambda. Let's say you're trying to sort a vector, but you want to use your own comparator. Annoying, right? Easy now:
    Code (Text):

    void testLambda()
    {
       vector<int> v = {50, -10, 20, -30};

       cout << "--- Lambda ---\n";

       sort(v.begin(), v.end());    // the default sort
       // now v should be { -30, -10, 20, 50 }
       cout << "List: ";
       for (auto el : v)
       {
          cout << " " << el;
       }
       cout << endl;

       // sort by absolute value:
       sort(v.begin(), v.end(), [](int a, int b) { return abs(a)<abs(b); });
       cout << "List after sort by absolute value with lambda: ";
       for (auto el : v)
       {
          cout << " " << el;
       }
       cout << endl;
    }
     
    See how easy it was to define a new comparator to make it sort by absolute value? A vast improvement, in my opinion. Doing that previously just seemed to be more difficult than it should have been.

    We also have a new array type, for fixed-size arrays:
    Code (Text):

    void arrayTest()
    {
       cout << dec << "--- Array test ---\n";
       array<int,6> a = { 1, 2, 3 };
       a[3]=4;
       for (int i = 0; i < a.size(); i++)
       {
          cout << "a[" << i << "] = " << a[i] << '\n';
       }
       cout << "Sum: " << accumulate(a.begin(), a.end(), 0) << endl;
    }
     
    They've also added new time functionality. Of course, there's always the Boost libraries, but these will come in handy:
    Code (Text):

    void testTime()
    {
       using namespace std::chrono;
       microseconds mms(12345);
       milliseconds ms(123);
       seconds s(10);
       minutes m(30);
       hours h(34);

       cout << "--- time ---\n";
       auto x = hours(2) + minutes(35) + seconds(9);
       cout << "Duration of 2:35:09 in seconds (should be 9309) = " << duration<double>(x).count() << endl;
       cout << " Or in decimal hours: " << duration<double,ratio<3600,1>>(x).count() << " hours\n";

       auto t = monotonic_clock::now();
       for (int i = 0; i < 1000000; i++); // waste time
       auto d = monotonic_clock::now() - t;
       cout << "1,000,000 useless iterations took " << duration<double>(d).count() << " seconds\n";
    }
     
    For the last feature, it's one I've always missed, and it's a simple one. They added 'nullptr' to represent a null pointer value. I know right? Earth. Shattering.
    Code (Text):

    void testNullptr()
    {
        cout << "--- nullptr ---\n";
        int *p = nullptr;
        if (p == nullptr)
        {
            cout << "p is a nullptr\n";
        }
    }
     
    Well, that's where I'm at so far. There's plenty more features, but that's plenty for one post. Hope that was actually interesting to someone here! I'm sure many of you use C++, and that's going to be the standard soon enough.

    I should mention that snippets were taken from Bjarne Stroustrup's FAQ on C++0x at:

    http://www2.research.att.com/~bs/C++0xFAQ.html
     
    Last edited: Mar 28, 2011
  2. jcsd
  3. Mark44

    Staff: Mentor

    Very interesting!
    Thanks for the report, Grep.
     
  4. jtbell

    Staff: Mentor

    The initializer list for vectors etc. is something I really wish C++ had when I was teaching it some years ago.

    Alas, it looks like Apple's Xcode Tools is not going to go beyond gcc 4.2, so if I want to get in on the fun with 4.6 I'll have to compile it from source.
     
  5. Glad you liked it!

    Yep, love initializer lists!

    Not sure if Xcode makes it more complicated, but compiling gcc 4.6.0 on Linux was 100% painless as long as the install docs are read and followed. I would hope it wouldn't be much worse on Mac. Wouldn't put any money down on it, but I'd hope!

    I did find one page from someone who compiled it on Mac who put up what he did. Looks like you might have to compile some of the prerequisites before gcc. In case it comes in handy:

    http://www.coding.com.br/mac/building-gcc-4-6-on-macosx/

    Just keep in mind the '--' characters in some of his commands are messed up. Also, he got it from SVN, but you'd probably want the released tarball from http://gcc.gnu.org instead.
     
  6. jtbell

    Staff: Mentor

    Here's a source for gcc 4.6 pre-compiled for Mac OS X 10.6:

    http://hpc.sourceforge.net/

    I've previously installed his binary package for gfortran and it seems to work OK. I'll try his gcc when I get some free time.
     
  7. Thanks for taking the time to show us all those examples.

    For future reference: It's generally bad to install to you home directory (or anywhere else) as a normal user; it leaves your executables vulnerable to being patched. The generally accepted place to install stuff is in /usr/local/(unique name here).
     
  8. No problem Tyler, glad to see at least a few people found it interesting.

    As for installing in my home directory, it's the perfect place to install if you think you might want to delete it later. There's no make uninstall for gcc. But it seems to work nicely, so it'll probably get it installed to /usr/local soon enough.

    If anything, my system is secured far more than necessary so it's not likely to be an issue. I've been tinkering and developing with Linux since 1993, and you could say making it secure is part of the fun for me. So it's unlikely to ever be an issue in this case. Still generally good advice though!
     
  9. jtbell

    Staff: Mentor

    I just installed it on my Mac at work, putting everything into /usr/local. It works fine after I tweaked my PATH to put /usr/local/bin at the beginning so "g++" invokes 4.6 by default instead of 4.2 from Xcode tools. I also have to specify the standard in the compilation command:

    Code (Text):

    g++ -std=c++0x initializer.cpp
     
     
  10. D H

    Staff: Mentor

    Nice job, Grep.

    What I like:
    • default and delete.
      A lot of times I don't need a copy constructor or assignment operator. A lot of times I don't even know what copying what is inherently an un-copyable object even means. The "solution" in the current implementation is to declare the copy constructor and assignment operator private and (whoops) fail to provide an implementation. A couple of problems with this:
      • Normally the private part of a class is at the tail end of the class definition. Not these guys. A user of a class needs to know that the copy constructor or assignment operator are not available.
      • When someone does invoke one of these unimplemented methods, the error message is extremely non-intuitive. (Link error).

      A related problem occurs with virtual destructors: All I want is the default behavior, except the default is not virtual. C++0x fixes these problems, and it adds a whole lot of new toys with that solution. Very nice.

    • I won't bother going over that long list of features to which you already provided a link except for one: Simplified iteration. You went over this, Grep, but dang, that is nice.


    What I don't like:
    • Still no exponentiation operator. This is one of the big coffee stains on the tray tables that make some stick with Fortran rather than convert to C/C++.
    • The mathematical functions in TR1 are not going to be a part of c++0x?? This lack of support for scientific programming is yet another reason some stick with Fortran.

    I switched from Fortran to C (and then C++) almost twenty years ago. There are times when I see the points raised by my cohorts who have stuck with Fortran.
     
  11. jtbell

    Staff: Mentor

    Ranged for works with both new and old style arrays!

    Code (Text):

    //  Let's try this with a new-style array.

        array<double,4> a = {1.1, 2.2, 3.3, 4.4};

        cout << "New Array: ";
        for (double x : a)
        {
            cout << x << ' ';
        }
        cout << endl;

    //  Can we do it with an old-style array? Yes!

        double a2[4] = {1.1, 2.2, 3.3, 4.4};

        cout << "Old Array: ";
        for (double x : a2)
        {
            cout << x << ' ';
        }
        cout << endl;

     
     
  12. Thanks D H! And I do like those features as well. Could have gone on with more features, but figured the post was long enough.

    Another nice feature is delegating constructors. Alas, that is not yet implemented in gcc. Also looking forward to the thread/concurrency support, which is also not yet implemented. And I haven't yet looked at the rvalue stuff.

    I know it! I value clean, comprehensible code. C++ sometimes fails to deliver, but that's a feature that cleans it up quite a bit.

    I suspect those comments will garner quite a lot of support in these forums, myself included! This has bothered me quite a bit. It's just kind of sad to have any reason to stick with a language like Fortran after this long. They, in fact, made a conscious decision not to add scientific programming support for this spec. Possibly the only thing that really annoys me about the new spec.

    I could go on a rant, but suffice to say I agree with you 100%.

    Didn't even think to try, thanks for pointing that out. Outstanding!
     
  13. jtbell

    Staff: Mentor

    When I saw the "ranged for", I thought, "This reminds me of something." I finally remembered it: "foreach" in Perl:

    Code (Text):

    print "Array contents: ";
    foreach $item (@myArray)
    {
        print "$item ";
    }
    print "\n";
     
     
  14. D H

    Staff: Mentor

    Yep. And for loops in python. And in bash. And in csh. Lot's of languages have that. Nice to see it added to c++.
     
  15. Does anyone here use boost? Some features of C++0x make some boost libraries obsolete. Should those who use these boost libraries continue to do so since C++0x is backwards compatible or should we be abandoning these obsolete libraries?

    It's a shame that all the effort put into some of these boost libraries are, in a sense, wasted...
     
  16. Many of the standard library additions came directly from Boost. How's that a waste?

    If there's a good Boost library, use it. If a lot of people use it, chances are, it'll be part of C++1x.
     
  17. D H

    Staff: Mentor

    On the other hand, they at least did make the c99 math library part of the new standard, and very much in their favor, there is this neat new header <random>.


    Wasted? Those efforts were anything but wasted! Having the Boost libraries (or parts thereof) incorporated into some future standard is one of the explicit goals of the Boost project.
    From http://www.boost.org:
    We aim to establish "existing practice" and provide reference implementations so that Boost libraries are suitable for eventual standardization.
     
  18. Very true! Gave getting that going another try. At least some parts of the new random functionality is still found only in tr1, and appears not to have been rolled into the C++0x libraries yet. Looks nice though!

    I whipped up a quick test function.

    You need to include <tr1/random> for this functionality, for now.

    Code (Text):

    int rand_int(int low, int high)
    {
       using namespace std::chrono;
       auto engine = default_random_engine();
       auto t = monotonic_clock::now();
       engine.seed(t.time_since_epoch().count());

       static tr1::variate_generator<default_random_engine, tr1::uniform_int<>> r {engine,tr1::uniform_int<>(low,high)};
       return r();
    }
     
    Of course, keep in mind I'm seeding it every single time the function is called, when it should be seeded once at the start. But I refuse to "pull a Sony" and initialize it with a static number. :wink:

    And we won't mention a bit of 'auto' abuse, will we? Well, it *is* just for demonstration purposes.

    You can call it like this:

    Code (Text):

       cout << "Random int from 1 to 6: " << rand_int(1, 6) << endl;
     
    The meat of the rand_int function was taken from the C++0x FAQ.

    EDIT: I should mention that this seeding method is still not adequate for cryptographic use.
     
    Last edited: Mar 31, 2011
  19. D H

    Staff: Mentor

    But the Mersenne Twister should be adequate for most simulation usages.
     
  20. Indeed, and there's a few others in there as well, I notice.

    One thing I love is the distribution classes they provide (from TR1 so not sure if they all made it in, but I'd guess so). TR1 has:
    • bernoulli_distribution
    • binomial_distribution
    • exponential_distribution
    • gamma_distribution
    • geometric_distribution
    • normal_distribution
    • poisson_distribution
    • uniform_int
    • uniform_real

    Now that could come in handy!
     
  21. I don't think people have understood my concern about wasted effort in Boost...

    For instance, who is going to continue using BOOST_FOREACH while C++0x's for loop construct now does the same thing? Will people go back to old code replacing instances of BOOST_FOREACH with the new for loop? Will people switch if their code already has BOOST_FOREACH all over it? Will the Boost libraries deprecate BOOST_FOREACH now that it essentially does nothing?

    Isn't it a bit of a shame that all the effort put into BOOST_FOREACH is wasted now that it's obsolete?

    These are the kinds of concerns I was having...
     
Know someone interested in this topic? Share a link to this question via email, Google+, Twitter, or Facebook