I’m a game developer working out of Western Canada, Vancouver and I was hoping to open a discussion on some explorative work I’ve done in applying C++20 coroutines to games.
The two problems I hope to solve to make C++20 coroutines more suitable for use as general purpose state machines are as follows:
With these goals in mind, I’d like to consider the following promise type implementation: https://godbolt.org/z/ccbjWxWd3
The above code implements a seemingly compiler-agnostic coroutine save/load solution that works for all versions of MSVC, GCC, and Clang dating back to their earliest implementations of coroutines. It also provides a mechanism to measure the size of a coroutine frame before execution and a way to optionally invoke a coroutine with a pre-allocated field of memory. While it's a neat proof-of-concept, it's not really production viable due to the gross misuse of the coroutine’s destroy chain to visit each in-scope variable on the coroutine frame for identification and serialization.
However, from this implementation we can derive an insight: “Any constraints on the compiler’s ability to optimize the lifetime and layout of variables in the coroutine frame resulting from the introduction of a hypothetical coroutine variable visitor function into the C++ Standard would be superseded by the constraints that already exist via the presence of the destroy function.”
My question(s) to the study group are: does the above inferred insight hold up to scrutiny? Would the language benefit from a way for external callers to access coroutine variables? And finally, what would the introduction of a hypothetical variable visitor function into the standard look like?
Before moving on to the next topic, I do want to note that the idea of a visitor function is not a silver bullet for enabling full general serialization of co-routines or nested coroutine chains. My own implementation above is somewhat crude in that it stores a whole memory snapshot and also limited in that it doesn’t support coroutine nesting at all. However, I think the presence of a Standard defined variable visitor function would provide a much wider range of possibilities for programmers to implement their own coroutine serialization functions constrained to the needs of their project’s requirements. The implementation of a visitor function could also lead to the emergence of other novel patterns such as bi-directional communication between the caller and the coroutine function body without the need to pass a reference into the function and risking it becoming dangling.
Turning our attention to the second problem, let’s take a look at the memory allocation features in the above code sample. The main kludge here is the use of the tls_delete variable in the promise type to optionally suppress the delete call on the coroutine
memory in the event that the coroutine memory was allocated and owned by the caller. A straightforward language feature to propose from this would be an optional suppress_delete function implementable on the promise type to indicate whether delete should be
called on the coroutine memory or not.
This got a little long winded, so I’ll pause here and thank the group members for their time in reading this far. I look forward to hearing the group’s thoughts on the ideas I have shared here and hope it can lead to some fruitful discussion.
Kind regards,
Julie Lim
LinkedIn: in/julie-lim-2a687262