Created
September 6, 2012 22:49
-
-
Save ecatmur/3661011 to your computer and use it in GitHub Desktop.
Classless reinvocable functions using lambdas
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 <iostream> | |
#include <vector> | |
#include <functional> | |
#include <memory> | |
#include <type_traits> | |
#include <tuple> | |
#include <algorithm> | |
#include <stdexcept> | |
template<typename R, typename... Args> class generator_base { | |
using signature = R(Args...); | |
std::function<std::function<signature>()> f; | |
std::function<signature> g; | |
bool check() { return (g = f()) || (f = decltype(f){}); } | |
public: | |
template<typename F> generator_base(F f): f{f}, g{} {} | |
generator_base(const generator_base &other): f{other.f}, g{} {} | |
generator_base(): f{}, g{} {} | |
explicit operator bool() { return g || (f && check()); } | |
R operator()(Args... args) { R r{g(args...)}; check(); return r; } | |
}; | |
template<typename Category, typename T, typename generator> class generator_iterator: | |
public std::iterator<Category, T, void> { | |
generator &self() { return *static_cast<generator *>(this); } | |
const generator &self() const { return *static_cast<const generator *>(this); } | |
public: | |
bool operator==(generator &rhs) { return !self() && !rhs; } | |
bool operator!=(generator &rhs) { return !(self() == rhs); } | |
generator begin() const { return self(); } | |
generator end() const { return {}; } | |
}; | |
template<typename F> class generator; | |
template<typename R, typename... Args> class generator<R(Args...)>: public generator_base<R, Args...> { | |
public: | |
template<typename F> generator(F f): generator_base<R, Args...>{f} {} | |
}; | |
template<typename R> class generator<R()>: public generator_base<R>, | |
public generator_iterator<std::input_iterator_tag, R, generator<R()>> { | |
bool ready; | |
R cached; | |
public: | |
template<typename F> generator(F f): generator_base<R>{f}, ready{}, cached{} {} | |
generator(): generator_base<R>{}, ready{}, cached{} {} | |
generator &operator++() { ready = false; return *this; } | |
generator &operator++(int) { return ++*this; } | |
const R &operator*() { return ready ? cached : (ready = true, cached = (*this)()); } | |
const R *operator->() { return &**this; } | |
}; | |
template<typename T> class generator<void(T)>: public generator_base<void, T>, | |
public generator_iterator<std::output_iterator_tag, T, generator<void(T)>> { | |
public: | |
template<typename F> generator(F f): generator_base<void, T>{f} {} | |
generator(): generator_base<void, T>{} {} | |
generator &operator++() { return *this; } | |
generator &operator++(int) { return ++*this; } | |
generator &operator*() { return *this; } | |
generator &operator=(T t) { (*this)(t); return *this; } | |
}; | |
template<typename Range> using range_type | |
= typename std::iterator_traits<decltype(std::begin(std::declval<Range>()))>::value_type; | |
template<int... I> struct tuple_range { using succ = tuple_range<I..., sizeof...(I)>; }; | |
template<int N> struct make_tuple_range { using type = typename make_tuple_range<N - 1>::type::succ; }; | |
template<> struct make_tuple_range<0> { using type = tuple_range<>; }; | |
template<typename T> struct remove_class { }; | |
template<typename C, typename R, typename... A> | |
struct remove_class<R(C::*)(A...)> { using type = R(A...); }; | |
template<typename C, typename R, typename... A> | |
struct remove_class<R(C::*)(A...) const> { using type = R(A...); }; | |
template<typename C, typename R, typename... A> | |
struct remove_class<R(C::*)(A...) volatile> { using type = R(A...); }; | |
template<typename C, typename R, typename... A> | |
struct remove_class<R(C::*)(A...) const volatile> { using type = R(A...); }; | |
template<typename T, bool> struct get_signature_impl { }; | |
template<typename R, typename... A> | |
struct get_signature_impl<R(A...), true> { using type = R(A...); }; | |
template<typename R, typename... A> | |
struct get_signature_impl<R(*)(A...), true> { using type = R(A...); }; | |
template<typename T> | |
struct get_signature_impl<T, true> { using type = typename remove_class< | |
decltype(&std::remove_reference<T>::type::operator())>::type; }; | |
template<typename T> using get_signature = typename get_signature_impl<T, true>::type; | |
template<typename F> using make_function_type = std::function<get_signature<F>>; | |
template<typename F> make_function_type<F> make_function(F &&f) { | |
return make_function_type<F>(std::forward<F>(f)); } | |
template<typename F> make_function_type<F> yield_if(bool condition, F &&f) { | |
return condition ? make_function_type<F>(std::forward<F>(f)) : make_function_type<F>{}; | |
} | |
// from http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/example/http/server4/coroutine.hpp | |
class coroutine | |
{ | |
public: | |
coroutine() : value_(0) {} | |
bool is_child() const { return value_ < 0; } | |
bool is_parent() const { return !is_child(); } | |
bool is_complete() const { return value_ == -1; } | |
private: | |
friend class coroutine_ref; | |
int value_; | |
}; | |
class coroutine_ref | |
{ | |
public: | |
coroutine_ref(coroutine& c) : value_(c.value_), modified_(false) {} | |
coroutine_ref(coroutine* c) : value_(c->value_), modified_(false) {} | |
~coroutine_ref() { if (!modified_) value_ = -1; } | |
operator int() const { return value_; } | |
int& operator=(int v) { modified_ = true; return value_ = v; } | |
private: | |
void operator=(const coroutine_ref&); | |
int& value_; | |
bool modified_; | |
}; | |
#define CORO_REENTER(c) \ | |
switch (coroutine_ref _coro_value = c) \ | |
case -1: if (_coro_value) \ | |
{ \ | |
goto terminate_coroutine; \ | |
terminate_coroutine: \ | |
_coro_value = -1; \ | |
goto bail_out_of_coroutine; \ | |
bail_out_of_coroutine: \ | |
break; \ | |
} \ | |
else case 0: | |
#define CORO_YIELD_IMPL(n) \ | |
for (_coro_value = (n);;) \ | |
if (_coro_value == 0) \ | |
{ \ | |
case (n): ; \ | |
break; \ | |
} \ | |
else \ | |
switch (_coro_value ? 0 : 1) \ | |
for (;;) \ | |
case -1: if (_coro_value) \ | |
goto terminate_coroutine; \ | |
else for (;;) \ | |
case 1: if (_coro_value) \ | |
goto bail_out_of_coroutine; \ | |
else case 0: | |
#define CORO_FORK_IMPL(n) \ | |
for (_coro_value = -(n);; _coro_value = (n)) \ | |
if (_coro_value == (n)) \ | |
{ \ | |
case -(n): ; \ | |
break; \ | |
} \ | |
else | |
#if defined(_MSC_VER) | |
# define CORO_YIELD CORO_YIELD_IMPL(__COUNTER__ + 1) | |
# define CORO_FORK CORO_FORK_IMPL(__COUNTER__ + 1) | |
#else // defined(_MSC_VER) | |
# define CORO_YIELD CORO_YIELD_IMPL(__LINE__) | |
# define CORO_FORK CORO_FORK_IMPL(__LINE__) | |
#endif // defined(_MSC_VER) | |
#define GENERATOR_BEGIN(signature) \ | |
coroutine _generator_coro; \ | |
return [=]() mutable -> std::function<signature> { CORO_REENTER(_generator_coro) { | |
#define GENERATOR_YIELD \ | |
CORO_YIELD return [&] | |
#define GENERATOR_STOP \ | |
return {} | |
#define GENERATOR_END \ | |
} GENERATOR_STOP; } | |
template<typename Range> auto iter(const Range &r) -> generator<range_type<Range>()> { | |
auto it = std::begin(r), end = std::end(r); | |
return [=]() mutable{ return yield_if(it != end, [&](){ return *it++; }); }; | |
} | |
template<typename I = size_t> auto iota(I start = I{}) -> generator<I()> { | |
return [=]() mutable{ return [&](){ return start++; }; }; | |
} | |
template<typename I, typename J> auto iota(I start, const J &stop) -> generator<I()> { | |
return [=]() mutable{ return yield_if(start != stop, [&](){ return start++; }); }; | |
} | |
template<typename I, typename J, typename K> generator<I()> iota(I start, const J &stop, const K &step) { | |
GENERATOR_BEGIN(I()) { | |
for (; start != stop; start += step) | |
GENERATOR_YIELD { return start; }; | |
} GENERATOR_END; | |
} | |
bool all() { return true; } | |
template<typename A, typename... R> bool all(A a, R... r) { return a && all(r...); } | |
template<int... I, typename... Range> auto zip_impl(tuple_range<I...>, Range... r) | |
-> generator<std::tuple<range_type<Range>...>()> { | |
std::tuple<decltype(std::begin(r))...> it{std::begin(r)...}, end{std::end(r)...}; | |
return [=]() mutable{ return yield_if(all(std::get<I>(it) != std::get<I>(end)...), | |
[&]() { return std::make_tuple(*std::get<I>(it)++...); }); }; | |
} | |
template<typename... Range> auto zip(Range... r) -> generator<std::tuple<range_type<Range>...>()> { | |
return zip_impl(typename make_tuple_range<sizeof...(Range)>::type{}, r...); | |
} | |
template<typename Range> auto enumerate(const Range &r) -> decltype(zip(iota(), r)) { | |
return zip(iota(), r); | |
} | |
generator<int()> primes() { | |
std::vector<int> seen; | |
int n = 2; | |
GENERATOR_BEGIN(int()) { | |
for (; ; ++n) { | |
if (std::none_of(std::begin(seen), std::end(seen), [n](int i) { return n % i == 0; })) { | |
GENERATOR_YIELD { return n; }; | |
seen.push_back(n); | |
} | |
} | |
} GENERATOR_END; | |
} | |
template<typename Range> generator<char32_t()> utf8_decode(Range &&r) { | |
typedef decltype(std::begin(r)) Iterator; | |
Iterator it{std::begin(r)}, end{std::end(r)}; | |
auto extract_trail = [](Iterator &it, const Iterator &end) -> unsigned char { | |
if (it == end) throw std::invalid_argument("Truncated sequence"); | |
unsigned char d = *it++; | |
if ((d & 0xC0) != 0x80) throw std::invalid_argument("Truncated sequence"); | |
return d & 0x3F; | |
}; | |
unsigned char c{}, d{}, e{}, f{}; | |
char32_t k{}; | |
GENERATOR_BEGIN(char32_t()) { | |
while (it != end) { | |
c = *it++; | |
if (c < 0x80) GENERATOR_YIELD { return c; }; | |
else if (c < 0xC0) throw std::invalid_argument("Unexpected trail byte"); | |
else if (c < 0xC2) throw std::invalid_argument("Overlong encoding"); | |
else if (c < 0xE0) { | |
d = extract_trail(it, end); | |
GENERATOR_YIELD { return ((c & 0x1F) << 6) | d; }; | |
} else if (c < 0xF0) { | |
d = extract_trail(it, end); | |
e = extract_trail(it, end); | |
GENERATOR_YIELD { return ((c & 0x1F) << 12) | (d << 6) | e; }; | |
} else if (c < 0xF5) { | |
d = extract_trail(it, end); | |
e = extract_trail(it, end); | |
f = extract_trail(it, end); | |
k = ((c & 0x1F) << 18) | (d << 12) | (e << 6) | f; | |
if (k > 0x10FFFF) throw std::invalid_argument("Invalid codepoint"); | |
GENERATOR_YIELD { return k; }; | |
} else throw std::invalid_argument("Invalid codepoint"); | |
} | |
} GENERATOR_END; | |
} | |
int main() { | |
std::vector<int> v(iota(9, 4, -1), generator<int()>{}); | |
for (auto f(iter(v)); f; ) std::cout << f() << ' '; std::cout << '\n'; | |
for (generator<int()> it{iter(v)}, end; it != end; ++it) | |
std::cout << *it << ' '; | |
std::cout << '\n'; | |
for (auto x: iter(v)) std::cout << x << ' '; std::cout << '\n'; | |
for (auto x: enumerate(iter(v))) | |
std::cout << (std::get<0>(x) == 0 ? "" : ", ") << std::get<1>(x); std::cout << '\n'; | |
for (auto x: zip(iota(1, 11), primes())) | |
std::cout << "p_" << std::get<0>(x) << " = " << std::get<1>(x) << '\n'; | |
for (auto x: utf8_decode("Pay \xE2\x82\xAC 3.50")) | |
std::cout << std::hex << std::showbase << x << ' '; std::cout << '\n'; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment