Advertisement

Boost.Python

Started by July 13, 2003 09:17 AM
3 comments, last by c t o a n 21 years, 4 months ago
I''m trying to build a app that both embeds and extends Python. To do this, I''m using the Boost.Python library. I''ve read in the tutorial docs that the ''object'' type can take any other type and convert it to a PyObject*. Here''s my code: Py_Initialize(); ... object blah = object( "Chris" ); ... Py_Finalize(); However, when I run this code (with a whole lot of other stuff, obviously), I get the following error: Adding parser accelerators... Done Fatal Python error... Interpreter not initialized (version mismatch?) This seems quite strange, as I''ve got version 2.2.1 of Python, which is supposed to be stable, and then I built my own Boost.Python libraries. Any suggestions on how to get rid of this? Chris Pergrossi < ctoan > My Realm
Chris PergrossiMy Realm | "Good Morning, Dave"
Ok, an update. I''ve got the objects to work, apparently it had something to do with the debug build. *shrug* Anyway, now I''ve got the problem of when I quit, I get a Thread error.

PyThreadState_Get(): no current thread

?!?! The app has finished, and I''ve called Py_Finalize(). No errors have occured and I''ve used Boost.Python''s ''objects'' to encapsulate all my objects, to make sure they''re reference counted correctly. Whats going on here?

Chris Pergrossi
< ctoan >
My Realm
Chris PergrossiMy Realm | "Good Morning, Dave"
Advertisement
Anyway, I''ve fixed all the problems, and just to allow people who haven''t used Boost.Python, or might be thinking of incorporating Python into their engine, let me give you a rundown of some of the techniques I''ve discovered.
First, if you want to incorporate Python into you''re engine, you''re EMBEDDING it, though it''s not much use without EXTENDING your functions (engine API) to Python so they can be called from within the script. To do these, you could use the included Python C/API and have to spend hours upon hours debugging the darn thing. The solution rests with utilizing a 3rd party ''middleware'' library to help you write the ''glue'' code. Currently, I use Boost.Python (available from www.boost.org, though the doc''s for the library are at www.boost.org/libs/python/doc/) which is REALLY easy to use and is GREAT for this sort of thing. Building the library is a real hassle though, because it uses bjam (a variant of ''jam'') to build. This, however, is a one-time-only step, so once you build the Boost.Python library (you can build it seperately), you can throw away the bjam .exe.
What I wanted to do was allow the Engine to load scripts and then call certain functions to do things like Initialize, Finalize, or to run certain tasks. (isn''t that a great idea? have a seperate script for each task you wish to perform such as ''beginscene()'', ''update all meshes'', or ''render_effect''?) I wanted to be able to simply say myScriptClass( "someFunc", GameObject1, TimeAsFloat, ENUM_BLUE ) and have the function execute, AND call out to C++ functions (note, the GameObject1 is a class built and initialized in the engine). Thats the second action I wanted the scripts to be able to perform, namely, call out to C++ functions. I mean, what good is a script that can only add two variables together? So this is where Boost.Python comes in. You build a class in the engine, say "CLogger", and then build a ''definition'' to go with it. The whole thing would look like this:
// the classclass CLogger{public:   void print( char* string, FILE* output );};// the definitionBOOST_PYTHON_MODULE(system){   class_<CLogger>("CLogger")      .def( "print", &CLogger::print )   ;}

and you''re done! Now, if you wanted to just build an extension module (NO embedding, like we''re doing), you''d just compile this into a DLL file and put it in your Python ''Lib'' directory, and then make a script like this:
import system# call the print functionx = system.CLogger()x.print( "Hello World!", stdout )

Note: stdout isn''t defined in this script, you''d have to include some other modules to compile it properly.
Well, since we want to both embed AND extend Python, we need to have a way to get this ''module'' (it''s called ''system'', check out the first line after the class definition) into Python without compiling a seperate DLL (cause that''s no good!). There''s a simple way. Whenever Python is extended via a module, it calls the modules ''init'' function, which is in the form of ''init()'' minus the ''<>''. Therefore, we simply ''simulate'' this behavior by calling ''initsystem()'' (you have to call it after Py_Initialize(), and before you load any scripts that use it) and POOF, Python knows and wants nothing better then to use this new module in scripts.

Now, for the loading of the Python ''core'', which loads and executes scripts, you have to use the Python C/API. I won''t explain this here, because it''s WELL documented in the Documentation section of the Python website (www.python.org). Anyway, in Python, there are no types, meaning any object could actually be ANY object. Unfortunately, the same does not apply for C++, so we need a way to convert C++ objects to Python ones (called PyObject* <- always come in pointers btw) so we can pass them to the Python C/API which in turn gives them to the script. To do this, theres a wonderful WONDERFUL object called ''object'' which is part of the Boost.Python library (therefore, the actual name of the object is boost:ython::object) and to convert any of your game objects (or integers, strings, whatever) into PyObjects*, all you need is this:
object myObj = object( GameObject1 );myObj.ptr();   // this is a PyObject* :)

Unfortunately, I noticed some problems in my distribution when using the debug library (I don''t know about the debug DLL) that causes a crash whenever this occurs. However, compile in release mode (or ''simulate'' release mode by #undef''ing _DEBUG before you include the Python files if you use VC++ 6.0, don''t know about other compilers...) and this problem dissapears. Now, read up on the Boost.Python exposure methods. For example, it''s possible to expose stand alone functions to python, or set certain variables in classes as ''readonly'' or ''readwrite'' and to specificy certain get/set functions to use if a Python script wants to change it (for reference, its called .add_property()). I also encountered another error, mentioned above, named a problem on exit: PyThreadState_Get(): blah blah. To get around this, I simply included a ''exit(1)'' at the end of my main() (or WinMain) function instead of simply dropping out of it. This solved the problem, with no noticable side effects whatsoever. I hope this helps some people trying to put Python into their engines/games, and I encourage you all to read the documentation behind both libraries (Python and Boost.Python) even if you don''t want this at the moment, it''s very enlightening (especially the BOOST_PYTHON_MODULE(system){ ... } syntax; *whistle*!!). Good luck!

Chris Pergrossi
< ctoan >
My Realm
Chris PergrossiMy Realm | "Good Morning, Dave"
IIRC, the debug version of the library is intended to work with the debug version of the python interpreter ... which is something few people need.

As for the threading problem, look at PyEval_InitThreads(). Calling exit() in C++ is a bad idea, since the application exits without calling the destructors of local variables (no stack unwinding). So no object you have in main() gets cleaned up.

All in all, a nice post

PS. See my reply about distutils for building your extension modules in the SWIG thread.

[ Start Here ! | How To Ask Smart Questions | Recommended C++ Books | C++ FAQ Lite | Function Ptrs | CppTips Archive ]
[ Header Files | File Format Docs | LNK2001 | C++ STL Doc | STLPort | Free C++ IDE | Boost C++ Lib | MSVC6 Lib Fixes ]

[edited by - Fruny on July 14, 2003 5:46:13 AM]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Thanks, I'll take a look at it!

[edit]:
Grrr...Still didn't work. I'm not even using multiple threads! The only reason I can think of this not working is that the app is being destroy before Python gets a chance to finish, but why would this happen? I'm calling Py_Finalize() way before the end of my program... Hmm... I'll keep working on it.

Chris Pergrossi
< ctoan >
My Realm

[edited by - c t o a n on July 14, 2003 8:35:19 AM]
Chris PergrossiMy Realm | "Good Morning, Dave"

This topic is closed to new replies.

Advertisement