- 主题:看到一个longjmp-style的coroutine实现
it works like C-stype longjmp function, to suspend/resume through multi-nested-coroutines.
I am not sure if this is a good coroutine model.
This is very useful for game coding when at every round the AI get update a little then jumps back to the main message loop.
The following code is what I got from stackoverflow:
https://stackoverflow.com/questions/61696746/supsending-thrugh-multiple-nested-coroutines
to compile:
g++-10 main.cpp -std=c++20 -fcoroutines -g -fsanitize=address -Wall
#include <cstdio>
#include <coroutine>
#include <optional>
namespace
{
template <typename T>
struct task
{
struct task_promise;
using promise_type = task_promise;
using handle_type = std::coroutine_handle<promise_type>;
mutable handle_type m_handle;
task(handle_type handle)
: m_handle(handle)
{
}
task(task&& other) noexcept
: m_handle(other.m_handle)
{
other.m_handle = nullptr;
}
bool await_ready()
{
return false;
}
bool await_suspend(std::coroutine_handle<> handle)
{
return true;
}
bool await_suspend(std::coroutine_handle<promise_type> handle)
{
handle.promise().m_inner_handler = m_handle;
m_handle.promise().m_outer_handler = handle;
return true;
}
auto await_resume()
{
return *m_handle.promise().m_value;
}
//manualy wait for finish
bool one_step()
{
auto curr = m_handle;
while (curr)
{
if (!curr.promise().m_inner_handler)
{
while (!curr.done())
{
curr.resume();
if (!curr.done())
{
return true;
}
if (curr.promise().m_outer_handler)
{
curr = curr.promise().m_outer_handler;
curr.promise().m_inner_handler = nullptr;
}
else
{
return false;
}
}
break;
}
curr = curr.promise().m_inner_handler;
}
return !curr.done();
}
~task()
{
if (m_handle)
m_handle.destroy();
}
struct task_promise
{
std::optional<T> m_value {};
std::coroutine_handle<promise_type> m_inner_handler {};
std::coroutine_handle<promise_type> m_outer_handler {};
auto value()
{
return m_value;
}
auto initial_suspend()
{
return std::suspend_never{};
}
auto final_suspend()
{
return std::suspend_always{};
}
auto return_value(T t)
{
m_value = t;
return std::suspend_always{};
}
task<T> get_return_object()
{
return {handle_type::from_promise(*this)};
}
void unhandled_exception()
{
std::terminate();
}
void rethrow_if_unhandled_exception()
{
}
};
};
task<int> suspend_one()
{
std::printf("suspend_one \\\n");
co_await std::suspend_always();
std::printf("suspend_one /\n");
co_return 1;
}
task<int> suspend_two()
{
auto a = co_await suspend_one();
auto b = co_await suspend_one();
co_return a + b;
}
task<int> suspend_five()
{
auto a = co_await suspend_two();
auto b = co_await suspend_two();
co_return 1 + a + b;
}
task<int> run()
{
std::printf("run\n");
auto a = co_await suspend_five();
auto b = co_await suspend_five();
auto c = co_await suspend_five();
co_return 5 + a + b + c;
}
}
int main()
{
std::printf( "main in\n");
auto r = run();
std::printf( "main -> while\n");
while (r.one_step()){ std::printf(" while loop\n"); }
std::printf( "main return\n");
return r.await_resume();
}
--
FROM 158.140.1.*
这个模型很顺手,相信大多数人能立刻习惯.
举个游戏里面的例子,如果对于某个怪物,或者什么需要描述行为的对象:
AI_update()
{
walk_2_step();
if(found_ememy()){
attack();
}
else{
sleep_2_sec();
}
random_turn();
}
然后如果这个monster有消息处理函数:
switch(message.type){
case UPDATE: AI_update(); break; // blocking
case QUERY : respond() ; break; // respond function, non-blocking
case ......;
}
这时候就有了麻烦:
当怪物得到一个时间片自我update的时候,需要调用这个AI_update()
但是AI_update()描述了一段时间的持续性为, 不可能在一个时间片完全执行.
这个coroutine model可以把函数改成:
AI_update()
{
walk_2_step(); yield;
if(found_ememy()){
attack(); yield;
}
else{
sleep_2_sec(); yield;
}
random_turn(); yield;
}
每次执行一个动作后就停住,等到下一个UPDATE消息来的时候在继续执行AI_update()函数.
不知道这个例子是否恰当?
--
FROM 158.140.1.*
我修改了一下,简化了一下one_step的函数.现在看起来没啥问题了.
我把这个类重新命名成了corof::long_jmper,因为task这个名字已经被占用了.
修改的地方:
1. 把promise改成了非template的,内部用any,这样long_jmper就可以支持await不同的type
代价是需要一个eval_op<T>提供any_cast的type
2. 把单步运行里面的寻找handle和执行handle分开了,并且只允许resume一次.
放个链接在这里,欢迎批判:
https://raw.githubusercontent.com/etorth/mir2x/c%2B%2B20/common/src/corof.hpp
调用实例:corof::long_jmper Monster::updateCoroFunc(),文件:
https://raw.githubusercontent.com/etorth/mir2x/c%2B%2B20/server/monoserver/src/monster.cpp
updateCoroFunc()的各个子coroutine定义:
https://raw.githubusercontent.com/etorth/mir2x/c%2B%2B20/server/monoserver/src/monsterco.cpp
--
修改:allegro FROM 158.140.1.*
FROM 158.140.1.*