Thursday, November 24, 2016

C++ tips, 2016 Week 46 (14-Nov - 20-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. Template normal programming

Taken from CppCon 2016 talk Template Normal Programming (part 1 of 2) by Arthur J. O'Dwyer


2. Currying

In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument. Currying is related to, but not the same as, partial application. (from Wikipedia)

Very simple example taken from Dependently-typed Curried printf in C++ by Sumant Tambe :
 
int multiply(int i, int j) { return i * j; }
  
auto curry = [](auto binary) { 
 return [=](int i) { 
   return [=](int j) { 
     return binary(i, j); 
   }; 
 }; 
}; 
  
auto mul = curry(multiply); 
int ten = mul(2)(5); 
auto a = mul(10); 
auto b = a(20); 
std:: cout << b << std::endl; // prints 200

3.  String literal to type sequence

Taken from the Dependently-typed Curried printf in C++ again. It is a treasure, isn't it?

template <char... chars> 
using CharSeq = std::integer_sequence<char, chars...>; 
 
template <typename T, T... chars> 
constexpr CharSeq<chars...> operator""_lift() { return { }; }

As we can see from the example:

#include <boost/core/demangle.hpp> 

auto cpptruths = "cpptruths"_lift; 
std::cout << boost::core::demangle(typeid(decltype(cpptruths)).name()) << "\n";

it will turn "cpptruths"_lift returns std::integer_sequence<char,'c','p','p','t','r','u','t','h','s'>.

4. decltype(auto)

Since C++14 we can use:

decltype(auto) x = f();

The difference between auto and decltype(auto) is that the type deduced by auto for an initializing expression strips the ref-qualifiers (i.e., lvalue references and rvalue references) and top-level cv-qualifiers (i.e., consts and volatiles) from the expression, but decltype does not (When decltype meets auto).

In short decltype(auto) deduces exactly the same type with everything included while auto removes stuff to leave only the "naked" type (beware - * is part of the "naked" type, & is not).

It is very useful for perfectly forward a return type, for example:

template<class T1, class T2> 

decltype(auto) the_perfect_sum(T1&& x, T2&& y) 

{ 

   return std::forward<T1>(x) + std::forward<T2>(y); 

}

God knows who and how overloaded operator+ and what it returns so we better return the exact same thing.

5. boost::container::small_vector

small_vector (inspired by LLVM's SmallVector) is a vector-like container optimized for the case when it contains few elements. If you know at compile time that you will work with low number of elements using small_vector you can avoid dynamic storage allocations (The Mallocator hates performance!) when the actual number of elements is lower than the compile time set threshold. Small vector preallocates elements in-place and dynamic allocations are postpones until (if) the capacity grows beyond the initial preallocated capacity.

Example:

boost::container::small_vector<int, 10> smallVect;


std::generate_n(std::back_inserter(smallVect), 9, std::rand);


std::copy(smallVect.begin(), smallVect.end(), std::ostream_iterator<int>(std::cout, " "));

( Probably The Mallocator does not actually hate performance rather has hit the limits of its capabilities and is very irritated by which - we should encourage it to always try to get better )