Overloading std::transform algorithm - c++

Consider this standard use of std::transform algorithm:
vector<int> in = { 1, 2, 3 };
auto f0 = [](int val) { return val + 1; };
auto f1 = [](int val) { return val > 1; };
vector<int> out0(in.size());
std::transform(in.begin(), in.end(), out0.begin(), f0);
vector<bool> out1(in.size());
std::transform(in.begin(), in.end(), out1.begin(), f1);
This works fine but is long to write. I would like to write something like this:
auto out0 = tranform(in, f0);
auto out1 = tranform(in, f1);
How to overload the transform algorithm to allow this syntax?

The following should do what you want
#include <algorithm>
#include <iostream>
#include <type_traits>
#include <vector>
template<typename T, typename F>
std::vector<typename std::result_of<F(T)>::type>
transform(const std::vector<T>& l, F func)
{
typedef typename std::result_of<F(T)>::type FunctorReturnType;
std::vector<FunctorReturnType> out(l.size());
std::transform(l.begin(), l.end(), out.begin(), func);
return out;
}
int main ()
{
const std::vector<int> in{ 1, 2, 3 };
auto f0 = [](int val) { return val + 1; };
auto f1 = [](int val) { return val > 1; };
auto out0 = transform(in, f0);
auto out1 = transform(in, f1);
for (const auto& m: out0) std::cout << m << std::endl;
for (const auto& m: out1) std::cout << m << std::endl;
}

Do you like template template arguments? This works with more containers than vectors.
template<template<class...> class C, class F, class T, class... Tail>
C<typename result_of<F(T)>::type>
transform(const C<T, Tail...>& in, F func)
{
C<typename result_of<F(T)>::type> out(in.size());
std::transform(in.begin(), in.end(), out.begin(), func);
return out;
}
Can be seen working at http://coliru.stacked-crooked.com/a/767adb662d7cbe42
EDIT: changed to not use the source's allocator in the resulting container.

Why aren't you ok with the following, which doesn't use any C++11 magic such as decltype ?
#include <algorithm>
#include <vector>
template<class T, class F>
std::vector<T> transform(const std::vector<T>& l, F func)
{
std::vector<T> out(l.size());
std::transform(l.begin(), l.end(), out.begin(), func);
return out;
}

Related

Getting UB in my variadic template summing a variable number of vectors element-wise

I'm trying to make a function AddVector that adds a (variable) number of vectors element wise. I think I got it, but as I get the wrong output, I clearly don't. I'm adding three vectors of doubles, each sized 5, containing 1+2+1, thus I expect
4 4 4 4 4
I get
1.36234e-316 2.0326e-316 4 4 4
Which is clearly wrong (maybe uninitialized mem?)
I used CppInsights to look at the translated code, but that also seemed OK. What am I doing wrong here?
My code:
#include <vector>
template<typename T>
using Vec = std::vector<T>;
template<typename T>
auto SumAndIncVcIt(T& t) {
return *t++;
}
template<typename T, typename... Args>
auto SumAndIncVcIt(T& t, Args&... args) {
return *t++ + SumAndIncVcIt(args...);
}
#include <tuple>
template<typename... Args>
auto VcBegins(Args... args){
return std::make_tuple(cbegin(args)...);
}
template<typename T, size_t... Is>
auto SumAndIncVcIts_impl(T& t, std::index_sequence<Is...>) {
return SumAndIncVcIt(std::get<Is>(t)...);
}
template<class Tuple>
auto SumAndIncVcIts(Tuple& t) {
return SumAndIncVcIts_impl(t,
std::make_index_sequence<std::tuple_size<Tuple>{}>{}
);
}
template<typename T, typename... Args>
Vec<T> AddVector(Vec<T> const& vt, Vec<Args> const&... vargs){
auto vret = Vec<T>(size(vt));
auto vcIts = VcBegins(vt, vargs...);
auto retIt = begin(vret);
while(retIt != end(vret)){
*retIt++ = SumAndIncVcIts(vcIts);
}
return vret;
}
#include<iostream>
int main() {
constexpr auto size = 5;
Vec<double> a(size, 1.0), b(size, 2.0);
auto c = AddVector(a, b, a);
for(auto const& el : c){
std::cout << el << " ";
}
}
p.s. yes, I should use SFINEA or concepts.
Not sure where lies your problem yet, You found it yourself :) ... But this is how I would implement it with C++17 and sizes:
#include <vector>
#include <algorithm>
template<typename T>
using Vec = std::vector<T>;
template<typename T, typename...Args>
Vec<T> AddVector_impl(Vec<Args> const & ... vecs){
auto sizes = {vecs.size()...};
auto new_size = *std::min_element(sizes.begin(),sizes.end());
Vec<T> res(new_size);
for(std::size_t i=0;i<new_size;++i){
res[i]=(vecs[i]+...);
}
return res;
}
// Ensures at least one vector, also sets its return type.
template<typename T, typename... Args>
Vec<T> AddVector(Vec<T> const& vt, Vec<Args> const&... vargs){
return AddVector_impl<T>(vt,vargs...);
}
#include <iostream>
int main() {
constexpr auto size = 5;
Vec<double> a(size, 1.0), b(size, 2.0);
auto c = AddVector(a, b, a);
for(auto const& el : c){
std::cout << el << " ";
}
}
Live Godbolt demo
Output:
4 4 4 4 4
Iterator-only solution
#include <vector>
#include <algorithm>
template<typename T>
using Vec = std::vector<T>;
template<typename T, typename...Args>
Vec<T> AddVector_impl(Vec<Args> const & ... vecs){
auto its = std::tuple(vecs.cbegin()...);
auto add_inc = [](auto&... iters){
return ((*iters++) + ... );
};
auto end_check = [&](auto&...iters){
return ((iters!=vecs.cend()) && ...);
};
Vec<T> res;
auto it = std::back_insert_iterator(res);
while(std::apply(end_check,its)){
*it++=std::apply(add_inc,its);
}
return res;
}
template<typename T, typename... Args>
Vec<T> AddVector(Vec<T> const& vt, Vec<Args> const&... vargs){
return AddVector_impl<T>(vt,vargs...);
}
#include <iostream>
int main() {
constexpr auto size = 5;
Vec<double> a(size, 1.0), b(size, 2.0);
auto c = AddVector(a, b, a);
for(auto const& el : c){
std::cout << el << " ";
}
}
Live Godbolt demo.
found it myself...
template<typename... Args>
auto VcBegins(Args... args){
return std::make_tuple(cbegin(args)...);
}
passes by value, thus the vectors are copied and I get back invalid iterators
It should of course be
template<typename... Args>
auto VcBegins(Args const&... args){
return std::make_tuple(cbegin(args)...);
}

I made a C++17 burrito for mapping over tuples, and anything that has begin() end() iterators. Is it possible to get rid of these factory functions?

I found myself needing to iterate (generically) over an array, or a tuple of N T's.
Code demo:
http://coliru.stacked-crooked.com/a/9b8ab7c01b79d086
I came up with the following implementation, which I'm reasonably happy with it, as it seems to to handle the cases I need without introducing any overhead.
#include <array>
#include <iostream>
#include <tuple>
#include <vector>
namespace functional
{
namespace detail {
template <typename Tuple, typename F, std::size_t... Indices>
void
tuple_for_each_impl(Tuple &&tuple, F &&f, std::index_sequence<Indices...>)
{
using swallow = int[];
(void)swallow{1, (f(std::get<Indices>(std::forward<Tuple>(tuple))), void(), int{})...};
}
} // ns detail
template <typename Tuple, typename F>
void
for_each(Tuple &&tuple, F &&f)
{
constexpr std::size_t N = std::tuple_size<std::remove_reference_t<Tuple>>::value;
detail::tuple_for_each_impl(std::forward<Tuple>(tuple), std::forward<F>(f), std::make_index_sequence<N>{});
}
} // ns functional
struct tuple_tag {};
struct iterator_tag {};
template<typename U, typename TAG>
struct burrito
{
U value;
using TAG_TYPE = TAG;
template<typename T>
burrito(T const& t) : value(t) {}
template<typename T>
burrito(T t0, T t1) : value(std::make_pair(t0, t1)) {}
template<typename FN>
void iterate(FN const fn) const
{
bool constexpr IS_TUPLE = std::is_same<tuple_tag, TAG_TYPE>();
if constexpr (IS_TUPLE) {
functional::for_each(this->value, fn);
} else {
for (auto it{value.first}; it < value.second; ++it) {
fn(*it);
}
}
}
};
template<typename M, typename FN>
void testfn(M const& m, FN const& fn)
{
m.iterate(fn);
}
template<typename T>
auto
make_burrito(T t0, T t1)
{
auto const p = std::make_pair(t0, t1);
return burrito<decltype(p), iterator_tag>{p};
}
template<typename C>
auto
make_burrito(C const& c)
{
return make_burrito(c.cbegin(), c.cend());
}
template<typename ...T>
auto
make_burrito(std::tuple<T...> const& t)
{
using U = std::tuple<T...>;
return burrito<U, tuple_tag>{t};
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// demo
int main()
{
std::tuple<int, int> const tup0 = std::make_tuple(-3, -4);
std::array<int, 10> const arr0 = {5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
std::vector<int> const v = {32, 42, 52, 62};
auto const m0 = make_burrito(tup0);
auto const m1 = make_burrito(arr0.begin(), arr0.end());
auto const m2 = make_burrito(v);
auto const fn = [](auto const& i) {
std::cerr << std::to_string(i) << "\n";
};
std::cerr << "printing tuple\n";
testfn(m0, fn);
std::cerr << "|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-" << "\n";
std::cerr << "printing array\n";
testfn(m1, fn);
std::cerr << "|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-" << "\n";
std::cerr << "printing vec\n";
testfn(m2, fn);
std::cerr << "|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-" << "\n";
}
I'd like to be able to hide the instantiation of the burrito from the caller entirely, but still define my function arguments as a burrito.
Like this:
template<typename B, typename FN>
void something(B const& burrito_like, FN const& fn) {
burrito_like.iterate(fn);
}
With the caller doing:
auto const tup = ...; // somehow caller creates a tuple
something(tup, []() {});
This doesn't work though (obviously) and the compiler complains about tuple not implement iterate(FN const&);
I looked into implicit conversion constructors, but I don't see how I would specify the different TAG parameters from within the implicit constructor.
So my question is, is something like this possible?

Generic for_eaches template [duplicate]

With the new range-based for-loop we can write code like:
for(auto x: Y) {}
Which IMO is a huge improvement from (for ex.)
for(std::vector<int>::iterator x=Y.begin(); x!=Y.end(); ++x) {}
Can it be used to loop over two simultaneous loops, like Python's zip function? For those unfamiliar with Python, the code:
Y1 = [1, 2, 3]
Y2 = [4, 5, 6, 7]
for x1,x2 in zip(Y1, Y2):
print(x1, x2)
Gives as output (1,4) (2,5) (3,6)
Warning: boost::zip_iterator and boost::combine as of Boost 1.63.0 (2016 Dec 26) will cause undefined behavior if the length of the input containers are not the same (it may crash or iterate beyond the end).
Starting from Boost 1.56.0 (2014 Aug 7) you could use boost::combine (the function exists in earlier versions but undocumented):
#include <boost/range/combine.hpp>
#include <vector>
#include <list>
#include <string>
int main() {
std::vector<int> a {4, 5, 6};
double b[] = {7, 8, 9};
std::list<std::string> c {"a", "b", "c"};
for (auto tup : boost::combine(a, b, c, a)) { // <---
int x, w;
double y;
std::string z;
boost::tie(x, y, z, w) = tup;
printf("%d %g %s %d\n", x, y, z.c_str(), w);
}
}
This would print
4 7 a 4
5 8 b 5
6 9 c 6
In earlier versions, you could define a range yourself like this:
#include <boost/iterator/zip_iterator.hpp>
#include <boost/range.hpp>
template <typename... T>
auto zip(T&&... containers) -> boost::iterator_range<boost::zip_iterator<decltype(boost::make_tuple(std::begin(containers)...))>>
{
auto zip_begin = boost::make_zip_iterator(boost::make_tuple(std::begin(containers)...));
auto zip_end = boost::make_zip_iterator(boost::make_tuple(std::end(containers)...));
return boost::make_iterator_range(zip_begin, zip_end);
}
The usage is the same.
std::transform can do this trivially:
std::vector<int> a = {1,2,3,4,5};
std::vector<int> b = {1,2,3,4,5};
std::vector<int>c;
std::transform(a.begin(),a.end(), b.begin(),
std::back_inserter(c),
[](const auto& aa, const auto& bb)
{
return aa*bb;
});
for(auto cc:c)
std::cout<<cc<<std::endl;
If the second sequence is shorter, my implementation seems to be giving default initialized values.
So I wrote this zip before when I was bored, I decided to post it because it's different than the others in that it doesn't use boost and looks more like the c++ stdlib.
template <typename Iterator>
void advance_all (Iterator & iterator) {
++iterator;
}
template <typename Iterator, typename ... Iterators>
void advance_all (Iterator & iterator, Iterators& ... iterators) {
++iterator;
advance_all(iterators...);
}
template <typename Function, typename Iterator, typename ... Iterators>
Function zip (Function func, Iterator begin,
Iterator end,
Iterators ... iterators)
{
for(;begin != end; ++begin, advance_all(iterators...))
func(*begin, *(iterators)... );
//could also make this a tuple
return func;
}
Example use:
int main () {
std::vector<int> v1{1,2,3};
std::vector<int> v2{3,2,1};
std::vector<float> v3{1.2,2.4,9.0};
std::vector<float> v4{1.2,2.4,9.0};
zip (
[](int i,int j,float k,float l){
std::cout << i << " " << j << " " << k << " " << l << std::endl;
},
v1.begin(),v1.end(),v2.begin(),v3.begin(),v4.begin());
}
With range-v3:
#include <range/v3/all.hpp>
#include <vector>
#include <iostream>
namespace ranges {
template <class T, class U>
std::ostream& operator << (std::ostream& os, common_pair<T, U> const& p)
{
return os << '(' << p.first << ", " << p.second << ')';
}
}
using namespace ranges::v3;
int main()
{
std::vector<int> a {4, 5, 6};
double b[] = {7, 8, 9};
std::cout << view::zip(a, b) << std::endl;
}
The output:
[(4, 7),(5, 8),(6, 9)]
See <redi/zip.h> for a zip function which works with range-base for and accepts any number of ranges, which can be rvalues or lvalues and can be different lengths (iteration will stop at the end of the shortest range).
std::vector<int> vi{ 0, 2, 4 };
std::vector<std::string> vs{ "1", "3", "5", "7" };
for (auto i : redi::zip(vi, vs))
std::cout << i.get<0>() << ' ' << i.get<1>() << ' ';
Prints 0 1 2 3 4 5
You can use a solution based on boost::zip_iterator. Make a phony container class maintaining references to your containers, and which return zip_iterator from the begin and end member functions. Now you can write
for (auto p: zip(c1, c2)) { ... }
Example implementation (please test):
#include <iterator>
#include <boost/iterator/zip_iterator.hpp>
template <typename C1, typename C2>
class zip_container
{
C1* c1; C2* c2;
typedef boost::tuple<
decltype(std::begin(*c1)),
decltype(std::begin(*c2))
> tuple;
public:
zip_container(C1& c1, C2& c2) : c1(&c1), c2(&c2) {}
typedef boost::zip_iterator<tuple> iterator;
iterator begin() const
{
return iterator(std::begin(*c1), std::begin(*c2));
}
iterator end() const
{
return iterator(std::end(*c1), std::end(*c2));
}
};
template <typename C1, typename C2>
zip_container<C1, C2> zip(C1& c1, C2& c2)
{
return zip_container<C1, C2>(c1, c2);
}
I leave the variadic version as an excellent exercise to the reader.
If you like operator overloading, here are three possibilities. The first two are using std::pair<> and std::tuple<>, respectively, as iterators; the third extends this to range-based for. Note that not everyone will like these definitions of the operators, so it's best to keep them in a separate namespace and have a using namespace in the functions (not files!) where you'd like to use these.
#include <iostream>
#include <utility>
#include <vector>
#include <tuple>
// put these in namespaces so we don't pollute global
namespace pair_iterators
{
template<typename T1, typename T2>
std::pair<T1, T2> operator++(std::pair<T1, T2>& it)
{
++it.first;
++it.second;
return it;
}
}
namespace tuple_iterators
{
// you might want to make this generic (via param pack)
template<typename T1, typename T2, typename T3>
auto operator++(std::tuple<T1, T2, T3>& it)
{
++( std::get<0>( it ) );
++( std::get<1>( it ) );
++( std::get<2>( it ) );
return it;
}
template<typename T1, typename T2, typename T3>
auto operator*(const std::tuple<T1, T2, T3>& it)
{
return std::tie( *( std::get<0>( it ) ),
*( std::get<1>( it ) ),
*( std::get<2>( it ) ) );
}
// needed due to ADL-only lookup
template<typename... Args>
struct tuple_c
{
std::tuple<Args...> containers;
};
template<typename... Args>
auto tie_c( const Args&... args )
{
tuple_c<Args...> ret = { std::tie(args...) };
return ret;
}
template<typename T1, typename T2, typename T3>
auto begin( const tuple_c<T1, T2, T3>& c )
{
return std::make_tuple( std::get<0>( c.containers ).begin(),
std::get<1>( c.containers ).begin(),
std::get<2>( c.containers ).begin() );
}
template<typename T1, typename T2, typename T3>
auto end( const tuple_c<T1, T2, T3>& c )
{
return std::make_tuple( std::get<0>( c.containers ).end(),
std::get<1>( c.containers ).end(),
std::get<2>( c.containers ).end() );
}
// implement cbegin(), cend() as needed
}
int main()
{
using namespace pair_iterators;
using namespace tuple_iterators;
std::vector<double> ds = { 0.0, 0.1, 0.2 };
std::vector<int > is = { 1, 2, 3 };
std::vector<char > cs = { 'a', 'b', 'c' };
// classical, iterator-style using pairs
for( auto its = std::make_pair(ds.begin(), is.begin()),
end = std::make_pair(ds.end(), is.end() ); its != end; ++its )
{
std::cout << "1. " << *(its.first ) + *(its.second) << " " << std::endl;
}
// classical, iterator-style using tuples
for( auto its = std::make_tuple(ds.begin(), is.begin(), cs.begin()),
end = std::make_tuple(ds.end(), is.end(), cs.end() ); its != end; ++its )
{
std::cout << "2. " << *(std::get<0>(its)) + *(std::get<1>(its)) << " "
<< *(std::get<2>(its)) << " " << std::endl;
}
// range for using tuples
for( const auto& d_i_c : tie_c( ds, is, cs ) )
{
std::cout << "3. " << std::get<0>(d_i_c) + std::get<1>(d_i_c) << " "
<< std::get<2>(d_i_c) << " " << std::endl;
}
}
// declare a, b
BOOST_FOREACH(boost::tie(a, b), boost::combine(list_of_a, list_of_b)){
// your code here.
}
I ran into this same question independently and didn't like the syntax of any of the above. So, I have a short header file that essentially does the same as the boost zip_iterator but has a few macros to make the syntax more palatable to me:
https://github.com/cshelton/zipfor
For example you can do
vector<int> a {1,2,3};
array<string,3> b {"hello","there","coders"};
zipfor(i,s eachin a,b)
cout << i << " => " << s << endl;
The main syntactic sugar is that I can name the elements from each container. I also include a "mapfor" that does the same, but for maps (to name the ".first" and ".second" of the element).
If you have a C++14 compliant compiler (e.g. gcc5) you can use zip provided in the cppitertools library by Ryan Haining, which looks really promising:
array<int,4> i{{1,2,3,4}};
vector<float> f{1.2,1.4,12.3,4.5,9.9};
vector<string> s{"i","like","apples","alot","dude"};
array<double,5> d{{1.2,1.2,1.2,1.2,1.2}};
for (auto&& e : zip(i,f,s,d)) {
cout << std::get<0>(e) << ' '
<< std::get<1>(e) << ' '
<< std::get<2>(e) << ' '
<< std::get<3>(e) << '\n';
std::get<1>(e)=2.2f; // modifies the underlying 'f' array
}
For a C++ stream processing library I'm writing I was looking for a solution that doesn't rely on third party libraries and works with an arbitrary number of containers. I ended up with this solution. It's similar to the accepted solution which uses boost (and also results in undefined behavior if the container lengths are not equal)
#include <utility>
namespace impl {
template <typename Iter, typename... Iters>
class zip_iterator {
public:
using value_type = std::tuple<const typename Iter::value_type&,
const typename Iters::value_type&...>;
zip_iterator(const Iter &head, const Iters&... tail)
: head_(head), tail_(tail...) { }
value_type operator*() const {
return std::tuple_cat(std::tuple<const typename Iter::value_type&>(*head_), *tail_);
}
zip_iterator& operator++() {
++head_; ++tail_;
return *this;
}
bool operator==(const zip_iterator &rhs) const {
return head_ == rhs.head_ && tail_ == rhs.tail_;
}
bool operator!=(const zip_iterator &rhs) const {
return !(*this == rhs);
}
private:
Iter head_;
zip_iterator<Iters...> tail_;
};
template <typename Iter>
class zip_iterator<Iter> {
public:
using value_type = std::tuple<const typename Iter::value_type&>;
zip_iterator(const Iter &head) : head_(head) { }
value_type operator*() const {
return value_type(*head_);
}
zip_iterator& operator++() { ++head_; return *this; }
bool operator==(const zip_iterator &rhs) const { return head_ == rhs.head_; }
bool operator!=(const zip_iterator &rhs) const { return !(*this == rhs); }
private:
Iter head_;
};
} // namespace impl
template <typename Iter>
class seq {
public:
using iterator = Iter;
seq(const Iter &begin, const Iter &end) : begin_(begin), end_(end) { }
iterator begin() const { return begin_; }
iterator end() const { return end_; }
private:
Iter begin_, end_;
};
/* WARNING: Undefined behavior if iterator lengths are different.
*/
template <typename... Seqs>
seq<impl::zip_iterator<typename Seqs::iterator...>>
zip(const Seqs&... seqs) {
return seq<impl::zip_iterator<typename Seqs::iterator...>>(
impl::zip_iterator<typename Seqs::iterator...>(std::begin(seqs)...),
impl::zip_iterator<typename Seqs::iterator...>(std::end(seqs)...));
}
From C++23, we can iterate on std::views::zip.
Below is simple example.
#include <iostream>
#include <ranges>
#include <vector>
int main() {
std::vector<int> x {4, 5, 6};
double y[] = {7, 8, 9};
for (auto [elem1,elem2] : std::views::zip(x, y))
std::cout << "[" << elem1 << "," << elem2 << "]" << " ";
}
The output can be verified below (an online compiler). Not sure how many days the link exists.
https://godbolt.org/z/KjjE4eeGY
An improvement on aaronman's solution:
Still C++11.
No recursive template expansion.
Support for container zipping.
Utilizes the approach of Sean Parent's famed for_each_arg().
// Includes only required for the example main() below!
#include <vector>
#include <iostream>
namespace detail {
struct advance {
template <typename T> void operator()(T& t) const { ++t; }
};
// Adaptation of for_each_arg, see:
// https://isocpp.org/blog/2015/01/for-each-argument-sean-parent
template <class... Iterators>
void advance_all(Iterators&... iterators) {
[](...){}((advance{}(iterators), 0)...);
}
} // namespace detail
template <typename F, typename Iterator, typename ... ExtraIterators>
F for_each_zipped(
F func,
Iterator begin,
Iterator end,
ExtraIterators ... extra_iterators)
{
for(;begin != end; ++begin, detail::advance_all(extra_iterators...))
func(*begin, *(extra_iterators)... );
return func;
}
template <typename F, typename Container, typename... ExtraContainers>
F for_each_zipped_containers(
F func,
Container& container,
ExtraContainers& ... extra_containers)
{
return for_each_zipped(
func, std::begin(container), std::end(container), std::begin(extra_containers)...);
}
int main () {
std::vector<int> v1 { 1, 2, 3};
std::vector<int> v2 { 3, 2, 1};
std::vector<float> v3 {1.2, 2.4, 9.0};
std::vector<float> v4 {1.2, 2.4, 9.0};
auto print_quartet =
[](int i,int j,float k,float l) {
std::cout << i << " " << j << " " << k << " " << l << '\n';
};
std::cout << "Using zipped iterators:\n";
for_each_zipped(print_quartet, v1.begin(), v1.end(), v2.begin(), v3.begin(), v4.begin());
std::cout << "\nUsing zipped containers:\n";
for_each_zipped_containers(print_quartet, v1, v2, v3, v4);
}
See it working on GodBolt.
I would propose this one. I found it to be quite elegant, and exactly what I (and you) needed.
https://github.com/CommitThis/zip-iterator
Just in case here's a code copy. Note, it is distributed under MIT License, also don't forget to put name of author.
zip.hpp
/***
* MIT License
* Author: G Davey
*/
#pragma once
#include <cassert>
#include <functional>
#include <iomanip>
#include <iostream>
#include <list>
#include <string>
#include <vector>
#include <typeinfo>
namespace c9 {
template <typename Iter>
using select_access_type_for = std::conditional_t<
std::is_same_v<Iter, std::vector<bool>::iterator> ||
std::is_same_v<Iter, std::vector<bool>::const_iterator>,
typename Iter::value_type,
typename Iter::reference
>;
template <typename ... Args, std::size_t ... Index>
auto any_match_impl(std::tuple<Args...> const & lhs,
std::tuple<Args...> const & rhs,
std::index_sequence<Index...>) -> bool
{
auto result = false;
result = (... | (std::get<Index>(lhs) == std::get<Index>(rhs)));
return result;
}
template <typename ... Args>
auto any_match(std::tuple<Args...> const & lhs, std::tuple<Args...> const & rhs)
-> bool
{
return any_match_impl(lhs, rhs, std::index_sequence_for<Args...>{});
}
template <typename ... Iters>
class zip_iterator
{
public:
using value_type = std::tuple<
select_access_type_for<Iters>...
>;
zip_iterator() = delete;
zip_iterator(Iters && ... iters)
: m_iters {std::forward<Iters>(iters)...}
{
}
auto operator++() -> zip_iterator&
{
std::apply([](auto && ... args){ ((args += 1), ...); }, m_iters);
return *this;
}
auto operator++(int) -> zip_iterator
{
auto tmp = *this;
++*this;
return tmp;
}
auto operator!=(zip_iterator const & other)
{
return !(*this == other);
}
auto operator==(zip_iterator const & other)
{
auto result = false;
return any_match(m_iters, other.m_iters);
}
auto operator*() -> value_type
{
return std::apply([](auto && ... args){
return value_type(*args...);
}, m_iters);
}
private:
std::tuple<Iters...> m_iters;
};
/* std::decay needed because T is a reference, and is not a complete type */
template <typename T>
using select_iterator_for = std::conditional_t<
std::is_const_v<std::remove_reference_t<T>>,
typename std::decay_t<T>::const_iterator,
typename std::decay_t<T>::iterator>;
template <typename ... T>
class zipper
{
public:
using zip_type = zip_iterator<select_iterator_for<T> ...>;
template <typename ... Args>
zipper(Args && ... args)
: m_args{std::forward<Args>(args)...}
{
}
auto begin() -> zip_type
{
return std::apply([](auto && ... args){
return zip_type(std::begin(args)...);
}, m_args);
}
auto end() -> zip_type
{
return std::apply([](auto && ... args){
return zip_type(std::end(args)...);
}, m_args);
}
private:
std::tuple<T ...> m_args;
};
template <typename ... T>
auto zip(T && ... t)
{
return zipper<T ...>{std::forward<T>(t)...};
}
}
Example
#include "zip.hpp"
#include <vector>
std::vector<int> a, b, c;
void foo() {
for (auto && [x, y] : zip(a, b))
c.push_back(x + z);
}
Boost.Iterators has zip_iterator you can use (example's in the docs). It won't work with range for, but you can use std::for_each and a lambda.
Here is a simple version that does not require boost. It won't be particularly efficient as it creates temporary values, and it does not generalise over containers other than lists, but it has no dependencies and it solves the most common case for zipping.
template<class L, class R>
std::list< std::pair<L,R> > zip(std::list<L> left, std::list<R> right)
{
auto l = left.begin();
auto r = right.begin();
std::list< std::pair<L,R> > result;
while( l!=left.end() && r!=right.end() )
result.push_back( std::pair<L,R>( *(l++), *(r++) ) );
return result;
}
Although the other versions are more flexible, often the point of using a list operator is make a simple one-liner. This version has the benefit that the common-case is simple.

std::bind and function templates

I am currently trying to use std::bind to create a std::function<void()> from the function template
template<class Iterator>
void printRange(Iterator first, Iterator last) {
std::copy(first, last, std::ostream_iterator<typename Iterator::value_type>(std::cout, " "));
std::cout << std::endl;
}
Conceptually, what I want is
int main() {
std::vector<int> v{1, 2, 3};
auto f0 = std::bind(printRange, v.begin(), v.end()); // won't compile, of course
f0();
return 0;
}
I understand that this does not compile and I have to instantiate the function template before I can actually use it. For example, the following alternatives would work:
auto f1 = std::bind(printRange<std::vector<int>::const_iterator>, v.begin(), v.end());
auto f2 = std::bind(printRange<decltype(v.begin())>, v.begin(), v.end());
auto f3 = [&v]() { printRange(v.begin(), v.end()); };
I already created a convenience function
template<class Iterator>
std::function<void()> makePrintRangeFunction(Iterator first, Iterator last) {
return std::bind(printRange<Iterator>, first, last);
}
to ease the process:
auto f4 = makePrintRangeFunction(v.begin(), v.end());
I wonder if it is possible to create a more generic std::function<void()> generator, accepting a function template as a first argument and the function template arguments as a variable-length argument list? If not using built-in language features, maybe via a macro?
As long as you do not need to have template function return type, you can do this:
#include <functional>
#include <iostream>
#include <typeinfo>
template<typename ... T>
std::function<void()> makePrintRangeFunction(void (*f)(T...), T... param) {
return std::bind(f, param...);
}
template<typename T, typename V>
void print(T type, V val)
{
std::cout << typeid(type).name() << '\n' << val << '\n';
}
int main()
{
int i = 5;
double d = 10.5;
auto f = makePrintRangeFunction(print, i, d);
f();
}
Maybe the following code will help :)
template <class F, class... Args>
void test(F&& f, Args&&... args) {
std::function<typename std::result_of<F(Args...)>::type()> task(
std::bind(std::forward<F>(f), std::forward<Args>(args)...));
task();
}
If your compiler supports C++14 you could define a generic lambda wrapper as:
template<typename F>
auto fungen(F f) {
return [=](auto... args) { f(args...); };
}
Use case:
int main() {
std::vector<int> v {1, 2, 3, 4};
auto f = fungen(printRange<std::vector<int>::iterator>);
f(v.begin(), v.end());
}
Live Demo

boost::range::join for multiple ranges

I want to do the following:
std::vector<int> a = {1,2,3}, b = {4,5,6}, c = {7,8,9};
for(auto&& i : join(a,b,c)) {
i += 1
std::cout << i; // -> 2345678910
}
I tried using boost::range::join, this works fine:
auto r = boost::join(a,b);
for(auto&& i : boost::join(r,c)) {
i += 1;
std::cout << i; // -> 2345678910
}
Chaining joins, reading operations work:
for(auto&& i : boost::join(boost::join(a,b),c))
std::cout << i; // -> 123456789
However, writing doesn't work:
for(auto&& i : boost::join(boost::join(a,b),c)) {
i += 1; // Fails :(
std::cout << i;
}
My variadic join has the same problem, i.e. works for reading but not for writing:
template<class C> C&& join(C&& c) { return c; }
template<class C, class D, class... Args>
auto join(C&& c, D&& d, Args&&... args)
-> decltype(boost::join(boost::join(std::forward<C>(c), std::forward<D>(d)),
join(std::forward<Args>(args)...))) {
return boost::join(boost::join(std::forward<C>(c), std::forward<D>(d)),
join(std::forward<Args>(args)...));
}
Mehrdad gave the solution in the comments
template<class C>
auto join(C&& c)
-> decltype(boost::make_iterator_range(std::begin(c),std::end(c))) {
return boost::make_iterator_range(std::begin(c),std::end(c));
}
template<class C, class D, class... Args>
auto join(C&& c, D&& d, Args&&... args)
-> decltype(boost::join(boost::join(boost::make_iterator_range(std::begin(c),std::end(c)),
boost::make_iterator_range(std::begin(d),std::end(d))),
join(std::forward<Args>(args)...))) {
return boost::join(boost::join(boost::make_iterator_range(std::begin(c),std::end(c)),
boost::make_iterator_range(std::begin(d),std::end(d))),
join(std::forward<Args>(args)...));
}
There are two overloads of boost::join
template<typename SinglePassRange1, typename SinglePassRange2>
joined_range<const SinglePassRange1, const SinglePassRange2>
join(const SinglePassRange1& rng1, const SinglePassRange2& rng2)
template<typename SinglePassRange1, typename SinglePassRange2>
joined_range<SinglePassRange1, SinglePassRange2>
join(SinglePassRange1& rng1, SinglePassRange2& rng2);
When you do this
for(auto&& i : boost::join(boost::join(a,b), c)) {
// ^^^^ ^^^^ temporary here
// ||
// calls the const ref overload
You get a temporary joined_range and as those can only bind to const references, the first overload is selected which returns a range that doesn't allow modifying.
You can work around this if you avoid temporaries:
#include <boost/range.hpp>
#include <boost/range/join.hpp>
int main()
{
std::vector<int> a = {1,2,3}, b = {4,5,6}, c = {7,8,9};
auto range = boost::join(a,b);
for(int& i : boost::join(range,c)) {
i += 1;
std::cout << i;
}
}
Live demo.
I haven't looked into your variadic functions, but the problem is likely similar.
Here's a complete solution, which works correctly on GCC 12. For GCC 10 & 11, the subranges function can be used to obtain an array of subranges, which can then be used as the lhs argument to | std::views::join.
EDIT: These functions only return on ranges that have a common iterator type. If you don't have a common iterator type, one option is to create a new container from the ranges (which is probably not what you want), or to create a custom type with different sub-ranges (which can't be used with std::views::join).
#include <ranges>
#include <vector>
#include <iostream>
#include <tuple>
#include <array>
#include <algorithm>
namespace detail {
template<std::size_t N, typename... Ts>
struct has_common_type_helper {
using T1 = std::decay_t<std::tuple_element_t<N-1, std::tuple<Ts...>>>;
using T2 = std::decay_t<std::tuple_element_t<N-2, std::tuple<Ts...>>>;
static constexpr bool value = std::same_as<T1, T2> && has_common_type_helper<N-1, Ts...>::value;
};
template<typename... Ts>
struct has_common_type_helper<0, Ts...> : std::false_type {
static_assert(std::is_void_v<Ts...>, "Undefined for an empty parameter pack");
};
template<typename... Ts>
struct has_common_type_helper<1, Ts...> : std::true_type {};
template<typename T> struct iterator_types;
template<std::ranges::range... Ts>
struct iterator_types<std::tuple<Ts...>> {
using type = std::tuple<std::ranges::iterator_t<Ts>...>;
};
}
template<typename T>
struct has_common_type;
template<typename T1, typename T2>
struct has_common_type<std::pair<T1,T2>> {
static constexpr bool value = std::same_as<std::decay_t<T1>, std::decay_t<T2>>;
};
template <typename... Ts>
struct has_common_type<std::tuple<Ts...>> : detail::has_common_type_helper<sizeof...(Ts), Ts...> {};
template <typename T>
inline constexpr bool has_common_type_v = has_common_type<T>::value;
template<std::size_t I = 0, typename Array, typename... Ts, typename Func> requires (I == sizeof...(Ts))
void init_array_from_tuple(Array& a, const std::tuple<Ts...>& t, Func fn)
{
}
template<std::size_t I = 0, typename Array, typename... Ts, typename Func> requires (I < sizeof...(Ts))
void init_array_from_tuple(Array& a, const std::tuple<Ts...>& t, Func fn)
{
a[I] = fn(std::get<I>(t));
init_array_from_tuple<I+1>(a, t, fn);
}
template<std::ranges::range... Ranges>
auto subranges(Ranges&&... rngs)
{
using IteratorTypes = detail::iterator_types<std::tuple<Ranges...>>::type;
static_assert(has_common_type_v<IteratorTypes>);
using SubrangeT = std::ranges::subrange<std::tuple_element_t<0, IteratorTypes>>;
auto subrngs = std::array<SubrangeT, sizeof...(Ranges)>{};
auto t = std::tuple<Ranges&&...>{std::forward<Ranges>(rngs)...};
auto fn = [](auto&& rng) {
return std::ranges::subrange{rng.begin(), rng.end()};
};
init_array_from_tuple(subrngs, t, fn);
return subrngs;
}
#if __GNUC__ >= 12
template<std::ranges::range... Ranges>
auto join(Ranges&&... rngs)
{
return std::ranges::owning_view{subranges(std::forward<Ranges>(rngs)...) | std::views::join};
}
#endif
int main()
{
std::vector<int> v1{1,2,3};
std::vector<int> v2{4};
std::vector<int> v3{5,6};
#if __GNUC__ >= 12
std::ranges::copy(join(v1,v2,v3,v1), std::ostream_iterator<int>(std::cout, " "));
#else
auto subrngs = subranges(v1,v2,v3,v1);
std::ranges::copy(subrngs | std::views::join, std::ostream_iterator<int>(std::cout, " "));
#endif
std::cout << '\n';
return 0;
}
Here's an implementation that works for two different ranges with a common reference type. You can extend it to 3 ranges using a brute-force approach.
#include <ranges>
#include <vector>
#include <list>
#include <iostream>
#include <algorithm>
template<std::ranges::range Range1, std::ranges::range Range2>
auto join2(Range1&& rng1, Range2&& rng2)
{
using Ref1 = std::ranges::range_reference_t<Range1>;
using Ref2 = std::ranges::range_reference_t<Range2>;
using Ref = std::common_reference_t<Ref1, Ref2>;
class Iter {
public:
using value_type = std::remove_cv_t<std::remove_reference_t<Ref>>;
using difference_type = std::ptrdiff_t;
Iter() = default;
Iter(Range1&& rng1_, Range2&& rng2_, bool begin)
: m_it1{begin ? rng1_.begin() : rng1_.end()}
, m_it2{begin ? rng2_.begin() : rng2_.end()}
, m_e1{rng1_.end()} {}
bool operator==(const Iter& rhs) const {
return m_it1 == rhs.m_it1 && m_it2 == rhs.m_it2;
}
Ref operator*() const {
return m_it1 != m_e1 ? *m_it1 : *m_it2;
}
Iter& operator++() {
(m_it1 != m_e1) ? (void)++m_it1 : (void)++m_it2;
return *this;
}
Iter operator++(int) {
Iter ret = *this;
++(*this);
return ret;
}
private:
std::ranges::iterator_t<Range1> m_it1;
std::ranges::iterator_t<Range2> m_it2;
std::ranges::iterator_t<Range1> m_e1;
};
static_assert(std::forward_iterator<Iter>);
auto b = Iter{std::forward<Range1>(rng1), std::forward<Range2>(rng2), true};
auto e = Iter{std::forward<Range1>(rng1), std::forward<Range2>(rng2), false};
return std::ranges::subrange<Iter>{b, e};
}
int main()
{
std::vector<int> v{1,2,3};
std::list<int> l{4,5,6};
std::ranges::copy(join2(v,l), std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
return 0;
}
P.S. I am not optimistic about a variadic implementation, although I'm sure someone smarter than me would be able to figure it out.