Friday, March 31, 2017

Structured Binding (C++17 inside)

Let's see how structured binding introduced in C++17 will change the way to interact with std::pair, std::tuple, std::array and such:
     std::pair<int,float> foo();  
     auto [a,b] = foo();  
it will replaces:
     std::pair<int,float> foo();  
     int a;  
     float b;  
     std::tie(a,b) = foo();  
in case you are running an obfuscated code contest this new swap can scrub up well (please don't try this at home):
     std::tie(a,b) = std::make_pair(b,a);  
The decomposition works with c array and std::array as well:
    int a[4] = { 1, 2, 3, 4};  
    auto [b,c,d,e] = a;  
    std::array<int, 4> a;  
    auto [b,c,d,e] = a;  
and this is what you can do using a ranged for loop:
    std::map myMap;  
    ...  
    for (const auto & [k,v] : myMap) {  
    }  
I bet someone in c++ committee has become recently a python enthusiast. Now if you wonder what your structures like this:
   stuct X {  
    int theInt = 3;  
    float thePi = 3.14;  
   };  
   auto [a,b] = x;  
shall provide to make the decomposition working the response is: a plain nothing. That will work indeed off the shelf.

Unfortunately if you need to do something more fancy with your class it has to support the get<>() functions, and you need to reopen the std namespace to specialize std::tuple_size and std::tuple_element.

Given the following user defined type (note a and b here are private members):
   class Y {  
   public:  
    int foo() const {  
     return a;  
    }  
    float bar() const {  
     return b;  
    }  
   private:  
    int a = 3;  
    float b = 3.14;  
   };  
you need to provide the gets<>() functions:
   template <int N> auto get(Y const &);  
   template <> auto get<0>(Y const & aY) {  
    return aY.foo();  
   }  
   template <> auto get<1>(Y const & aY) {  
    return aY.bar();  
   }  
and then you need to reopen the std namespace (one of those few allowed cases):
   namespace std {  
    template<>  
    struct std::tuple_size<Y> {  
      static const size_t value = 2;  
    };  
    template<size_t I>  
    struct std::tuple_element<I, Y> {  
     using type = decltype(get<I>(declval<Y>()));  
    };  
   }  
Note the partial specialization for std::tuple_element, you don't need to hard code the type of each index, it's enough to "deduce" it using the get function. You did a lot of work in order to have your class supporting the decomposition, in this case c++17 can save you some work taking advantage of a new c++17 feature, the "constexpr if", just writing a single version of get<>():

   template<int N>   
   auto get(Y const & aY) {  
     static_assert(N==0 || N==1);  
     if constexpr (N == 0) {  
       return aY.foo();  
     } else if constexpr (N == 1) {  
       return aY.bar();  
     }  
   }  
If you want use/experiment with those new language features go for clang++ (I tried only version 5.0 but it should work with the 4.0 as well) and you need to specify -std=gnu++1z

Saturday, March 18, 2017

Concepts!

Let's start from something very easy, you have the simple function:

soon you realize you need another one but for float and before you even need the third one you write it like this:

looks like you are done, everything goes well until, while compiling your huge project, the compiler gives the following error:

right, you think, the type Point needs to have defined the operator+, after defining it and after some precious minutes you get now another error:

and finally this is the last error and fixing it fixes your whole compilation.

Sometime that Point type is defined in an header that makes your entire project to recompile every time you add a missing feature.

Let see how can concepts can save our time and some headache.

Basically that sumScale function has a strong requirement on the type T. It should be a summable type (it has to support the operator+) and it should be scalable (it has to support operator* with an int), we can express these two concepts in the following way:

and then use this defined concept rewriting the sumScale function:

doing so the error would have been a more useful one:

wow, within a single iteration the compiler was able to gives us all the information we needed in order to fix the issue. In case you missed it I'll report for convenience the old and the new version of sumScale function.

and this is, in my humble opinion, one of the main advantages of concepts: simplify the generic programming taking rid of the cumbersome template syntax.

Let's go back now to our concept:

this concept is the refining of a Scalable concept indeed we can define the SummableScalable concept writing first a Summable concept then refining it in the following way:

even better we can combine two concepts Summale + Scalable obtaining a third one:

I believe when we will get the concepts available on our preferred compiler, for instance the concepts didn't make C++17 and today (at my knowledge) the concepts are implemented only in GCC 6 (using the flag -fconcepts), it will change the face of c++ code even more the c++11 specification did.