TL;DR;

How do I get yield-like control flow in C++?

Threads instead of scripts

This article describes, how I use threads to model a natural C++ syntax and flow of logic for sequential, script-like actions.

My terminology for "unit" and "strategy" is borrowed from my other article.
Short: unit = agent. strategy = current state of an agent.

Some strategies are basically sequences. "Do this, then do that, then do this."

Typically for AI is a function "update" which updates the attributes of the unit. This function must not block but instead return "immediately".

But some sequences of actions continue over a longer period of time. This is hard to model, if the function has to return immediately.

Example: The strategy "confuse" is used when a unit is blocked by obstacles unexpectedly. "Confuse" first rotates the unit, then waits a couple of seconds, then rotates the unit again and finally wait again. (Actually, the "confuse" in Nightwatch is a bit more complex, but the simple version here will do for illustration).

The ideal straight forward implementation would look like:

void update(float elapsed) {
	unit.direction -= 0.3f;
	sleep(1.5f);
	unit.direction += 0.3f;
	sleep(1.5f);
}
The implementation of "sleep(1.5f)" is what causes trouble here. Update must not block! So you have to create different states for the phase before the each sleep, for the sleep itself and after the sleep. Depending on this state, you have to branch at each call to update.

The shortest (not necesarily the best readable) version I can come up with looks like:

void update(float elapsed) {
	switch (state_)
	{
	case 0: unit.direction -= 0.3f; state_++; sleeping_ = 1.5f;
	case 1: if (sleeping_ > 0) {sleeping_-=elapsed; break;} else state_++;
	case 2: unit.direction += 0.3f; state_++; sleeping_ = 1.5f;
	case 3: if (sleeping_ > 0) {sleeping_-=elapsed; break;} else state_++;
	}
}
This way of indirect flow logic is error prone, unnecessary complex and just unnatural. Sure, it could be made a bit more readable but I doubt that it will achieve the simplicity of my "ideal version" above.

A scripting language (cp. Python) is used for sequences of actions often. The scripts have to be executable line-by-line (or rather statement-by-statement). The current position within the script replaces the "state_" in my example above. Execution is done until a blocking function like "sleep" is hit.

Scripting languages have a lot of different pros and cons. Sometimes they can't be used. But even then, not all is lost. Normal threads can be used to model agents with simple sequences of actions. I used this idea in my class "Script". "Script" starts a new thread when created, which immediately blocks until "update" releases it. While the thread is running, "update" waits until the thread hits either a "sleep" or finishes execution.

DWORD WINAPI ScriptThread( void* param )
{
	Script& script = *(Script*)param;
	script.run();
	script.scriptFinished_ = true;
	SignalObjectAndWait(script.lock_, script.lock_, INFINITE, false);
	return 0;
}

void Script::update(float elapsed)
{
	if (!scriptThread_)
	{
		lock_ = CreateEvent(0, false, false, 0);
		scriptThread_ = CreateThread(0, 0, ScriptThread, this, 0, 0);
		assert(scriptThread_);
		if (!scriptThread_)
		{	// something wrong - break up script
			CloseHandle(lock_);
			finished_ = true;
			return; 
		}
		WaitForSingleObject(lock_, INFINITE);
	}

	if (sleeping_ > 0)
	{
		sleeping_ -= elapsed;
		return;
	}

	if (sleeping_ <= 0)
	{
		sleeping_ = 0;
		SignalObjectAndWait(lock_, lock_, INFINITE, false);
	}

	if (scriptFinished_)
	{
		SignalObjectAndWait(lock_, 0, 0, false);
		CloseHandle(lock_);
		finished_ = true;
	}
}

void Script::sleep(float time)
{
	sleeping_ = time;
	SignalObjectAndWait(lock_, lock_, INFINITE, false);
}
Finally: The code for "Confuse" is identical with my "ideal example" above.

One final comment: Using this kind of multi-threading doesn't trigger any typical multi-threading problems, as the two threads are never executed simultanously.

PS: Of course, you would want to decrease the stack size of threads when running many scripts in parallel!