///////////////////////////////////////////////////////////////////////////////
// COPYRIGHT HEADER ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// C++ Views and Pipes
// by John Ryland
// (c) Copyright 2019
//
///////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <sstream>
#include <array>
#include <regex>
///////////////////////////////////////////////////////////////////////////////
// TYPE TRAITS ////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Container Traits ///////////////////////////////////////////////////////////
template <typename T, typename _ = void>
struct has_container_traits : std::false_type {};
template <typename... Ts>
struct has_container_traits_helper {};
template <typename T>
struct has_container_traits<T, std::conditional_t<false, has_container_traits_helper<
// Const Container Trails:
typename T::value_type,
typename T::const_iterator,
typename T::const_reverse_iterator,
decltype(std::declval<T>().crbegin()),
decltype(std::declval<T>().crend()),
decltype(std::declval<T>().cbegin()),
decltype(std::declval<T>().cend())
>, void>> : public std::true_type {};
template <typename Container,
typename std::enable_if<has_container_traits<Container>{}, bool>::type = true>
struct is_container
{
using type = bool;
};
template <typename Container>
using is_container_t = typename is_container<Container>::type;
// View Traits ////////////////////////////////////////////////////////////////
template <typename T, typename _ = void>
struct has_view_traits : std::false_type {};
template <typename... Ts>
struct has_view_traits_helper {};
/*
template <typename T>
class view // Not actually needed, but defines the expected interface of a view
{
public:
// It is expected that it should be possible to go to the first or last, and from there
// to request the next or previous, and also to then move in either direction.
using element_type = T;
virtual bool first() = 0; // sets current to first (returns false if none)
virtual bool last() = 0; // sets current to last (returns false if none)
virtual bool next() = 0; // moves current to next (returns false if current is last)
virtual bool previous() = 0; // moves current to previous (returns false if current is first)
virtual const T& current() = 0; // returns current (defaults to first)
};
*/
template <typename T>
struct has_view_traits<T, std::conditional_t<false, has_view_traits_helper<
// View Traits:
typename T::element_type,
decltype(std::declval<T>().first()),
decltype(std::declval<T>().last()),
decltype(std::declval<T>().next()),
decltype(std::declval<T>().previous()),
decltype(std::declval<T>().current())
>, void>> : public std::true_type {};
template <typename View,
typename std::enable_if<has_view_traits<View>{}, bool>::type = true>
struct is_view
{
using type = bool;
};
template <typename View>
using is_view_t = typename is_view<View>::type;
///////////////////////////////////////////////////////////////////////////////
// VIEW TYPES /////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Container View /////////////////////////////////////////////////////////////
template <typename Container, typename BaseType, is_container_t<BaseType> = true>
class container_wrapper_view
{
public:
container_wrapper_view(Container a_container)
: m_container(a_container)
{
}
// implements View
using element_type = typename BaseType::value_type;
bool first() { m_it = m_container.cbegin(); return m_it != m_container.cend(); }
bool last() { m_it = std::prev(m_container.cend()); return m_it != std::prev(m_container.cbegin()); }
bool next() { m_it = std::next(m_it); return m_it != m_container.cend(); }
bool previous() { m_it = std::prev(m_it); return m_it != std::prev(m_container.cbegin()); }
const element_type& current() { return *m_it; }
private:
Container m_container;
typename BaseType::const_iterator m_it;
};
// Range View /////////////////////////////////////////////////////////////////
template <typename T>
class range_view
{
public:
range_view(T start, T end)
: m_start(start)
, m_end(end)
{
}
// implements View
using element_type = T;
bool first() { m_current = m_start; return m_start <= m_end; }
bool last() { m_current = m_end; return m_start <= m_end; }
bool next() { ++m_current; return m_current <= m_end; }
bool previous() { --m_current; return m_current >= m_start; }
const T& current() { return m_current; }
private:
T m_current;
T m_start;
T m_end;
};
// Match View ////////////////////////////////////////////////////////////////
template <typename ParentView, typename Functor, is_view_t<ParentView> = true>
class match_view
{
public:
using element_type = typename ParentView::element_type;
match_view(const ParentView& a_parentView, const Functor& a_match)
: m_parentView(a_parentView)
, m_match(a_match)
{
}
// implements View
bool first() { if (!m_parentView.first()) return false; return nextValid(); }
bool last() { if (!m_parentView.last()) return false; return previousValid(); }
bool next() { if (!m_parentView.next()) return false; return nextValid(); }
bool previous() { if (!m_parentView.previous()) return false; return previousValid(); }
const element_type& current() { return m_parentView.current(); }
private:
bool nextValid() { while (!m_match(m_parentView.current())) { if (!m_parentView.next()) return false; } return true; }
bool previousValid() { while (!m_match(m_parentView.current())) { if (!m_parentView.previous()) return false; } return true; }
ParentView m_parentView;
Functor m_match;
};
// Filter View ////////////////////////////////////////////////////////////////
template <typename ParentView, is_view_t<ParentView> = true>
class filter_view : public match_view<ParentView, std::function<bool(typename ParentView::element_type)>>
{
public:
// inherits View
using element_type = typename ParentView::element_type;
using function_type = std::function<bool(element_type)>;
filter_view(const ParentView& a_parentView, const function_type& a_filter)
: match_view<ParentView, function_type>(a_parentView, [a_filter](element_type a_item) { return !a_filter(a_item); })
{
}
};
// Transform View /////////////////////////////////////////////////////////////
template <typename ParentView, typename Functor, typename ReturnType, is_view_t<ParentView> = true>
class transform_view
{
public:
using parent_element_type = typename ParentView::element_type;
transform_view(const ParentView& a_parentView, const Functor& a_transform)
: m_parentView(a_parentView)
, m_transform(a_transform)
{
}
// implements View
using element_type = ReturnType;
bool first() { return m_parentView.first(); }
bool last() { return m_parentView.last(); }
bool next() { return m_parentView.next(); }
bool previous() { return m_parentView.previous(); }
element_type current() { return m_transform(m_parentView.current()); } // NOTE: Transformed elements are returned by value
private:
ParentView m_parentView;
Functor m_transform;
};
// Reverse View ///////////////////////////////////////////////////////////////
template <typename ParentView, is_view_t<ParentView> = true>
class reverse_view
{
public:
reverse_view(const ParentView& view)
: m_parent(view)
{
}
// implements View
using element_type = typename ParentView::element_type;
bool first() { return m_parent.last(); }
bool last() { return m_parent.first(); }
bool next() { return m_parent.previous(); }
bool previous() { return m_parent.next(); }
const element_type& current() { return m_parent.current(); }
private:
element_type m_current;
ParentView m_parent;
};
// Skip View //////////////////////////////////////////////////////////////////
template <typename ParentView, is_view_t<ParentView> = true>
class skip_view
{
public:
using element_type = typename ParentView::element_type;
skip_view(const ParentView& a_parentView, size_t a_count)
: m_parentView(a_parentView)
, m_count(a_count)
{
}
// implements View
bool first() { if (!m_parentView.first()) return false; return nextValid(); }
bool last() { if (!m_parentView.last()) return false; return previousValid(); }
bool next() { return m_parentView.next(); }
bool previous() { return m_parentView.previous(); }
const element_type& current() { return m_parentView.current(); }
private:
bool nextValid() { while (m_count >= 1) { if (!m_parentView.next()) return false; m_count--; } return true; }
bool previousValid() { while (m_count >= 1) { if (!m_parentView.previous()) return false; m_count--; } return true; }
ParentView m_parentView;
size_t m_count;
};
// Take View //////////////////////////////////////////////////////////////////
template <typename ParentView, is_view_t<ParentView> = true>
class take_view
{
public:
using element_type = typename ParentView::element_type;
take_view(const ParentView& a_parentView, size_t a_count)
: m_parentView(a_parentView)
, m_count(a_count)
{
}
// implements View
bool first() { return m_parentView.first(); }
bool last() {
size_t count = m_count - 1;
if (count <= 0 || !m_parentView.first()) return false;
while (count)
{
if (!m_parentView.next()) return false;
count--;
}
return true;
}
bool next() { m_count--; return (m_count > 0) ? m_parentView.next() : false; }
bool previous() { m_count--; return (m_count > 0) ? m_parentView.previous() : false; }
const element_type& current() { return m_parentView.current(); }
private:
ParentView m_parentView;
size_t m_count;
};
///////////////////////////////////////////////////////////////////////////////
// WRAPPERS ///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Container Wrapper //////////////////////////////////////////////////////////
// creates a const reference container view
template <typename Container, is_container_t<Container> = true>
auto container_view(const Container& a_container)
{
return container_wrapper_view<const Container&, Container>(a_container);
}
// creates a container view by value
template <typename Container, is_container_t<Container> = true>
auto container_copy_view(const Container& a_container)
{
return container_wrapper_view<Container, Container>(a_container);
}
// Range Wrapper //////////////////////////////////////////////////////////////
template <typename T>
auto range(T start, T end)
{
return range_view<T>(start, end);
}
// Array Wrapper //////////////////////////////////////////////////////////////
template <class T, std::size_t N, std::size_t... I>
constexpr auto to_array_impl(T (&a)[N], std::index_sequence<I...>)
{
return std::array<std::remove_cv_t<T>, N>{ { a[I]... } };
}
template <class T, std::size_t N, std::size_t... I>
constexpr auto to_array_impl(T (&&a)[N], std::index_sequence<I...>)
{
return std::array<std::remove_cv_t<T>, N>{ { std::move(a[I])... } };
}
template <class T, std::size_t N>
constexpr auto to_array(T (&a)[N])
{
return to_array_impl(a, std::make_index_sequence<N>{});
}
template <class T, std::size_t N>
constexpr auto to_array(T (&&a)[N])
{
return to_array_impl(std::move(a), std::make_index_sequence<N>{});
}
// Print Wrapper /////////////////////////////////////////////////////////////
class print_class
{
// No state, just the class as a tag
};
// Reverse Wrapper ////////////////////////////////////////////////////////////
class reverse_class
{
// No state, just the class as a tag
};
// Match Wrapper //////////////////////////////////////////////////////////////
template <typename Functor>
class match_class
{
public:
match_class(Functor&& a_m)
: m_m(a_m)
{
}
Functor m_m;
};
// Filter Wrapper /////////////////////////////////////////////////////////////
template <typename Functor>
class filter_class
{
public:
filter_class(Functor&& a_f)
: m_f(a_f)
{
}
Functor m_f;
};
// Transform Wrapper //////////////////////////////////////////////////////////
template <typename Functor>
class transform_class
{
public:
transform_class(Functor&& t)
: m_t(t)
{
}
Functor m_t;
};
///////////////////////////////////////////////////////////////////////////////
// COMMANDS ///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Print Command //////////////////////////////////////////////////////////////
print_class print;
// Reverse Command ////////////////////////////////////////////////////////////
reverse_class reverse;
// Match Command //////////////////////////////////////////////////////////////
template <typename Functor>
auto match(Functor&& a_m)
{
return match_class<Functor>(std::forward<Functor>(a_m));
}
// Grep Command ///////////////////////////////////////////////////////////////
// Match a regular expression
auto grep(const std::string& a_re)
{
std::regex re(a_re);
auto f = [re](const std::string& a_s) { return std::regex_search(a_s, re); };
return match_class<decltype(f)>(std::move(f));
}
// Filter Command /////////////////////////////////////////////////////////////
template <typename Functor>
auto filter(Functor&& a_f)
{
return filter_class<Functor>(std::forward<Functor>(a_f));
}
// Take Command ///////////////////////////////////////////////////////////////
class take
{
public:
take(size_t a_count)
: m_count(a_count)
{
}
size_t m_count;
};
// Head Command ///////////////////////////////////////////////////////////////
using head = take;
// First Command //////////////////////////////////////////////////////////////
using first = take;
// Last Command ///////////////////////////////////////////////////////////////
class last
{
public:
last(size_t a_count)
: m_count(a_count)
{
}
size_t m_count;
};
// Tail Command ///////////////////////////////////////////////////////////////
using tail = last;
// Skip Command ///////////////////////////////////////////////////////////////
class skip
{
public:
skip(size_t a_count)
: m_count(a_count)
{
}
size_t m_count;
};
// Drop Command ///////////////////////////////////////////////////////////////
using drop = skip;
// Transform Command //////////////////////////////////////////////////////////
template <typename Functor>
auto transform(Functor&& a_f)
{
return transform_class<Functor>(std::forward<Functor>(a_f));
}
// Convert Command ////////////////////////////////////////////////////////////
template <typename Functor>
auto convert(Functor&& a_f)
{
return transform_class<Functor>(std::forward<Functor>(a_f));
}
// Join Command ///////////////////////////////////////////////////////////////
class join
{
public:
join(const std::string& s)
: m_s(s)
{
}
std::string m_s;
};
// Split Command //////////////////////////////////////////////////////////////
class split
{
public:
split(const std::string& a_delimiters)
: m_delimiters(a_delimiters)
{
}
std::string m_delimiters;
};
///////////////////////////////////////////////////////////////////////////////
// PIPES //////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Container Pipe /////////////////////////////////////////////////////////////
template <typename Container, typename View, is_container_t<Container> = true>
auto operator|(const Container& c, const View& v)
{
return container_view(c) | v;
}
// Array Pipe /////////////////////////////////////////////////////////////////
template <typename ArrayT, std::size_t ArrayN, typename View>
auto operator|(const ArrayT (&a)[ArrayN], const View& v)
{
auto c = to_array(a);
return container_view(c) | v;
}
// Reverse Pipe ///////////////////////////////////////////////////////////////
template <typename View, is_view_t<View> = true>
auto operator|(const View& v1, const reverse_class& r)
{
return reverse_view<View>(v1);
}
// Match Pipe ////////////////////////////////////////////////////////////////
template <typename View, typename Functor, is_view_t<View> = true>
auto operator|(const View& v1, const match_class<Functor>& m)
{
return match_view<View, Functor>(v1, m.m_m);
}
// Filter Pipe ////////////////////////////////////////////////////////////////
template <typename View, typename Functor, is_view_t<View> = true>
auto operator|(const View& v1, const filter_class<Functor>& f)
{
return filter_view<View>(v1, f.m_f);
}
// Take Pipe //////////////////////////////////////////////////////////////////
template <typename View, is_view_t<View> = true>
auto operator|(const View& v1, const take& t)
{
return take_view<View>(v1, t.m_count);
}
// Last Pipe //////////////////////////////////////////////////////////////////
template <typename View, is_view_t<View> = true>
auto operator|(const View& v1, const last& l)
{
return v1 | reverse | take(l.m_count) | reverse;
}
// Skip Pipe //////////////////////////////////////////////////////////////////
template <typename View, is_view_t<View> = true>
auto operator|(const View& v1, const skip& s)
{
return skip_view<View>(v1, s.m_count);
}
// Transform Pipe /////////////////////////////////////////////////////////////
template <typename View, typename Functor, is_view_t<View> = true>
auto operator|(const View& v1, const transform_class<Functor>& t)
{
return transform_view<View, Functor, decltype(t.m_t((typename View::element_type){}))>(v1, t.m_t);
}
// Join Pipe //////////////////////////////////////////////////////////////////
std::string operator|(const std::string& s, const join& j)
{
return s;
}
std::string operator|(const char* s, const join& j)
{
return s;
}
template <typename View, is_view_t<View> = true>
std::string operator|(const View& c, const join& j)
{
std::stringstream ss;
std::string deliminator = j.m_s;
apply(c, [&ss, deliminator](const auto& a, bool f, bool l) { ss << a; if (!l) ss << deliminator; } );
return ss.str();
}
// Split Pipe /////////////////////////////////////////////////////////////////
// // Input can be a string or a view
// template <typename View>
template <typename View, is_view_t<View> = true>
auto operator|(const View& v1, const split& s)
{
// https://stackoverflow.com/questions/236129/how-do-i-iterate-over-the-words-of-a-string
std::string str = v1 | join("");
std::string delimiters = s.m_delimiters;
std::vector<std::string> tokens;
std::size_t start = 0, end, length = str.length();
while (length && start < length + 1)
{
end = str.find_first_of(delimiters, start);
if (end == std::string::npos)
{
end = length;
}
std::string token = std::string(str.data() + start, end - start);
tokens.push_back(token);
start = end + 1;
}
// No warnings from the compiler that this will blow up
// it takes a reference to the tokens from the stack
// Now changed to a container_copy_view so it doesn't blow up
// and works correctly, but might not be as efficient as possible.
// Ideally the compiler should give a warning. Even better the
// language should be designed so you can't even make incorrect
// programs like that.
return container_copy_view<std::vector<std::string>>(tokens);
// return container_view<std::vector<std::string>>(tokens);
}
// It should spit out string_spans which are views in to the original string - don't need to copy the string
/*
// implements RangeView
template <typename ParentView>
class split_string_view
{
public:
using element_type = typename ParentView::element_type;
split_string_view(const std::string& a_parentView, const std::string& a_delimiters = " ")
: m_parentView(a_parentView)
, m_delimiteres(a_delimiters)
{
}
bool first() { if (!m_parentView.first()) return false; return nextValid(); }
bool last() { if (!m_parentView.last()) return false; return previousValid(); }
bool next() { if (!m_parentView.next()) return false; return nextValid(); }
bool previous() { if (!m_parentView.previous()) return false; return previousValid(); }
const element_type& current() { return m_parentView.current(); }
private:
bool nextValid() { while (!m_filter(m_parentView.current())) { if (!m_parentView.next()) return false; } return true; }
bool previousValid() { while (!m_filter(m_parentView.current())) { if (!m_parentView.previous()) return false; } return true; }
const std::string& m_parentView;
const std::string& m_delimiters;
};
*/
// Print Pipe /////////////////////////////////////////////////////////////////
template <typename View, is_view_t<View> = true>
void operator|(const View& v1, const print_class& r)
{
apply(v1, [](const auto& a, bool f, bool l) { std::cout << a; if (!l) std::cout << ","; } );
std::cout << std::endl;
}
void operator|(const std::string& s, const print_class& r)
{
std::cout << s << std::endl;
}
// Apply Pipe /////////////////////////////////////////////////////////////////
template <typename View, typename Functor, is_view_t<View> = true>
void for_each(const View& a_iterable, Functor&& f)
{
View it = a_iterable; // takes a copy of the view, views should be cheap to copy
if (it.first())
{
do
{
f(it.current());
} while (it.next());
}
}
template <typename View, typename Functor, is_view_t<View> = true>
void apply(const View& a_iterable, Functor&& f)
{
bool first = true;
bool last = false;
View it = a_iterable; // takes a copy of the view, views should be cheap to copy
if (it.first())
{
do
{
auto current = it.current();
last = !it.next();
f(current, first, last);
first = false;
} while (!last);
}
}
template <typename View, is_view_t<View> = true>
void operator|(const View& v, const std::function<void(typename View::element_type)>& f)
{
for_each(v, f);
}
template <typename Container, is_container_t<Container> = true>
void operator|(const Container& c, const std::function<void(typename Container::value_type)>& f)
{
std::for_each(c.begin(), c.end(), f);
}
// Generic Pipe ///////////////////////////////////////////////////////////////
/*
template <typename View1, typename View2>
auto pipe(const View1& in, const View2& next)
{
return true;
}
template <typename View1, typename View2>
auto operator|(const View1& in, const View2& next)
{
return pipe(in, next);
}
template <typename R, typename View1, typename View2>
R pipe(const View1& in, const View2& next)
{
static_assert(false, "no specialization found");
}
*/
void demo1()
{
std::cout << "Demo" << std::endl;
auto strings = to_array({"one","two","three","four","five"});
std::cout << "Printing strings" << std::endl;
strings | print;
std::cout << "Reversing strings" << std::endl;
strings | reverse | print;
std::cout << "Matching strings with 'o'" << std::endl;
strings | match([](std::string a){ return a.find("o") != std::string::npos; }) | print;
std::cout << "Grepping strings for 'one|two|four'" << std::endl;
strings | grep("one|two|four") | print;
std::cout << "Filtering strings without 'o'" << std::endl;
strings | filter([](std::string a){ return a.find("o") == std::string::npos; }) | print;
std::cout << "Taking 3 strings" << std::endl;
strings | take(3) | print;
std::cout << "Head 3 strings" << std::endl;
strings | head(3) | print;
std::cout << "First 3 strings" << std::endl;
strings | first(3) | print;
std::cout << "Last 3 strings" << std::endl;
strings | last(3) | print;
std::cout << "Tail 3 strings" << std::endl;
strings | tail(3) | print;
std::cout << "Skipping 2 strings" << std::endl;
strings | skip(2) | print;
std::cout << "Dropping 2 strings" << std::endl;
strings | drop(2) | print;
std::cout << "Transforming strings to lengths" << std::endl;
strings | transform([](std::string a) { return a.length(); }) | print;
std::cout << "Converting strings to lengths" << std::endl;
strings | convert([](std::string a) { return a.size(); }) | print;
std::cout << "Joining strings with '.'" << std::endl;
strings | join(".") | print;
std::cout << "Splitting strings with '.'" << std::endl;
strings | join(".") | split(".") | print;
}
void demo2()
{
// Functions
auto printer = [](auto x) { std::cout << x << ","; };
auto newline = []() { std::cout << std::endl; };
// Input
std::vector<int> v = {1,2,3,4,5};
// Normal way
for (auto i : v)
printer(i);
std::cout << std::endl;
// C++11 functional way
std::for_each(v.begin(), v.end(), printer);
std::cout << std::endl;
// Improved way
v | printer;
newline();
// Best way
v | print;
// More interesting options
v | reverse | print;
}
//auto test = [](auto testCond, auto expect) { std::cout << std::endl; };
#define CHECK_EQUAL(lhs, rhs) assert((lhs) == (rhs));
void test()
{
CHECK_EQUAL(range(10,15) | join(","), "10,11,12,13,14,15");
CHECK_EQUAL(range(10,15) | reverse | join(","), "15,14,13,12,11,10");
}
void test_utf8()
{
// UTF-8 test (4 characters, 8 bytes)
char str[] = "αγρω";
printf("%ld %ld\n", sizeof(str), strlen(str));
// Latin-1 (4 characters, 4 bytes)
char str2[] = "1234";
printf("%ld %ld\n", sizeof(str2), strlen(str2));
}
int main()
{
test_utf8();
test();
demo1();
demo2();
std::stringstream ss;
auto printer = [](auto x) { std::cout << x << ","; };
auto newline = []() { std::cout << std::endl; };
auto odd = [](int x) -> bool { return (x & 1) == 1; };
auto as_string = [](auto x) -> std::string { return std::to_string(x); };
int v2[] = {1,2,3,4,5};
v2 | print;
int v3[]{1,2,3,4,5};
v3 | print;
auto v4 = to_array<int>({1,2,3,4,5});
v4 | print;
to_array({1,2,3,4,5}) | print;
std::string("13,15,66,42,23") | split(",") | join(".") | print;
"13,15,66,42,23" | split(",") | join(".") | print;
"13,15,66,42,23" | split(",") | grep("3") | join(".") | print;
auto sl = "13,15,66,42,23,123" | split(",");
auto s = sl | join(".");
std::cout << s << std::endl;
newline();
range(10,15) | print;
std::cout << (range(10,15) | transform(as_string) | join(",")) << std::endl;
for_each(range(10,15), printer);
newline();
for_each(reverse_view<range_view<int>>(range(10,15)), printer);
newline();
/*
for_each(filter_view(reverse_view(range(10,15)), odd), printer);
newline();
for_each(filter_view(reverse_view(range(9,15)), odd), printer);
newline();
for_each(filter_view(range(9,15), odd), printer);
newline();
for_each(reverse_view(range(9,15)), printer);
newline();
for_each(filter_view(reverse_view(range(9,14)), odd), printer);
newline();
for_each(filter_view(reverse_view(range(10,14)), odd), printer);
newline();
for_each(reverse_view(range(10,14)) | filter(odd), printer);
newline();
*/
for_each(range(10,14) | reverse | filter(odd), printer);
newline();
std::vector<int> v = {1,2,3,4,5};
container_view(v) | print;
container_view(v) | match(odd) | print;
container_view(v) | filter(odd) | print;
container_view(v) | reverse | print;
container_view(v) | reverse | reverse | print;
container_view(v) | reverse | reverse | filter(odd) | reverse | reverse | print;
container_view(v) | reverse | filter(odd) | reverse | print;
container_view(v) | filter(odd) | reverse | print;
container_view(v) | reverse | filter(odd) | print;
range(10,14) | reverse | filter(odd) | print;
range(10,15) | reverse | print;
range(10,15) | reverse | reverse | print;
range(10,15) | filter(odd) | print;
range(10,15) | reverse | filter(odd) | print;
range(10,15) | reverse | filter(odd) | reverse | print;
range(10,14) | reverse | filter(odd) | print;
range(1,101) | reverse | filter(odd) | take(5) | print;
range(1,101) | reverse | filter(odd) | head(5) | print;
range(1,101) | reverse | filter(odd) | first(5) | print;
range(1,101) | reverse | first(5) | reverse | print;
range(1,101) | last(5) | print;
range(1,101) | tail(5) | print;
range(1,6) | print;
range(1,6) | skip(3) | print;
auto filt = [](std::string a){ return a == "BAD"; };
to_array({"five","BAD","four","three","BAD","two","one"}) | reverse | filter(filt) | print;
to_array({"one","two","three","four","five"}) | reverse | filter([](std::string a){ return a == "two"; }) | print;
}
#if 0
template <typename T>
class iterator
{
public:
explicit iterator(T v = 0) : m_v(v) { }
iterator& operator++() { m_v++; return *this; }
iterator operator++(int) { m_v++; return *this; }
bool operator==(iterator other) const { return m_v == other.m_v; }
bool operator!=(iterator other) const { return !(*this == other); }
const T& operator*() const { return m_v; }
private:
T m_v;
};
template <typename T>
class reverse_iterator
{
public:
explicit reverse_iterator(T v = 0) : m_v(v) { }
reverse_iterator& operator++() { m_v--; return *this; }
reverse_iterator operator++(int) { m_v--; return *this; }
bool operator==(reverse_iterator other) const { return m_v == other.m_v; }
bool operator!=(reverse_iterator other) const { return !(*this == other); }
const T& operator*() const { return m_v; }
private:
T m_v;
};
class range // : public View<int>
{
public:
range(int start, int end)
: m_start(start)
, m_end(end)
{
}
iterator<int> begin() const { return iterator<int>(m_start); }
iterator<int> end() const { return iterator<int>(m_end + 1); }
reverse_iterator<int> rbegin() const { return reverse_iterator<int>(m_end); }
reverse_iterator<int> rend() const { return reverse_iterator<int>(m_start - 1); }
private:
int m_start;
int m_end;
};
template <typename View>
class reverse_view
{
public:
reverse_view(const View& view)
: m_view(view)
{
}
auto begin() const { return m_view.rbegin(); }
auto end() const { return m_view.rend(); }
auto rbegin() const { return m_view.begin(); }
auto rend() const { return m_view.end(); }
private:
const View& m_view;
};
template <typename BaseIter>
class filter_iterator
{
public:
explicit filter_iterator(BaseIter it, BaseIter end, const std::function<bool(int)>& f)
: m_it(it)
, m_end(end)
, m_f(f)
{
next();
}
void next() { while (!m_f(*m_it) && m_it != m_end) m_it++; }
filter_iterator& operator++() { m_it++; next(); return *this; }
filter_iterator operator++(int) { m_it++; next(); return *this; }
bool operator==(filter_iterator other) const { return m_it == other.m_it; }
bool operator!=(filter_iterator other) const { return !(*this == other); }
const auto& operator*() const { return *m_it; }
private:
std::function<bool(int)> m_f;
BaseIter m_it;
BaseIter m_end;
//T m_v;
};
template <typename View>
class filter_view
{
public:
filter_view(const View& view, const std::function<bool(int)>& f)
: m_view(view)
, m_f(f)
{
}
auto begin() const { return filter_iterator(m_view.begin(), m_view.end(), m_f); }
auto end() const { return filter_iterator(m_view.end(), m_view.begin(), m_f); }
auto rbegin() const { return filter_iterator(m_view.rbegin(), m_view.rend(), m_f); }
auto rend() const { return filter_iterator(m_view.rend(), m_view.rbegin(), m_f); }
private:
const View& m_view;
std::function<bool(int)> m_f;
};
#endif