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.
|