Skip to content

Instantly share code, notes, and snippets.

@ecatmur
Created September 6, 2012 22:49
Show Gist options
  • Save ecatmur/3661011 to your computer and use it in GitHub Desktop.
Save ecatmur/3661011 to your computer and use it in GitHub Desktop.
Classless reinvocable functions using lambdas
#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