#include <FXPython.h>
Embedding Python into your TnFOX application is extremely easy - simply create an instance of the Python interpreter by creating an FXPythonInterp. Thereafter, make calls to Boost.Python passing whatever parameters you like after setting which interpreter you want them to use by setContext(). Full support is provided for multithreaded code, nesting calls to multiple interpreters and the python Global Interpreter Lock (GIL) is correctly maintained.
One issue is what happens when you are embedding python into TnFOX code which is itself embedded in python code? In this situation, you want one of two things: (i) to create a new virgin python environment or (ii) to link into the python environment in which you reside. The former is easy, simply create a new FXPythonInterp. The latter is even easier, simply inspect FXPythonInterp::current() which returns a pointer to the FXPythonInterp responsible for executing the caller (thus if python code calls C++ code, it is set - but if no python is higher up the call stack, it is zero).
Furthermore, nesting of interpreters is possible eg; interpreter A calls C++ code which calls python code in interpreter B which calls some more C++ code. At each stage, current() returns the correct value for the current context. You should note that python treats interpreters and threads running inside an interpreter very similarly and thus a FXPythonInterp exists for each running thread in each interpreter plus an instance for each interpreter you create.
When creating a new interpreter which is the first "touch" of python in the process, the current python program name and arguments will set from FX::FXApp if one of those has been initialised. Otherwise, you must set it yourself (see FX::FXPython). TnFOX.pyd is loaded into your new python environment so you can use it immediately.
The setContext() and unsetContext() methods are used to select the current interpreter that calls to Boost.Python will use. It is very important that for every setContext() there is an equivalent unsetContext() and you must ensure a context is set before calling any python code at all (failing to do this results in an immediate crash and process exit). Furthermore you should be aware that python currently requires mutual exclusion between its interpreters and also between threads running within each of its interpreters so therefore while a setContext() is in place, you are also holding a mutex preventing all other threads in python or going through python from running. Needless to say, you don't want to hold it for too long - and don't rely on this behaviour, as it may change (especially different interpreters being able to run concurrently).
Calls to setContext() and unsetContext() can be nested. When python calls you, the context is released (by the patch applied to boost.python) but when you call python, you must ensure the context is set (our patched pyste ensures all generated bindings for virtual calls do this). You may find FX::FXPython_CtxHold of use which doubly ensures exception safety. Be especially cautious of the fact that setContext() is recursive ie; if a context is set on entry to setContext(), it is restored on unsetContext() and so deadlock is extremely likely.
You must not create threads using python's threading module or any other method - or if you do, these threads must never call TnFOX code. TnFOX relies on QThread creating all the threads as associated state is created with each thread which would be unset otherwise. Besides, currently python's threads behave a bit oddly. v0.5 now permits any QThread calling python for the first time to automatically set up a python thread state so you no longer need worry about it. Note that any threads started before the python DLL is loaded will assume they reference code in the primary interpreter - this contrasts against threads normally inheriting the interpreter in force when the start() method was called for that QThread.
You will almost certainly make great use of Boost.Python's "python like" C++ objects which make interacting between the two environments a real boon. While BPL's documentation isn't great, its reference manual outlines what the following objects can do: dict, list, long, numeric, object, str, tuple
. In particular, you want to read object
and then read it a second time - and remember that BPL reminds you that python isn't C++ by forcing you to do a lot of explicit copy construction. Instead of throwing a error_already_set
exception, TnFOX's customised BPL throws a FX::FXPythonException.
Finally, while it has been intended with the facilities provided here to cover all common usage without having to resort to the Python C API, obviously there are limits. If you want to do specialist things, you will need to dive into the direct API from time to time - remember to maintain reference counts (look into handle<>
in BPL). However, I think that more often than not you will be too quick to "go direct" when in fact BPL does actually provide an easier solution. Ask on the C++-SIG mailing list on python.org.
using namespace FX; using namespace boost::python; { FXPythonCtxHold ctxhold; py("def printNum(no): print no"); object printNum(pyeval("printNum")); long_ bignum((FXulong)-1); fxmessage("\nThe biggest number C++ can handle: "); printNum(bignum); bignum*=bignum; fxmessage("That number multiplied by itself: "); printNum(bignum); }
Public Member Functions | |
FXPythonInterp (int argc=0, char **argv=0, const char *name=0) | |
const char * | name () const throw () |
void | setContext () |
void | unsetContext () |
Static Public Member Functions | |
static FXPythonInterp * | current () |
static void | int_enterCPP () |
static void | int_exitCPP () |
Friends | |
struct | FXPythonInterpPrivate |
class | FXPython |
FX::FXPythonInterp::FXPythonInterp | ( | int | argc = 0 , |
|
char ** | argv = 0 , |
|||
const char * | name = 0 | |||
) |
Creates an instance of the python interpreter optionally with the specified arguments and name. If python previously was not initialised, this initialises python and returns the default interpreter. In all other cases, creates a new interpreter. Note that if this initialises python, when the last is destroyed it will deinitialise python too.
const char* FX::FXPythonInterp::name | ( | ) | const throw () |
Returns the name of this python interpreter.
static FXPythonInterp* FX::FXPythonInterp::current | ( | ) | [static] |
Returns the python interpreter executing the calling code or 0 if there is none.
void FX::FXPythonInterp::setContext | ( | ) |
Sets the execution context to this interpreter.
void FX::FXPythonInterp::unsetContext | ( | ) |
Unsets the execution context. This MUST be matched with a corresponding setContext().