Date: Sat, 7 Nov 2026 11:22:51 -0800
Hi Julie,
That's a clever hack! I'd be curious to see what you think of solving your
problem with my proposed std::execution::function (https://wg21.link/p4223)
and C++26's reflection capabilities. I think that, compared to coroutines,
senders give you more control over memory allocation, and they're "just
objects" (albeit complicated ones), so they may be more amenable to
reflection-based approaches to serialization than coroutines. On the
downside, of course, senders have no language support to make expressing
the logic of your program convenient, which is a significant drawback
compared to coroutines. I think this could be addressed with a language
change that permits the compiler to generate a sender that represents a
coroutine function.
Ian
On Fri, Nov 6, 2026 at 1:19 PM Julie Lim via SG14 <sg14_at_[hidden]>
wrote:
> Hello SG14,
>
>
>
> 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.
>
> As a gameplay programmer specifically, my interest is less focused on the
> high-throughput, multithreaded asynchronous programming goals that
> coroutines were designed to support, and more focused on their application
> as an intuitive means to create compact, ad-hoc state machines via a single
> function definition. These ad-hoc state machines are especially useful for
> creating sequenced behaviors for characters or other game logic and have
> seen use in both public game engines like Unity as well as (citing from own
> experience) proprietary game engines with scripting solutions that support
> await-like syntax.
>
>
>
> The two problems I hope to solve to make C++20 coroutines more suitable
> for use as general purpose state machines are as follows:
>
> - Completely opaque internal state (preventing serialization for game
> state save/load/replay, network replication, and runtime CRC checking)
> - Allocation at the point of invocation (as opposed to allocating once
> upon initialization of game or entity state and re-using as needed)
>
>
>
> With these goals in mind, I’d like to consider the following promise type
> implementation: *https://godbolt.org/z/ccbjWxWd3*
> <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?
>
> For me personally, I would like to see an opt-in feature using a
> contents_visitor callable object type that could be defined in the promise
> type. If present, the compiler would generate a visitor function that takes
> a contents_visitor& and invokes contents_visitor::operator()(T&) with each
> variable, input parameter, and active awaiter stored in the frame. The
> visitor function pointer would be stored on the coroutine frame alongside
> the resume and destroy function and would be callable via a
> visit_contents(Promise::contents_visitor&) function on the coroutine handle
> (only defined if a contents_visitor is defined).
>
>
>
> 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.
>
> However, this approach requires that the promise type be aware of the
> memory context that it was allocated into. If we are to include the
> possibility of using memory allocated by an outside caller, then the
> natural approach is to pass an allocator or allocation info struct in as a
> parameter to the coroutine. Looking back through the group’s mailing
> archive, I saw that this practice has already been discussed by the group
> back in April with particular attention paid to the associated issue of
> function signature pollution. I would like to hear more about this
> discussion especially with the added possible use case of forwarding
> pre-allocated memory onto coroutines or even nested coroutines.
>
>
>
> 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
>
> _______________________________________________
> SG14 mailing list
> SG14_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/sg14
>
That's a clever hack! I'd be curious to see what you think of solving your
problem with my proposed std::execution::function (https://wg21.link/p4223)
and C++26's reflection capabilities. I think that, compared to coroutines,
senders give you more control over memory allocation, and they're "just
objects" (albeit complicated ones), so they may be more amenable to
reflection-based approaches to serialization than coroutines. On the
downside, of course, senders have no language support to make expressing
the logic of your program convenient, which is a significant drawback
compared to coroutines. I think this could be addressed with a language
change that permits the compiler to generate a sender that represents a
coroutine function.
Ian
On Fri, Nov 6, 2026 at 1:19 PM Julie Lim via SG14 <sg14_at_[hidden]>
wrote:
> Hello SG14,
>
>
>
> 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.
>
> As a gameplay programmer specifically, my interest is less focused on the
> high-throughput, multithreaded asynchronous programming goals that
> coroutines were designed to support, and more focused on their application
> as an intuitive means to create compact, ad-hoc state machines via a single
> function definition. These ad-hoc state machines are especially useful for
> creating sequenced behaviors for characters or other game logic and have
> seen use in both public game engines like Unity as well as (citing from own
> experience) proprietary game engines with scripting solutions that support
> await-like syntax.
>
>
>
> The two problems I hope to solve to make C++20 coroutines more suitable
> for use as general purpose state machines are as follows:
>
> - Completely opaque internal state (preventing serialization for game
> state save/load/replay, network replication, and runtime CRC checking)
> - Allocation at the point of invocation (as opposed to allocating once
> upon initialization of game or entity state and re-using as needed)
>
>
>
> With these goals in mind, I’d like to consider the following promise type
> implementation: *https://godbolt.org/z/ccbjWxWd3*
> <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?
>
> For me personally, I would like to see an opt-in feature using a
> contents_visitor callable object type that could be defined in the promise
> type. If present, the compiler would generate a visitor function that takes
> a contents_visitor& and invokes contents_visitor::operator()(T&) with each
> variable, input parameter, and active awaiter stored in the frame. The
> visitor function pointer would be stored on the coroutine frame alongside
> the resume and destroy function and would be callable via a
> visit_contents(Promise::contents_visitor&) function on the coroutine handle
> (only defined if a contents_visitor is defined).
>
>
>
> 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.
>
> However, this approach requires that the promise type be aware of the
> memory context that it was allocated into. If we are to include the
> possibility of using memory allocated by an outside caller, then the
> natural approach is to pass an allocator or allocation info struct in as a
> parameter to the coroutine. Looking back through the group’s mailing
> archive, I saw that this practice has already been discussed by the group
> back in April with particular attention paid to the associated issue of
> function signature pollution. I would like to hear more about this
> discussion especially with the added possible use case of forwarding
> pre-allocated memory onto coroutines or even nested coroutines.
>
>
>
> 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
>
> _______________________________________________
> SG14 mailing list
> SG14_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/sg14
>
Received on 2026-11-07 19:23:08
