Browse Source

testing modern C++ generator

dm 7 years ago
parent
commit
84197f2ef7

+ 1 - 1
examples/HelloWorld/proj.cmake/CMakeLists.txt

@@ -21,6 +21,6 @@ endif(WIN32)
 
 if (EMSCRIPTEN)
 	SET(CMAKE_EXECUTABLE_SUFFIX ".html")	
-	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_EXIT_RUNTIME=1 -s WARN_ON_UNDEFINED_SYMBOLS=1 --memory-init-file 0 -s TOTAL_MEMORY=50331648 -s FORCE_FILESYSTEM=1")
+	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=0 -std=c++1z -fcoroutines-ts -s NO_EXIT_RUNTIME=1 -s WARN_ON_UNDEFINED_SYMBOLS=1 --memory-init-file 0 -s TOTAL_MEMORY=50331648 -s FORCE_FILESYSTEM=1")
 	em_link_pre_js(HelloWorld  ${OXYGINE_JS_LIBRARIES}  ${CMAKE_CURRENT_SOURCE_DIR}/data.js)
 endif(EMSCRIPTEN)

+ 8 - 12
examples/HelloWorld/proj.win32/HelloWorld.vcxproj

@@ -17,8 +17,6 @@
     <ProjectName>HelloWorld</ProjectName>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
-
-
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
@@ -36,11 +34,8 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
-    
     <WholeProgramOptimization>false</WholeProgramOptimization>
     <WholeProgramOptimization Condition="'$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '14.0'">true</WholeProgramOptimization>
-    
-
     <CharacterSet>Unicode</CharacterSet>
     <PlatformToolset Condition="'$(VisualStudioVersion)' == '10.0'">v100</PlatformToolset>
     <PlatformToolset Condition="'$(VisualStudioVersion)' == '11.0'">v110</PlatformToolset>
@@ -62,15 +57,12 @@
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
   </ImportGroup>
   <PropertyGroup Label="UserMacros" />
-
   <PropertyGroup>
     <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)_$(PlatformToolset)\</OutDir>
     <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)_$(PlatformToolset)\</IntDir>
     <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)_$(PlatformToolset)\</OutDir>
     <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)_$(PlatformToolset)\</IntDir>
   </PropertyGroup>
-
-
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <LinkIncremental>false</LinkIncremental>
   </PropertyGroup>
@@ -85,11 +77,13 @@
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>../../..//oxygine/src;../../../..//SDL/include;../../..//oxygine/third_party/win32/pthreads/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalOptions>/await  /std:c++17 %(AdditionalOptions)</AdditionalOptions>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies></AdditionalDependencies>
+      <AdditionalDependencies>
+      </AdditionalDependencies>
       <AdditionalLibraryDirectories>../../..//oxygine/third_party/win32/libraries;../../..//libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <AdditionalOptions>/ignore:4099 %(AdditionalOptions)</AdditionalOptions>
     </Link>
@@ -111,17 +105,19 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <AdditionalLibraryDirectories>../../..//oxygine/third_party/win32/libraries;../../..//libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
-      <AdditionalDependencies></AdditionalDependencies>
+      <AdditionalDependencies>
+      </AdditionalDependencies>
       <AdditionalOptions>/ignore:4099 %(AdditionalOptions)</AdditionalOptions>
     </Link>
   </ItemDefinitionGroup>
-  <ItemGroup>    
+  <ItemGroup>
     <ProjectReference Include="../../../\oxygine\SDL\win32\oxygine.vcxproj">
       <Project>{52411305-cfe1-4fa8-9885-5729bfc816cf}</Project>
     </ProjectReference>
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="../src/example.cpp" /><ClCompile Include="../src/main.cpp" />
+    <ClCompile Include="../src/example.cpp" />
+    <ClCompile Include="../src/main.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="../src/example.h" />

+ 310 - 1
examples/HelloWorld/src/example.cpp

@@ -1,6 +1,7 @@
 #include "oxygine-framework.h"
 #include <functional>
-
+#include <experimental/coroutine>
+#include <variant>
 using namespace oxygine;
 
 //it is our resources
@@ -9,6 +10,298 @@ using namespace oxygine;
 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
 {
 public:
@@ -143,9 +436,25 @@ typedef oxygine::intrusive_ptr<MainActor> spMainActor;
 
 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
 void example_init()
 {
+    for (auto q : test())
+    {
+        //int q = 0;
+    }
+
     //load xml file with resources definition
     gameResources.loadXML("res.xml");