Last active
February 3, 2019 18:58
-
-
Save bisqwit/a263ac9f9b57d32559042677a32afa9d to your computer and use it in GitHub Desktop.
Add another example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <memory> | |
#include <iterator> | |
#include <typeindex> | |
/* Tiny transform_iterator for C++. Copyright 2018 © Joel Yliluoma - http://iki.fi/bisqwit/ | |
* License: MIT | |
Example WITHOUT transform_iterator: | |
template<typename Func> | |
std::vector<int> make_transformed_vector(const std::vector<int>& model, Func&& func) | |
{ | |
std::vector<int> result; // Create named temporary | |
std::transform(model.begin(), model.end(), std::back_inserter(result), std::forward<Func>(func)); | |
return result; | |
} | |
int main() | |
{ | |
std::vector<int> test{6,10,5}; | |
std::vector<int> test2 = make_transformed_vector(test, [&](int v) { return v*2; }); | |
for(auto d: test2) std::printf("%d\n", d); | |
} | |
Example use, WITH transform_iterator: | |
#include "transform_iterator.hh" | |
template<typename Func> | |
std::vector<int> make_transformed_vector(const std::vector<int>& model, Func&& func) | |
{ | |
// Look ma, no named temporaries! | |
return std::vector<int> ( make_transform_iterator(model.begin(), model.end(), std::forward<Func>(func)), | |
transform_iterator<int>() ); // int is the return type of the functor. | |
} | |
int main() | |
{ | |
std::vector<int> test{6,10,5}; | |
std::vector<int> test2 = make_transformed_vector(test, [&](int v) { return v*2; }); | |
for(auto d: test2) std::printf("%d\n", d); | |
} | |
Another example: | |
std::vector<int> values{1,2,3,4}; | |
// Initialize a std::map with string values corresponding to each int key from values | |
std::map<int, std::string> ints | |
{ | |
make_transform_iterator(values.begin(),values.end(), | |
[&](int v){return std::pair(v,std::to_string(v)); }), | |
transform_iterator<std::pair<int,std::string>>{} | |
}; | |
*/ | |
template<typename R> | |
struct transform_iterator | |
{ | |
public: | |
using iterator_category = std::input_iterator_tag; | |
using value_type = std::decay_t<R>; | |
using difference_type = std::ptrdiff_t; | |
using pointer = value_type*; | |
using reference = value_type&; | |
using const_pointer = const value_type*; | |
using const_reference = const value_type&; | |
private: | |
struct transform_iterator_base | |
{ | |
virtual ~transform_iterator_base() = default; | |
protected: | |
transform_iterator_base() = default; | |
private: | |
friend struct transform_iterator<R>; | |
virtual bool is_end() const = 0; | |
virtual bool eq(const transform_iterator_base&) const = 0; | |
virtual void delta(std::ptrdiff_t) = 0; | |
virtual R ref() const = 0; | |
virtual transform_iterator_base* clone() const = 0; | |
}; | |
std::unique_ptr<transform_iterator_base> ptr {}; | |
public: | |
transform_iterator() = default; | |
transform_iterator(const transform_iterator& b) : ptr(b.ptr ? b.ptr->clone() : nullptr) {} | |
transform_iterator(transform_iterator&& b) = default; | |
transform_iterator& operator= (transform_iterator&& p) = default; | |
transform_iterator& operator= (const transform_iterator& b) | |
{ | |
if(this != &b) ptr.reset(b.ptr ? b.ptr->clone() : nullptr); | |
return *this; | |
} | |
bool operator!=(const transform_iterator<R>& b) const { return !operator==(b); } | |
bool operator==(const transform_iterator<R>& b) const | |
{ | |
return ptr ? (b.ptr ? ptr->eq(*b.ptr) : ptr->is_end()) | |
: (b.ptr ? b.ptr->is_end() : true); | |
} | |
R operator* () const { return ptr->ref(); } | |
transform_iterator<R>& operator++() { if(ptr) ptr->delta(1); return *this; } | |
transform_iterator<R>& operator--() { if(ptr) ptr->delta(-1); return *this; } | |
transform_iterator<R>& operator+=(std::ptrdiff_t p) { if(ptr) ptr->delta(p); return *this; } | |
transform_iterator<R>& operator-=(std::ptrdiff_t p) { if(ptr) ptr->delta(-p); return *this; } | |
transform_iterator<R> operator++(int) { transform_iterator<R> result(*this); ++*this; return result; } | |
transform_iterator<R> operator--(int) { transform_iterator<R> result(*this); --*this; return result; } | |
transform_iterator<R> operator+(std::ptrdiff_t p) const { transform_iterator<R> result(*this); result+=p; return result; } | |
transform_iterator<R> operator-(std::ptrdiff_t p) const { transform_iterator<R> result(*this); result-=p; return result; } | |
private: | |
transform_iterator(transform_iterator_base* p) : ptr(p) {} | |
template<typename I, typename F> | |
friend auto make_transform_iterator(const I& begin,const I&,F&& func) -> transform_iterator<decltype(func(*begin))>; | |
template<typename I, typename F> | |
struct transform_iterator_spec final: public transform_iterator_base | |
{ | |
transform_iterator_spec(const I& b, const I& e, F&& f) : transform_iterator_base(), | |
cur(b), end(e), func(std::forward<F>(f)) {} | |
private: | |
// These should be never invoked, but I guess defaults are fine nonetheless | |
transform_iterator_spec(transform_iterator_spec&&) = default; | |
transform_iterator_spec(const transform_iterator_spec&) = default; | |
transform_iterator_spec& operator=(transform_iterator_spec&&) = default; | |
transform_iterator_spec& operator=(const transform_iterator_spec&) = default; | |
private: | |
bool eq(const transform_iterator_base& b) const override { return std::type_index(typeid(*this)) == std::type_index(typeid(b)) | |
&& cur == ((const transform_iterator_spec<I,F>&)b).cur; } | |
bool is_end() const override { return cur == end; } | |
void delta(std::ptrdiff_t by) override { std::advance(cur, by); } | |
R ref() const override { return func(*cur); } | |
transform_iterator_base* clone() const override { return new transform_iterator_spec<I,F>(cur,end,std::forward<F>(func)); } | |
private: | |
I cur, end; | |
F&& func; | |
}; | |
}; | |
template<typename I, typename F> | |
auto make_transform_iterator(const I& begin, const I& end, F&& func) | |
-> transform_iterator<decltype(func(*begin))> | |
{ | |
using R = decltype(func(*begin)); | |
return new typename transform_iterator<R>::template transform_iterator_spec<I,F>(begin, end, std::forward<F>(func)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Why the templated lambda instead of just a function?