Skip to content

Instantly share code, notes, and snippets.

@bisqwit
Last active February 3, 2019 18:58
Show Gist options
  • Save bisqwit/a263ac9f9b57d32559042677a32afa9d to your computer and use it in GitHub Desktop.
Save bisqwit/a263ac9f9b57d32559042677a32afa9d to your computer and use it in GitHub Desktop.
Add another example
#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));
}
@mincrmatt12
Copy link

Why the templated lambda instead of just a function?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment