diff --git a/doc/tutorial/doc/embedding.html b/doc/tutorial/doc/embedding.html new file mode 100644 index 00000000..ef64b088 --- /dev/null +++ b/doc/tutorial/doc/embedding.html @@ -0,0 +1,97 @@ + +
+ +
+ |
+ + Embedding + | +
![]() |
+ ![]() |
+ ![]() |
+
+By now you should know how to use Boost.Python to call your C++ code from +Python. However, sometimes you may need to do the reverse: call Python code +from the C++-side. This requires you to embed the Python interpreter +into your C++ program.
+
+Currently, Boost.Python does not directly support everything you'll need
+when embedding. Therefore you'll need to use the
+
+Python/C API to fill in
+the gaps. However, Boost.Python already makes embedding a lot easier and,
+in a future version, it may become unnecessary to touch the Python/C API at
+all. So stay tuned... 
+To be able to use embedding in your programs, they have to be linked to +both Boost.Python's and Python's static link library.
++Boost.Python's static link library comes in two variants. Both are located +in Boost's /libs/python/build/bin-stage subdirectory. On Windows, the +variants are called boost_python.lib (for release builds) and +boost_python_debug.lib (for debugging). If you can't find the libraries, +you probably haven't built Boost.Python yet. See +and Testing on how to do this.
++Python's static link library can be found in the /libs subdirectory of +your Python directory. On Windows it is called pythonXY.lib where X.Y is +your major Python version number.
++Additionally, Python's /include subdirectory has to be added to your +include path.
++In a Jamfile, all the above boils down to:
+
+ projectroot c:\projects\embedded_program ; # location of the program
+
+ # bring in the rules for python
+ SEARCH on python.jam = $(BOOST_BUILD_PATH) ;
+ include python.jam ;
+
+ exe embedded_program # name of the executable
+ : #sources
+ embedded_program.cpp
+ : # requirements
+ <find-library>boost_python <library-path>c:\boost\libs\python
+ $(PYTHON_PROPERTIES)
+ <library-path>$(PYTHON_LIB_PATH)
+ <find-library>$(PYTHON_EMBEDDED_LIBRARY) ;
+
+Being able to build is nice, but there is nothing to build yet. Embedding +the Python interpreter into one of your C++ programs requires these 4 +steps:
++(Of course, there can be other C++ code between all of these steps.)
+Now that we can embed the interpreter in our programs, lets see how to put it to use...
![]() |
+ ![]() |
+ ![]() |
+
Copyright © 2002-2003 Dirk Gerrits
+Permission to copy, use, modify, sell and distribute this document
+ is granted provided this copyright notice appears in all copies. This document
+ is provided "as is" without express or implied warranty, and with
+ no claim as to its suitability for any purpose.
![]() |
![]() |
- ![]() |
+ ![]() |
@@ -82,7 +82,7 @@ create a new scope around a class:




![]() |
- ![]() |
+ ![]() |
![]() |
![]() |
- ![]() |
+ ![]() |
![]() |
+ |
+ + Using the interpreter + | +
![]() |
+ ![]() |
+ ![]() |
+
+As you probably already know, objects in Python are reference-counted. +Naturally, the PyObjects of the Python/C API are also reference-counted. +There is a difference however. While the reference-counting is fully +automatic in Python, the Python/C API requires you to do it + +by hand. This is +messy and especially hard to get right in the presence of C++ exceptions. +Fortunately Boost.Python provides the +handle class +template to automate the process.
++There are two ways in which a function in the Python/C API can return a +PyObject*: as a borrowed reference or as a new reference. Which of +these a function uses, is listed in that function's documentation. The two +require slightely different approaches to reference-counting but both can +be 'handled' by Boost.Python.
++For a function returning a borrowed reference we'll have to tell the +handle that the PyObject* is borrowed with the aptly named + +borrowed function. Two functions +returning borrowed references are +PyImport_AddModule and +PyModule_GetDict. +The former returns a reference to an already imported module, the latter +retrieves a module's namespace dictionary. Let's use them to retrieve the +namespace of the __main__ module:
+
+ handle<> main_module(borrowed( PyImport_AddModule("__main__") ));
+ handle<> main_namespace(borrowed( PyModule_GetDict(main_module.get()) ));
+
++Because the Python/C API doesn't know anything about handles, we used +the +get member function to +retrieve the PyObject* from which the handle was constructed.
++For a function returning a new reference we can just create a handle +out of the raw PyObject* without wrapping it in a call to borrowed. One +such function that returns a new reference is +PyRun_String which we'll +discuss in the next section.
+
+ Handle is a class template, so why haven't we been using any template parameters?+ +handle has a single template parameter specifying the type of the managed object. This type is PyObject 99% of the time, so the parameter was defaulted to PyObject for convenience. Therefore we can use the shorthand handle<> instead of the longer, but equivalent, handle<PyObject>. + |
+
+To run Python code from C++ there is a family of functions in the API +starting with the PyRun prefix. You can find the full list of these +functions +here. They +all work similarly so we will look at only one of them, namely:
+
+ PyObject* PyRun_String(char *str, int start, PyObject *globals, PyObject *locals)
+
++ +PyRun_String takes the code to execute as a null-terminated (C-style) +string in its str parameter. The function returns a new reference to a +Python object. Which object is returned depends on the start paramater.
++The start parameter is the start symbol from the Python grammar to use +for interpreting the code. The possible values are:
+| +Start symbols | +|||||
| +Py_eval_input | for interpreting isolated expressions | +Py_file_input | for interpreting sequences of statements | +Py_single_input | for interpreting a single statement |
+When using +Py_eval_input, the input string must contain a single expression +and its result is returned. When using +Py_file_input, the string can +contain an abitrary number of statements and None is returned. + +Py_single_input works in the same way as +Py_file_input but only accepts a +single statement.
++Lastly, the globals and locals parameters are Python dictionaries +containing the globals and locals of the context in which to run the code. +For most intents and purposes you can use the namespace dictionary of the +__main__ module for both parameters.
++We have already seen how to get the __main__ module's namespace so let's +run some Python code in it:
+
+ handle<> main_module(borrowed( PyImport_AddModule("__main__") ));
+ handle<> main_namespace(borrowed( PyModule_GetDict(main_module.get()) ));
+ handle<>( PyRun_String("hello = file('hello.txt', 'w')\n"
+ "hello.write('Hello world!')\n"
+ "hello.close()", Py_file_input,
+ main_namespace.get(), main_namespace.get()) );
+
++This should create a file called 'hello.txt' in the current directory +containing a phrase that is well-known in programming circles.
+
+
Note that we wrap the return value of
+PyRun_String in a
+(nameless) handle even though we are not interested in it. If we didn't
+do this, the the returned object would be kept alive unnecessarily. Unless
+you want to be a Dr. Frankenstein, always wrap PyObject*s in handles.
+It's nice that handle manages the reference counting details for us, but +other than that it doesn't do much. Often we'd like to have a more useful +class to manipulate Python objects. But we have already seen such a class +in the +previous section: the aptly named object +class and it's derivatives. What we haven't seen, is that they can be +constructed from a handle. The following examples should illustrate this +fact:
+
+ handle<> main_module(borrowed( PyImport_AddModule("__main__") ));
+ main_namespace dict(handle<>(borrowed( PyModule_GetDict(main_module.get()) )));
+ handle<>( PyRun_String("result = 5 ** 2", Py_file_input,
+ main_namespace.ptr(), main_namespace.ptr()) );
+ int five_squared = extract<int>( main_namespace["result"] );
+
++Here we create a dictionary object for the __main__ module's namespace. +Then we assign 5 squared to the result variable and read this variable from +the dictionary. Another way to achieve the same result is to let + +PyRun_String return the result directly with +Py_eval_input:
+
+ object result(handle<>( PyRun_String("5 ** 2", Py_eval_input,
+ main_namespace.ptr(), main_namespace.ptr()) ));
+ int five_squared = extract<int>(result);
+
+
+
Note that object's member function to return the wrapped
+PyObject* is called ptr instead of get. This makes sense if you
+take into account the different functions that object and handle
+perform.
+If an exception occurs in the execution of some Python code, the +PyRun_String function returns a null pointer. Constructing a handle out of this null pointer throws +error_already_set, so basically, the Python exception is automatically translated into a C++ exception when using handle:
+
+ try
+ {
+ object result(handle<>( PyRun_String("5/0", Py_eval_input,
+ main_namespace.ptr(), main_namespace.ptr()) ));
+ // execution will never get here:
+ int five_divided_by_zero = extract<int>(result);
+ }
+ catch(error_already_set)
+ {
+ // handle the exception in some way
+ }
+
++The error_already_set exception class doesn't carry any information in itself. To find out more about the Python exception that occurred, you need to use the +exception handling functions of the Python/C API in your catch-statement. This can be as simple as calling +PyErr_Print() to print the exception's traceback to the console, or comparing the type of the exception with those of the +standard exceptions:
+
+ catch(error_already_set)
+ {
+ if (PyErr_ExceptionMatches(PyExc_ZeroDivisionError))
+ {
+ // handle ZeroDivisionError specially
+ }
+ else
+ {
+ // print all other errors to stderr
+ PyErr_Print();
+ }
+ }
+
++(To retrieve even more information from the exception you can use some of the other exception handling functions listed +here.)
++If you'd rather not have handle throw a C++ exception when it is constructed, you can use the +allow_null function in the same way you'd use borrowed:
+
+ handle<> result(allow_null( PyRun_String("5/0", Py_eval_input,
+ main_namespace.ptr(), main_namespace.ptr()) ));
+ if (!result)
+ // Python exception occurred
+ else
+ // everything went okay, it's safe to use the result
+
+![]() |
+ ![]() |
+ ![]() |
+
Copyright © 2002-2003 Dirk Gerrits
+Permission to copy, use, modify, sell and distribute this document
+ is granted provided this copyright notice appears in all copies. This document
+ is provided "as is" without express or implied warranty, and with
+ no claim as to its suitability for any purpose.