Skip to content

Session: Functional programming with C

rrahn edited this page Mar 6, 2017 · 11 revisions

Parameter Packs/Variadic Templates

Something you might know from variadic macros. Let us use define classes with zero or more template parameters or functions with zero or more arguments.

Recommended Reading/Viewing

So let's start with implementing a generic zip iterator, which allows us to zip multiple iterators into one and to simultaneously iterate over a set of containers in one iteration loop.

Template code (click to expand)

```c++ #include #include #include #include

using namespace std;

template <typename first_type, typename ...remaining_type> class Zip_iterator { public: Zip_iterator() = delete;

Zip_iterator(first_type && first, remaining_type &&... remaining) : _data(std::make_tuple(first, remaining...)) {}

std::tuple< first_type, remaining_types...> _data; };

int main() { vector v{0,1,2,3,4,5}; string s{'a','b','c','d','e','f'};

Zip_iterator<vector<int>::iterator, string::iterator> zipper{v.begin(), s.end()};

return 0;

}

</p></detail>

In this code example we defined a class **Zip_iterator**, which can take 1 ore more iterator types.
The _..._ notation denotes the parameter pack. 
The STL already gives us a data structure, which can hold 0 or more elements of different type, the [tuple](http://en.cppreference.com/w/cpp/utility/tuple) class.
However, the creation of the zipper is quite tedious, as we have to specify all template arguments, this zipper should be apply for. So we implement a utility function, called _zip_ similar to make_tuple, to not worry about the types that are stored in the zipper.

<details><summary>Template code (click to expand)</summary><p>
```c++
...

template <typename first_type, typename ...remaining_types>
inline auto 
zip(first_type && first, remaining_types && ...remaining)
{
    return Zip_iterator<first_type, remaining_types...>{first, std::forward<remaining_types>(remaining)...};
}

int main()
{
    vector<int> v{0,1,2,3,4,5};
    string	s{'a','b','c','d','e','f'};
  
    auto zipper = zip(v.begin(), s.end());

    return 0;
}

Ok, now we have a generic zipper that can hold one or more iterators. Now let's start implementing the iteration. We will start with the ++operator.

Template code (click to expand)

```c++ ...

namespace impl { template <typename tuple_type, size_t ...index> inline void increment(tuple_type & data, std::index_sequence<Is...> const & /idx/) { (void) std::initializer_list{(++std::get(tuple), 0)...}; } }

template <typename ...iter_types> inline Zip_iterator<iter_types...>& operator++(Zip_iterator<iter_types...> & me /pre-increment/) { impl::increment(me._data, std::make_index_sequence<sizeof...(iter_types)>()); return me; } ... int main() { vector v{0,1,2,3,4,5}; string s{'a','b','c','d','e','f'};

auto zipper = zip(v.begin(), s.end());

return 0;

}

</p></detail>

When we call `++` we somehow need to access all the elements in the tuple and increment the contained iterator.
We could loop over the tuple, but there is a more efficient way to do it utilising pack expansion.
With `[std::make_index_sequence<SIZE>()](http://en.cppreference.com/w/cpp/utility/integer_sequence)`, we can get all the indices that are available for the tuple using the `sizeof...()` operator. 
We than can expand all elements of the tuple via: `std::get<index>(tuple)...`. 
Now we also want to apply a function to all expanded elements. This can be achieved in C++14 with the initializer_list trick. Here we kind of create an initialiser list of integers and create it with an comma-separated expression. C++ will evaluate all expressions and return the last one. This means in our example, that c++ first calls ++ on the iterator at the corresponding expanded index of the tuple and then returns 0, to fill the initialiser list. Thus we can apply a function to all elements in the tuple with expansion semantics.

# Lambda functions:

### Recommended Reading/Viewing

 * http://en.cppreference.com/w/cpp/language/lambda
 * http://www.cprogramming.com/c++11/c++11-lambda-closures.html

Now we are going to implement multiple operators on the zip iterator. Since we want to reuse the initialiser trick, we will write a utility function called `apply_to`, which takes an arbitrary function and applies the elements in the tuple to it. We then can rewrite `++operator` to also submit a lambda function which implements the ++operation on the 

<details><summary>Template code (click to expand)</summary><p>
```c++
...

namespace impl
{
template <typename tuple_type, typename func_type, 
          size_t ...index>
inline void
apply_each(tuple_type && tuple, 
           func_type && func, 
           std::index_sequence<index...> const &/*idx*/)
{
    (void) std::initializer_list<int>{(func(std::get<index>(tuple)), 0)...};
}
}

template <typename ...iter_types>
inline Zip_iterator<iter_types...>& 
operator++(Zip_iterator<iter_types...> & me /*pre-increment*/)
{
    impl::apply_each(me._data, [](auto & it){ ++it; }, std::make_index_sequence<sizeof...(iter_types)>());
    return me;
}

template <typename ...iter_types>
inline Zip_iterator<iter_types...>& 
operator--(Zip_iterator<iter_types...> & me /*pre-decrement*/)
{
    impl::apply_each(me._data, [](auto & it){ --it; }, std::make_index_sequence<sizeof...(iter_types)>());
    return me;
}
...
int main()
{
    vector<int> v{0,1,2,3,4,5};
    string	s{'a','b','c','d','e','f'};
  
    auto zipper = zip(v.begin(), s.end());
    ++zipper;
    --zipper;
    return 0;
}
Clone this wiki locally