The question is follow-up to preprocessing iterator.
Specification:
Given
Functor functor
(which isCallable
) andIterator iterator
(which isOutputIterator
), iterator is created that behaves the same as underlying iterator, except it applies a functor to the incoming data and then passes the result into underlying iterator. Input type of theFunctor
is not required to match value type of theIterator
, but the input should be only single parameter (even defaulted arguments are not allowed).
Code:
#ifndef SUNRISE_TRANSFORM_ITERATOR_HPP
#define SUNRISE_TRANSFORM_ITERATOR_HPP
#include <iterator>
namespace shino
{
template<typename Functor, typename Iterator>
class transform_iterator :
public std::iterator<std::output_iterator_tag,
void, void, void, void>
{
Functor functor;
Iterator iterator;
public:
transform_iterator(const Functor& f, const Iterator& it) :
functor(f),
iterator(it)
{}
transform_iterator(Functor&& f, Iterator&& it) :
functor(f),
iterator(it)
{}
class proxy
{
friend class transform_iterator;
Iterator &iterator;
Functor &f;
public:
template <typename U>
proxy &operator=(U&& value)
{
*iterator = f(std::forward<U>(value));
return *this;
}
private:
proxy(Iterator &it, Functor &functor) :
iterator(it),
f(functor)
{}
};
proxy operator*()
{
return proxy(iterator, functor);
}
transform_iterator &operator++()
{
++iterator;
return *this;
}
transform_iterator operator++(int)
{
auto copy = *this;
++iterator; //might exhibit different behavior sometimes
return copy;
}
const Iterator& internal_iterator() const
{
return iterator;
}
const Functor& internal_functor() const
{
return functor;
}
void swap(transform_iterator& other)
{
using std::swap;
swap(other.functor, functor);
swap(other.iterator, iterator);
}
};
template<typename Functor, typename Iterator>
bool operator==(const transform_iterator<Functor, Iterator>& lhs,
const transform_iterator<Functor, Iterator>& rhs)
{
return lhs.internal_iterator() == rhs.internal_iterator();
}
template <typename Functor, typename Iterator>
bool operator!=(const transform_iterator<Functor, Iterator>& lhs,
const transform_iterator<Functor, Iterator>& rhs)
{
return !(lhs == rhs);
}
template <typename Functor, typename Iterator>
void swap(shino::transform_iterator<Functor, Iterator>& lhs,
shino::transform_iterator<Functor, Iterator>& rhs)
{
lhs.swap(rhs);
}
template <typename Functor, typename Iterator>
auto transformer(Functor&& f, Iterator&& iterator)
{
return transform_iterator<std::remove_const_t<std::remove_reference_t <Functor>>,
std::remove_const_t<std::remove_reference_t<Iterator>>>(std::forward<Functor>(f),
std::forward<Iterator>(iterator));
}
}
#endif //SUNRISE_TRANSFORM_ITERATOR_HPP
I don't have any special concerns, but anything, even small nitpicks are appreciated (I had serious flaw in transformer<>()
during implementation, so I want to get rid of all of the dangerous things).
This post shares example code with sliding window, because it is meant to be paired with it:
#include <vector>
#include <iostream>
#include <utility>
template <typename InputIt, typename OutputIt>
std::pair<InputIt, OutputIt> sliding_average(InputIt first, InputIt last,
const typename std::iterator_traits<InputIt>::difference_type window_length,
OutputIt d_first)
{
using value_type = typename std::iterator_traits<InputIt>::value_type;
auto divide = [&window_length](const value_type& value)
{
return value / window_length;
};
auto iterator = shino::transformer(divide, d_first); //transform_iterator<Functor, Iterator>
auto result = shino::sliding_window(first, last, iterator, window_length);
return std::make_pair(result.first, result.second.internal_iterator());
}
The example might not be so appealing, but currently I have lack of imagination to write something wonderful small enough to not be applicable for its own review.
Semiregular
, adapt iterator categories (if Iterator is RAI make sure to be RAI too, otherwise not), usestd::decay_t<T>
instead ofstd::remove_const_t<std::remove_reference_t<T>>
and use constraints on your template arguments... \$\endgroup\$