Wednesday, November 9, 2016

C++ tips, 2016 Week 44 (31-Oct - 6-Nov-2016)

This is part of my weekly C++ posts based on the daily C++ tips I make at my work. I strongly recommend this practice. If you dont have it in your company start it. 
List of all weekly posts can be found here.

1. Locking the lambda type

This trick I saw in C++ Slack (great place, lots of interesting stuff). Its from Miro Knejp.

As we know the compiler generates different lambda types for different lambdas even if they are exactly the same:

auto lambda1 = []() {}; 
auto lambda2 = []() {}; 
std::cout << typeid(decltype(lambda1)).name() << '\n'; 
std::cout << typeid(decltype(lambda2)).name() << '\n';

This produces different results. Its implementation specific so it varies between compilers. However sometimes we want to put lambdas in a container and do something. We can use std::function and type erase them but it has some overhead (havent researched what exactly but I will, stay tuned).

So the trick here is to use a helper function that returns a lambda:

auto make_f = [](int multiplyer ) {     
return [multiplyer](const int& i) {  
return i * multiplyer;  
}; 
}; 

auto Funcs = std::vector<decltype(make_f(0))>(); 
Funcs.push_back(make_f(1)); 
Funcs.push_back(make_f(2));

This way we force the compiler to lock the lambda type because the return type of make_f must always be the same.

2. Table of parallel algorithms in C++17

This week infographic/picture/printable is the parallel algorithms in the Standard Library that are coming with C++17:

We can add to Sean Parent recommendation "No raw loops" - "Know the STL algorithms [and which ones are parallelizable]"

3. std::apply

std::apply is introduced in C++17 and allows us to  pass a tuple’s elements as function arguments. Since it is very easy to create tuples from variadics this is useful utility.


template <class F, class Tuple> 

constexpr decltype(auto) apply(F&& f, Tuple&& t);  

A possible implementation looks like this (I found it on the internet and cant remember where, apologies for stealing):


namespace detail 

{ 

   template <typename F, typename Tuple, bool Done, int Total, int... N> 

   struct call_impl 

   { 

     static void call(F f, Tuple && t) 

     { 

        call_impl<F, Tuple, Total == 1 + sizeof...(N), Total, N..., sizeof...

(N)>::call(f, std::forward<Tuple>(t)); 

     } 

  }; 

  template <typename F, typename Tuple, int Total, int... N> 
  struct call_impl<F, Tuple, true, Total, N...> 
  { 
     static void call(F f, Tuple && t) 
     { 
           f(std::get<N>(std::forward<Tuple>(t))...); 
     } 
  }; 
} 
// user invokes this 
template <typename F, typename Tuple> 
void call(F f, Tuple&& t) 
{ 
  typedef typename std::decay<Tuple>::type ttype; 
  detail::call_impl<F, Tuple, 0 == std::tuple_size<ttype>::value, 
std::tuple_size<ttype>::value>::call(f, std::forward<Tuple>(t)); 
}

This tip was suggested by mu coworker Orlin

4. Boost.MultiIndex

Boost.MultiIndex container is a powerful container that maintains one or more indices with different sorting and access semantics (as the name suggests). At first glance it can be very strange and hard looking but when you use it several times you get used to it and may experience that how-did-I-lived-without-this moment. 
I'll give as example one often occurring case - bidirectional map. You can find the full working example at boost tutorials. 


template<typename FromType, typename ToType> 
struct bidirectional_map 
{ 
   struct value_type 
   { 
       value_type(const FromType& first_, const ToType& second_) : 
       first(first_), second(second_) 
       {} 
 
       FromType first; 
       ToType second; 
   }; 
 
/* A bidirectional map can be simulated as a multi_index_container 
* of pairs of (FromType,ToType) with two unique indices, one 
* for each member of the pair. 
*/ 
 
   typedef multi_index_container< 
   value_type, 
   indexed_by< 
           ordered_unique<tag<from>, member<value_type, FromType, &value_type::first >>, 
           ordered_unique<tag<to>,   member<value_type, ToType,   &value_type::second>> 
       > 
   > type; 
};

We can use it to make for example a dictionary (as I shamelessly cop, paste and modified from boost tutorials):

using dictionary = bidirectional_map<std::string, std::string>::type; 
dictionary d;  
auto itFrom = d.get<from>().find("hola"); 
auto itTo   = d.get<to>().find(itFrom->second); 
 
if ("hola" != itTo->first) 
   std::terminate(); //omg its not in the bi-map! the world ends!

It also appears to give you a performance gain over using two or more containers:
Why you should use Boost.MultiIndex (Part I)


5. Compiler Explorer

Last but not least the most awesome tool nowadays. You probably lived in a cave in the last year or so if you havent heart of it but no shout out is enough.

This tool written by Matt Godbolt shows you the assembly generated by the compilers. It is very useful for examining the optimizations that the compiler is doing:



It appears that x * 8 - x is cheaper than x * 7.

You can also check if the zero overhead abstraction rule holds like for example:



Funny how clang entirely optimizes out the std::make_unique in this example even if we wrap it with lambda:



It has a dedicated C++ Slack channel where you can ask Matt anything. He is very cool and very approachable.

I'll be using CE a lot!

No comments:

Post a Comment