This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| Yield in C++ Submitted by Chris Pergrossi

I'm currently working on an engine that relies heavily on the scripting language Python. Unfortunately, I've discovered major bottlenecks in relying on Python (mostly because it's interpreted), and so am switching to scripts written in C++ (compiled, but seperate to my engine, for ease of development). One of the major disadvantages to doing this would be to loose some of the 'cool' features of Python, such as generators, which allow you to resume functions by using a 'yield' keyword to signal you're ready to quit, and would like to resume from that spot when the function is called again. Today, I solved the problem (took me about 15 minutes actually, so I can only assume many others have discovered this cool feature) by using a bit of ASM (sorry, windows only, though you could probably do something similar on other systems) and some compiler generosity. I developed this using VC++ 6.0, though with minimal changes, it should work on other compilers as well. Well, here's the code. It has two examples, one with a function with a return value, and the other has a function without one (the yield command is a macro, and doesn't use up ANY parameter/return value space). I tried to comment the macros and program as best I could, and one improvement that might be necessary (I don't need it) is the ability to 'reset' the function, to execute from the beginning of the function (instead of wherever the last place you left off is). A cool side-effect of the way I did this was that the calling function doesn't know or care if the function it's calling is a 'generator' or not, it just calls it more than once (same way) to yield/resume! Enjoy Chris Pergrossi
My Realm Email me at only_jolly_roger at hotmail dot com if you have questions, comments, improvements, or have used the tip in any of your projects. Feel free to use the code any way you like, I'm just being curious.

    #include <stdio.h
    #include <conio.h
    #include <iostream.h

    //
    // marks a location in the program for resume
    // does not return control, exits function from inside macro
    //
    // yield( x, ret )
    //      x : the 'name' of the yield, cannot be ambiguous in the
    //          function namespace
    //    ret : the return value for when yield() exits the function;

    //          must match function return type (leave blank for no return type)

    #define yield(x,ret)                            \
        {                                           \
            /* store the resume location */         \
            __asm {                                 \
                mov _myStaticMkr,offset label_##x   \
            }                                       \
                                                    \
            /* return the supplied value */         \
            return ret;                             \
        }                                           \
        /* our offset in the function */            \
        label_##x:

    //
    // resumes function from the stored offset, or
    // continues without notice if there's not one
    // stored
    //
    // resume()
    //   <void

    #define resume()                        \
        /* our stored offset */             \
        static _myStaticMkr=0;              \
                                            \
        /* test for no offset */            \
        if( _myStaticMkr )                  \
        {                                   \
            /* resume from offset */        \
            __asm                           \
            {                               \
                jmp _myStaticMkr            \
            }                               \
        }

    // example demonstrating a function with an int return type
    // using the yield() and resume() macros
    //
    // myFunc()
    //   <void

    int myFunc()
    {
        resume();

        cout << "1\n";

        yield(1,1);

        cout << "2\n";

        yield(2,1);

        cout << "3\n";

        yield(3,1);

        cout << "4\n";

        return 0;
    }

    // main function
    //
    // main()
    //   <void

    void main( void )
    {
        cout << "Yield in C++\n";
        cout << "Chris Pergrossi\n\n";

        myFunc();

        do

        {
            cout << "main()\n";
            cout.flush();
        } while( myFunc() );

        cout.flush();

        getch();
    }

    /*

    // example demonstrating a function with no return type
    // using the yield() and resume() macros
    //
    // myFunc()
    //   <void

    void myFunc()
    {
        resume();

        cout << "1\n";

        yield(1);

        cout << "2\n";

        yield(2);

        cout << "3\n";

        yield(3);

        cout << "4\n";

        return;
    }

    // main function
    //
    // main()
    //   <void

    void main( void )
    {
        cout << "Yield in C++\n";
        cout << "Chris Pergrossi\n\n";

        myFunc();

        for( int k = 0; k < 4; k ++ )
        {
            cout << "main()\n";
            cout.flush();

            myFunc();
        }

        cout.flush();

        getch();
    }

    */

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.