|
@@ -1,6 +1,7 @@
|
|
|
#include "oxygine-framework.h"
|
|
#include "oxygine-framework.h"
|
|
|
#include <functional>
|
|
#include <functional>
|
|
|
-
|
|
|
|
|
|
|
+#include <experimental/coroutine>
|
|
|
|
|
+#include <variant>
|
|
|
using namespace oxygine;
|
|
using namespace oxygine;
|
|
|
|
|
|
|
|
//it is our resources
|
|
//it is our resources
|
|
@@ -9,6 +10,298 @@ using namespace oxygine;
|
|
|
Resources gameResources;
|
|
Resources gameResources;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+namespace detail {
|
|
|
|
|
+ // simple type erasure for iterators
|
|
|
|
|
+
|
|
|
|
|
+ template<typename T>
|
|
|
|
|
+ struct generic_iterable
|
|
|
|
|
+ {
|
|
|
|
|
+ virtual ~generic_iterable() = default;
|
|
|
|
|
+
|
|
|
|
|
+ virtual T& operator*() = 0;
|
|
|
|
|
+ virtual generic_iterable<T>& operator++() = 0;
|
|
|
|
|
+ virtual bool empty() const = 0;
|
|
|
|
|
+
|
|
|
|
|
+ bool await_ready() const noexcept
|
|
|
|
|
+ {
|
|
|
|
|
+ return empty();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ template<typename U>
|
|
|
|
|
+ void await_suspend(std::experimental::coroutine_handle<U> h) noexcept
|
|
|
|
|
+ {
|
|
|
|
|
+ h.promise().store_iterator(this);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void await_resume() const noexcept {}
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ template<typename T, typename StartIterator, typename EndIterator>
|
|
|
|
|
+ struct iterator_iterable : public generic_iterable<T>
|
|
|
|
|
+ {
|
|
|
|
|
+ iterator_iterable(StartIterator start, EndIterator end) : start(start), end(end) {}
|
|
|
|
|
+
|
|
|
|
|
+ T& operator*() override
|
|
|
|
|
+ {
|
|
|
|
|
+ return *start;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ iterator_iterable<T, StartIterator, EndIterator>& operator++() override
|
|
|
|
|
+ {
|
|
|
|
|
+ ++start;
|
|
|
|
|
+ return *this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bool empty() const override
|
|
|
|
|
+ {
|
|
|
|
|
+ return start == end;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ StartIterator start;
|
|
|
|
|
+ EndIterator end;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+} // namespace detail
|
|
|
|
|
+
|
|
|
|
|
+template<typename T>
|
|
|
|
|
+struct generator
|
|
|
|
|
+{
|
|
|
|
|
+ using value_type = T;
|
|
|
|
|
+
|
|
|
|
|
+ struct promise_type
|
|
|
|
|
+ {
|
|
|
|
|
+ // 0: prestart, 1: value, 2: range, 3: done
|
|
|
|
|
+ std::variant<std::monostate, T*, detail::generic_iterable<T>*, std::monostate> state;
|
|
|
|
|
+
|
|
|
|
|
+ promise_type& get_return_object() noexcept
|
|
|
|
|
+ {
|
|
|
|
|
+ return *this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::experimental::suspend_always initial_suspend() const noexcept
|
|
|
|
|
+ {
|
|
|
|
|
+ return {};
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::experimental::suspend_always final_suspend() const noexcept
|
|
|
|
|
+ {
|
|
|
|
|
+ return {};
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::experimental::suspend_always yield_value(T& value) noexcept
|
|
|
|
|
+ {
|
|
|
|
|
+ state.template emplace<1>(std::addressof(value));
|
|
|
|
|
+ return {};
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::experimental::suspend_always yield_value(T&& value) noexcept
|
|
|
|
|
+ {
|
|
|
|
|
+ state.template emplace<1>(std::addressof(value));
|
|
|
|
|
+ return {};
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void return_void() noexcept
|
|
|
|
|
+ {
|
|
|
|
|
+ state.template emplace<3>();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ template<typename Range>
|
|
|
|
|
+ auto await_transform(Range&& range) const noexcept
|
|
|
|
|
+ {
|
|
|
|
|
+ using std::begin;
|
|
|
|
|
+ using std::end;
|
|
|
|
|
+ auto s = begin(range);
|
|
|
|
|
+ auto e = end(range);
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: properly constraint
|
|
|
|
|
+ static_assert(std::is_same_v<decltype(*s), T&>);
|
|
|
|
|
+ detail::iterator_iterable<T, decltype(s), decltype(e)> iterator{ s, e };
|
|
|
|
|
+ return iterator;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void unhandled_exception()
|
|
|
|
|
+ {
|
|
|
|
|
+ state.template emplace<3>();
|
|
|
|
|
+ auto ex = std::current_exception();
|
|
|
|
|
+ std::rethrow_exception(ex);
|
|
|
|
|
+ //// MSVC bug? should be possible to rethrow with "throw;"
|
|
|
|
|
+ //// rethrow exception immediately
|
|
|
|
|
+ // throw;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void store_iterator(detail::generic_iterable<T>* iterator) noexcept
|
|
|
|
|
+ {
|
|
|
|
|
+ state.template emplace<2>(iterator);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ T& value()
|
|
|
|
|
+ {
|
|
|
|
|
+ switch (state.index()) {
|
|
|
|
|
+ case 1:
|
|
|
|
|
+ return *std::get<1>(state);
|
|
|
|
|
+ case 2:
|
|
|
|
|
+ return **std::get<2>(state);
|
|
|
|
|
+ case 0:
|
|
|
|
|
+ next();
|
|
|
|
|
+ return value();
|
|
|
|
|
+ default:
|
|
|
|
|
+ case 3:
|
|
|
|
|
+ throw std::logic_error("Generator already completed!");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const T& value() const
|
|
|
|
|
+ {
|
|
|
|
|
+ switch (state.index()) {
|
|
|
|
|
+ case 1:
|
|
|
|
|
+ return *std::get<1>(state);
|
|
|
|
|
+ case 2:
|
|
|
|
|
+ return **std::get<2>(state);
|
|
|
|
|
+ case 0:
|
|
|
|
|
+ next();
|
|
|
|
|
+ return value();
|
|
|
|
|
+ default:
|
|
|
|
|
+ case 3:
|
|
|
|
|
+ throw std::logic_error("Generator already completed!");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void next()
|
|
|
|
|
+ {
|
|
|
|
|
+ auto handle = std::experimental::coroutine_handle<promise_type>::from_promise(*this);
|
|
|
|
|
+ switch (state.index()) {
|
|
|
|
|
+ case 0:
|
|
|
|
|
+ case 1:
|
|
|
|
|
+ handle.resume();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 2: {
|
|
|
|
|
+ auto& iterator = *std::get<2>(state);
|
|
|
|
|
+ ++iterator;
|
|
|
|
|
+ if (iterator.empty()) {
|
|
|
|
|
+ state.template emplace<0>();
|
|
|
|
|
+ handle.resume();
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ default:
|
|
|
|
|
+ case 3:
|
|
|
|
|
+ throw std::logic_error("Generator already completed!");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ using handle_type = std::experimental::coroutine_handle<promise_type>;
|
|
|
|
|
+
|
|
|
|
|
+ struct iterator
|
|
|
|
|
+ {
|
|
|
|
|
+ using iterator_category = std::input_iterator_tag;
|
|
|
|
|
+ using value_type = T;
|
|
|
|
|
+ using difference_type = ptrdiff_t;
|
|
|
|
|
+ using pointer = T * ;
|
|
|
|
|
+ using reference = T & ;
|
|
|
|
|
+
|
|
|
|
|
+ handle_type coro_handle;
|
|
|
|
|
+
|
|
|
|
|
+ iterator() : coro_handle(nullptr) {}
|
|
|
|
|
+
|
|
|
|
|
+ iterator(handle_type coro_handle) : coro_handle(coro_handle) {}
|
|
|
|
|
+
|
|
|
|
|
+ iterator& operator++()
|
|
|
|
|
+ {
|
|
|
|
|
+ try {
|
|
|
|
|
+ coro_handle.promise().next();
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (...) {
|
|
|
|
|
+ coro_handle = nullptr;
|
|
|
|
|
+ throw;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (coro_handle.done())
|
|
|
|
|
+ coro_handle = nullptr;
|
|
|
|
|
+ return *this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ iterator operator++(int) = delete;
|
|
|
|
|
+
|
|
|
|
|
+ bool operator==(iterator const& other) const
|
|
|
|
|
+ {
|
|
|
|
|
+ return coro_handle == other.coro_handle;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bool operator!=(iterator const& other) const
|
|
|
|
|
+ {
|
|
|
|
|
+ return !(*this == other);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const T& operator*() const
|
|
|
|
|
+ {
|
|
|
|
|
+ return coro_handle.promise().value();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const T* operator->() const
|
|
|
|
|
+ {
|
|
|
|
|
+ return std::addressof(operator*());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ T& operator*()
|
|
|
|
|
+ {
|
|
|
|
|
+ return coro_handle.promise().value();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ T* operator->()
|
|
|
|
|
+ {
|
|
|
|
|
+ return std::addressof(operator*());
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ iterator begin()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (coro_handle) {
|
|
|
|
|
+ if (coro_handle.done())
|
|
|
|
|
+ return {};
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return { coro_handle };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ iterator end()
|
|
|
|
|
+ {
|
|
|
|
|
+ return {};
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ generator(promise_type& promise) : coro_handle(handle_type::from_promise(promise)) {}
|
|
|
|
|
+
|
|
|
|
|
+ generator() = default;
|
|
|
|
|
+ generator(generator const&) = delete;
|
|
|
|
|
+ generator& operator=(generator const&) = delete;
|
|
|
|
|
+
|
|
|
|
|
+ generator(generator&& other) : coro_handle(other.coro_handle)
|
|
|
|
|
+ {
|
|
|
|
|
+ other.coro_handle = nullptr;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ generator& operator=(generator&& other)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (&other != this) {
|
|
|
|
|
+ coro_handle = other.coro_handle;
|
|
|
|
|
+ other.coro_handle = nullptr;
|
|
|
|
|
+ }
|
|
|
|
|
+ return *this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ~generator()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (coro_handle) {
|
|
|
|
|
+ coro_handle.destroy();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+private:
|
|
|
|
|
+ std::experimental::coroutine_handle<promise_type> coro_handle = nullptr;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
class MainActor: public Actor
|
|
class MainActor: public Actor
|
|
|
{
|
|
{
|
|
|
public:
|
|
public:
|
|
@@ -143,9 +436,25 @@ typedef oxygine::intrusive_ptr<MainActor> spMainActor;
|
|
|
|
|
|
|
|
void example_preinit() {}
|
|
void example_preinit() {}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+generator<int> test()
|
|
|
|
|
+{
|
|
|
|
|
+ logs::messageln("a");
|
|
|
|
|
+ co_yield 12;
|
|
|
|
|
+ logs::messageln("b");
|
|
|
|
|
+ co_yield 13;
|
|
|
|
|
+ logs::messageln("c");
|
|
|
|
|
+ co_yield 14;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
//called from main.cpp
|
|
//called from main.cpp
|
|
|
void example_init()
|
|
void example_init()
|
|
|
{
|
|
{
|
|
|
|
|
+ for (auto q : test())
|
|
|
|
|
+ {
|
|
|
|
|
+ //int q = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
//load xml file with resources definition
|
|
//load xml file with resources definition
|
|
|
gameResources.loadXML("res.xml");
|
|
gameResources.loadXML("res.xml");
|
|
|
|
|
|