Stackless resumable functions

bitwise via Digitalmars-d digitalmars-d at puremagic.com
Mon Feb 23 15:10:28 PST 2015


> you don't need to. if you really need to do that, you're doing 
> something

This makes no sense to me. A usage example may be helpful.

> resumable functions are not iterators. it's a slightly 
> perversed flow
> control method. iteration/generation is one of the use cases.

So how do you explain enumerators in C#, or generators in visual 
c++?

> and, by the way, yielding is a two-way communication channel.

http://www.boost.org/doc/libs/1_57_0/libs/coroutine/doc/html/index.html

[quote]
Coroutine
     Asymmetric coroutine
     Symmetric coroutine
[/quote]

> you seem to stuck with iteration/generation idea, but this is 
> not the way resumable functions should be seen.

Not sure what to say to this..

> mimicking delegates allows to use resumable function in any 
> code that expects delegate.

If you can come up with even one example(with code) where it 
would make
sense to accept both coroutines and delegates, I will be very 
surprised.


In any case, I have revised my design. Generator(T) was 
redundant, so I removed it. Below is something that I think is 
well formed enough for me to start digging through the compiler 
for specifics.



interface MethodRange(T)
{
	@property T front();
	void popFront();
	@property bool empty();
	int opApply(int delegate(T) dg);
}

class __ResumeableMethod(T, METHOD_TYPE, CONTEXT_TYPE) : 
MethodRange!T
{
	// method locals and passed args
	CONTEXT_TYPE* context;

	// -initially contains method entry point
	// -once executed, contains the resume address
	// -when finished, contains null
	void *fptr;
	
	// object reference for instance methods
	void *obj;

	T value;

	this(CONTEXT_TYPE* context, void *fptr, void *obj) {
		this.context = context;
		this.fptr = fptr;
		this.obj = obj;
		invoke(context, &value);
	}

	private T invoke(CONTEXT_TYPE *context, T *yielded_value) {
		fptr(context);
		fptr = context->return_address;

		if(fptr)
			*yielded_value = context->yielded_value;
	}

	@property override T front() {
		assert(!this.empty);
		return value;
	}

	override void popFront() {
		assert(!this.empty);
		invoke(context, &value);
	}
	
	@property override bool empty() {
		return fptr == null;
	}
	
	int opApply(int delegate(T) dg)
	{
		while(!this.empty)
		{
			if(dg(this.front))
				return 1;
		}
		
		return 0;
	}
}


MethodRange!int myResumableMethod()
{
     for(int i = 0; i < 10; ++i)
     	yield i;
}

// the compiler would automatically transform the above
// method into something like the one below

MethodRange!int myResumableMethod()
{
	void __method(__ContextFor__method *ctx)
	{
		for(ctx->i = 0; i < 10; ++i)
		{
			ctx->yielded_value = i;
			ctx->return_address = &return_pos;
			return;
		return_pos:
		}
		
		ctx->return_address = null;
	}

	return new __ResumeableMethod!int(new __ContextFor__method, 
&__method, null);
}

// usage

void main()
{
	foreach(num; myResumableMethod())
	    writeln(num);

// or

	MethodRange!int[] methods;

	auto mr = myResumableMethod();

	if(!mr.empty)
		methods ~= mr;

	for(int i = 0; i < methods.length; )
	{
		auto mr = methods[i];

		int status = mr.front;
		// handle item status..
		mr.popFront();

		if(mr.empty)
			methods.remove(i);
		else
			++i;
	}
}


More information about the Digitalmars-d mailing list