Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

Changes between Version 1 and Version 2 of API/Part2

Show
Ignore:
Author:
JarrettBillingsley (IP: 65.117.184.122)
Timestamp:
08/03/07 19:11:27 (17 years ago)
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • API/Part2

    v1 v2  
    3737}}} 
    3838 
    39 OK, when we create the closure in main, notice the extra parameter we're passing: an array of MDValues.  These are the upvalues, and we just have one upvalue for this function.  In the function 'count', we get the upvalue as an int, increment it, and then store it back in the upvalue.  Don't forget to set the upvalues back  
     39OK, when we create the closure in main, notice the extra parameter we're passing: an array of MDValues.  These are the upvalues, and we just have one upvalue for this function.  In the function 'count', we get the upvalue as an int, increment it, and then store it back in the upvalue.  Don't forget to set the upvalues back after you've modified them. 
     40 
     41Upvalues work, but they can be a little unwieldy.  You have to get them and then set them after you modify them.  Furthermore you can't store anything but convertible types in them, meaning you can't make a closure which keeps some D object or so as state.  Fortunately, there's a simple solution.  D delegates have a context pointer, which can be used to create closures, although they have to be made explicitly.  All we have to do is make a struct which has the function as a member, and keep the persistent state in an instance of that struct. 
     42 
     43{{{ 
     44#!d 
     45void main() 
     46
     47        MDContext ctx = NewContext(); 
     48        ctx.globals["count"d] = ctx.newClosure(&(new Count).count, "count"); 
     49        loadStatementString(ctx.mainThread, 
     50                `writefln(count()); 
     51                writefln(count()); 
     52                writefln(count()); 
     53                ` 
     54        ); 
     55
     56 
     57struct Count 
     58
     59        int val = 0; 
     60 
     61        int count(MDState s, uint numParams) 
     62        { 
     63                val++; 
     64                s.push(val); 
     65                return 1; 
     66        } 
     67
     68}}} 
     69 
     70Our counter example has been rewritten to use a struct to hold the persistent state instead.  The count function is now simpler (and on a side note, more efficient), and we don't have to associate any upvalues with the closure when it's created.  And just like the upvalue method, as long as we use a new instance of the Count struct for each closure, each closure will keep its own state. 
     71 
     72== Coroutines == 
     73 
     74The [LanguageSpec/Functions Functions] section of the language specification tells all about how to create and use coroutines from within MiniD.  Now you'll learn how to do so from D, and it's really not all that different. 
     75 
     76A coroutine is represented by the MDState class.  I guess you could say that there are two kinds of MDStates: "main threads" and "coroutines".  "Main threads" are constructed without any coroutine function, and "coroutines" are.  The equivalent of the MiniD "coroutine" expression is then just the construction of a new MDState with a closure as a parameter to the constructor.  You also have to provide a context which will serve as the parent context of the coroutine.   
     77 
     78The "yield" expression in MiniD is replaced by MDState.yield in D.  It's just as easy to use as it is in MiniD, although it follows the same kind of calling conventions that MDState.call, easyCall, and callMethod follow. 
     79 
     80Finally to resume a coroutine in MiniD, you just call it like a function.  And that's exactly what you do in native code too -- just call it using another state as if it were a function.  When you call a coroutine for the first time (when it leaves the initial state), the context is significant and becomes the context to the top-level coroutine function for the life of the coroutine, just like in MiniD.  Subsequent calls to resume the coroutine ignore the context, again just like in MiniD. 
     81 
     82{{{ 
     83#!d 
     84import minid.minid; 
     85import minid.types; 
     86import tango.io.Stdout; 
     87 
     88void main() 
     89
     90        MDContext ctx = NewContext(); 
     91        MDState s = ctx.mainThread; 
     92        MDState co = new MDState(ctx, ctx.newClosure(&coro, "co")); 
     93 
     94        Stdout.formatln("Going to call the coroutine."); 
     95        s.easyCall(co, 0, MDValue(5)); 
     96        Stdout.formatln("Back in main, giving a value to the coroutine."); 
     97        s.easyCall(co, 2, MDValue.nullValue, "hey there"); 
     98        MDValue r2 = s.pop(); 
     99        MDValue r1 = s.pop(); 
     100        Stdout.formatln("Back in main, got values from the coroutine: {}, {}", r1.toUtf8(), r2.toUtf8()); 
     101        s.easyCall(co, 1, MDValue.nullValue); 
     102        r1 = s.pop(); 
     103        Stdout.formatln("In main, coroutine is now in the '{}' state.  Got the value '{}'.", co.stateString(), r1.toUtf8()); 
     104        co.reset(); 
     105        Stdout.formatln("Reset the coroutine, back in the '{}' state.  Going to restart it with a new context..", co.stateString()); 
     106        s.easyCall(co, 0, MDValue(10)); 
     107
     108 
     109int coro(MDState s, uint numParams) 
     110
     111        Stdout.formatln("Entered the coroutine.  Context is {}.  About to yield.", s.getContext().toUtf8()); 
     112        s.yield(1); 
     113        MDValue x = s.pop(); 
     114        Stdout.formatln("Back in the coroutine, got the following result from yield: '{}', going to yield some values.", x.toUtf8()); 
     115        s.yield(0, MDValue("hi"), MDValue(1)); 
     116        Stdout.formatln("Back in the coroutine, about to return a value."); 
     117        s.push("Finished!"); 
     118        return 1; 
     119
     120}}} 
     121 
     122This example shows both writing and calling a coroutine in native code.  It's a bit more verbose than the corresponding MiniD code would be, but just as powerful.  Notice that there's not even a single line of MiniD code used, it's all native!   
     123 
     124The MDState.yield method is very straightforward and follows the same kind of convention as easyCall and methodCall.  The first parameter to MDState.yield is the number of returns you want, and the rest of the parameters are a variadic list of MDValues (slight difference from the other two).  Pass -1 to mean "as many values as I get" and then get the return value of yield.  When the yield returns, as many values as you requested are on the stack, and you pop them off.