g++协程演示
main h1=00000000 h2=00000000 main h1=009B0048 h2=00000000 main h1=009B0048 h2=009C0078 main h1=009B0048 h2=009C0078 coroutine: 0 @ 009B005C coroutine: 9 @ 009C008C main h1=009B0048 h2=009C0078 coroutine: 1 @ 009B005C coroutine: 10 @ 009C008C main h1=009B0048 h2=009C0078 coroutine: 2 @ 009B005C coroutine: 11 @ 009C008C // cr_func是协程函数。main里启动了两个协程,参数不同 // 协程里printf("coroutine: %2d @ %p\n", i++, &i); int main() { HCR h1, h2; printf("main h1=%p h2=%p\n", h1, h2); cr_func(&h1, 0); printf(" main h1=%p h2=%p\n", h1, h2); cr_func(&h2, 9); printf(" main h1=%p h2=%p\n", h1, h2); for (int i = 0; i < 3; ++i) { printf(" main h1=%p h2=%p\n", h1, h2); h1.resume(), h2.resume(); } h2.destroy(), h1.destroy(); }
勾起兴趣再往下看:
struct awaitable : public std::suspend_always { HCR* m_phcr; awaitable(HCR* phcr) : m_phcr(phcr) {} void await_suspend(HCR new_handle) { *m_phcr = new_handle; } }; cr_ret_type cr_func(HCR* phcr, int i) { char data[65536]; awaitable a(phcr); // *phcr = new_handle;修改的是main里的handle. while (1) { co_await a; // 干3件事: // 1. Save all local variables (如data) and "jmp_buf" to a heap-allocated object. // 2. "setjmp", 设置一个"书签", 以后再"longjmp"跳回来继续执行(resume)。官话: // Create a new coroutine handle X and pass it to a.await_suspend(). // HCR是the heap-allocated object的句柄,是个void*, 黑盒子能用不能看。 // 3. "return" // resume回来后执行"return"后的下一条语句。 // jmp_buf ~= 数据寄存器和instruction pointer/program counter. 局部变量宜只放个指针: // struct big_data* pdata; printf("coroutine: %2d @ %p\n", i++, &i); } }
丑陋的东西在最后:
typedef std::coroutine_handle<> HCR; struct cr_ret_type { struct promise_type { cr_ret_type get_return_object() { return {}; } std::suspend_never initial_suspend() { return {}; } std::suspend_never final_suspend() noexcept { return {}; } void unhandled_exception() {} }; }; // My tutorial and take on C++20 coroutines (stanford.edu) // g++ -fcoroutines crdemo.cpp (gcc 10.2或更高版; tdm-gcc-10.3.0.exe ok) #include <coroutine> #include <stdio.h>
协程必须返回cr_ret_type,void cr_func()是不行的。promise_type和它里面的东西不要改。cr_ret_type里可以再加东西。可以像python那样做生成器。
1. 我完全尊重C++设计者的劳动,而且我远没有做出这样工作的能力
2. 他们疑似死心眼子,我不能每个协程一个struct data *p; data里放个T return_value吗?哦,明白了,要支持递归:
def permute(x): # 假的,y只有1份,但能达到目的 def pmt(x, y, n, i): if i == n: yield y for j in range(n): if x[j] == 99: continue y[i] = s = x[j]; x[j] = 99 yield from pmt(x, y, n, i + 1) x[j] = s n = len(x) return pmt(x, list(range(n)), n, 0)
def gen(n, i): # 得到6个0. 动态语言没做到的,静态语言要做到?不加-std=c++20没有co_yield,加了也没有co_yield_from啊
if n == 0: yield i; return
for i in range(n):
yield from gen(n - 1, i) # 去掉from得到3个generator
for i in gen(3, 0): print(i)
这样的需求多吗?能T return_value_stack[]吗? 做floodfill时我干过切换stack (sp)的事,所以老念念不忘。 也许有点像placement new, 函数能用using stack(buf)来控制,或者:
coroutine X { void run() { // 不能有参数和返回值 // 不能有局部变量,但也不用写this->t // this还是得放在堆栈里, but how? or ecx? // 协程不可能同时调别的函数,所以堆栈基本上可以共享 ... } int t; int m_i; char m_buf[]; } x; // 不能放在堆栈里?
// c++ - Linux 64bit calling convention uses register to pass 'this' pointer, but code is less efficient? - Stack Overflow
// 用汇编写些代码?瞎想瞎说,不关我的事
// 给个__builtin_coro_suspend啊!gcc里的coroutine_handle
// 撸了把排位,我方猴子大后期不行了
// memcpy(&(int((void*)this)), &m_that, sizeof(void*)); 编译通不过。32位x86下,this在寄存器ecx里的可能性不是100%。
// 搞个coroutine.h,把丑陋的东西都藏进去得了: #define i_am_a_corutine ... #define i_am_so_nice_that_i_give_up_cpu ...
// 原来h代表hidden和hideous啊! :-)

浙公网安备 33010602011771号