?? tutorial.qbk
字號:
Boost.Python comes with a set of derived [^object] types corresponding to
that of Python's:
* list
* dict
* tuple
* str
* long_
* enum
These derived [^object] types act like real Python types. For instance:
str(1) ==> "1"
Wherever appropriate, a particular derived [^object] has corresponding
Python type's methods. For instance, [^dict] has a [^keys()] method:
d.keys()
[^make_tuple] is provided for declaring ['tuple literals]. Example:
make_tuple(123, 'D', "Hello, World", 0.0);
In C++, when Boost.Python [^object]s are used as arguments to functions,
subtype matching is required. For example, when a function [^f], as
declared below, is wrapped, it will only accept instances of Python's
[^str] type and subtypes.
void f(str name)
{
object n2 = name.attr("upper")(); // NAME = name.upper()
str NAME = name.upper(); // better
object msg = "%s is bigger than %s" % make_tuple(NAME,name);
}
In finer detail:
str NAME = name.upper();
Illustrates that we provide versions of the str type's methods as C++
member functions.
object msg = "%s is bigger than %s" % make_tuple(NAME,name);
Demonstrates that you can write the C++ equivalent of [^"format" % x,y,z]
in Python, which is useful since there's no easy way to do that in std C++.
[blurb
__alert__ [*Beware] the common pitfall of forgetting that the constructors
of most of Python's mutable types make copies, just as in Python.
]
Python:
[python]
>>> d = dict(x.__dict__) # copies x.__dict__
>>> d['whatever'] # modifies the copy
C++:
[c++]
dict d(x.attr("__dict__")); // copies x.__dict__
d['whatever'] = 3; // modifies the copy
[h2 class_<T> as objects]
Due to the dynamic nature of Boost.Python objects, any [^class_<T>] may
also be one of these types! The following code snippet wraps the class
(type) object.
We can use this to create wrapped instances. Example:
object vec345 = (
class_<Vec2>("Vec2", init<double, double>())
.def_readonly("length", &Point::length)
.def_readonly("angle", &Point::angle)
)(3.0, 4.0);
assert(vec345.attr("length") == 5.0);
[endsect]
[section Extracting C++ objects]
At some point, we will need to get C++ values out of object instances. This
can be achieved with the [^extract<T>] function. Consider the following:
double x = o.attr("length"); // compile error
In the code above, we got a compiler error because Boost.Python
[^object] can't be implicitly converted to [^double]s. Instead, what
we wanted to do above can be achieved by writing:
double l = extract<double>(o.attr("length"));
Vec2& v = extract<Vec2&>(o);
assert(l == v.length());
The first line attempts to extract the "length" attribute of the Boost.Python
[^object]. The second line attempts to ['extract] the [^Vec2] object from held
by the Boost.Python [^object].
Take note that we said "attempt to" above. What if the Boost.Python [^object]
does not really hold a [^Vec2] type? This is certainly a possibility considering
the dynamic nature of Python [^object]s. To be on the safe side, if the C++ type
can't be extracted, an appropriate exception is thrown. To avoid an exception,
we need to test for extractibility:
extract<Vec2&> x(o);
if (x.check()) {
Vec2& v = x(); ...
__tip__ The astute reader might have noticed that the [^extract<T>]
facility in fact solves the mutable copying problem:
dict d = extract<dict>(x.attr("__dict__"));
d['whatever'] = 3; # modifies x.__dict__ !
[endsect]
[section Enums]
Boost.Python has a nifty facility to capture and wrap C++ enums. While
Python has no [^enum] type, we'll often want to expose our C++ enums to
Python as an [^int]. Boost.Python's enum facility makes this easy while
taking care of the proper conversions from Python's dynamic typing to C++'s
strong static typing (in C++, ints cannot be implicitly converted to
enums). To illustrate, given a C++ enum:
enum choice { red, blue };
the construct:
enum_<choice>("choice")
.value("red", red)
.value("blue", blue)
;
can be used to expose to Python. The new enum type is created in the
current [^scope()], which is usually the current module. The snippet above
creates a Python class derived from Python's [^int] type which is
associated with the C++ type passed as its first parameter.
[blurb __note__ [*what is a scope?]\n\n The scope is a class that has an
associated global Python object which controls the Python namespace in
which new extension classes and wrapped functions will be defined as
attributes. Details can be found [@../../../v2/scope.html here].]
You can access those values in Python as
[python]
>>> my_module.choice.red
my_module.choice.red
where my_module is the module where the enum is declared. You can also
create a new scope around a class:
[c++]
scope in_X = class_<X>("X")
.def( ... )
.def( ... )
;
// Expose X::nested as X.nested
enum_<X::nested>("nested")
.value("red", red)
.value("blue", blue)
;
[def Py_Initialize [@http://www.python.org/doc/current/api/initialization.html#l2h-652 Py_Initialize]]
[def Py_Finalize [@http://www.python.org/doc/current/api/initialization.html#l2h-656 Py_Finalize]]
[def PyRun_String [@http://www.python.org/doc/current/api/veryhigh.html#l2h-55 PyRun_String]]
[def PyRun_File [@http://www.python.org/doc/current/api/veryhigh.html#l2h-56 PyRun_File]]
[def Py_eval_input [@http://www.python.org/doc/current/api/veryhigh.html#l2h-58 Py_eval_input]]
[def Py_file_input [@http://www.python.org/doc/current/api/veryhigh.html#l2h-59 Py_file_input]]
[def Py_single_input [@http://www.python.org/doc/current/api/veryhigh.html#l2h-60 Py_single_input]]
[def Py_XINCREF [@http://www.python.org/doc/current/api/countingRefs.html#l2h-65 Py_XINCREF]]
[def Py_XDECREF [@http://www.python.org/doc/current/api/countingRefs.html#l2h-67 Py_XDECREF]]
[def PyImport_AppendInittab [@http://www.python.org/doc/current/api/importing.html#l2h-137 PyImport_AppendInittab]]
[def PyImport_AddModule [@http://www.python.org/doc/current/api/importing.html#l2h-125 PyImport_AddModule]]
[def PyModule_New [@http://www.python.org/doc/current/api/moduleObjects.html#l2h-591 PyModule_New]]
[def PyModule_GetDict [@http://www.python.org/doc/current/api/moduleObjects.html#l2h-594 PyModule_GetDict]]
[endsect]
[endsect] [/ Object Interface]
[section 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
[@http://www.python.org/doc/current/api/api.html 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... :-)
[h2 Building embedded programs]
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
[@../../../building.html Building 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:
[pre
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) ;
]
[h2 Getting started]
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:
# '''#include''' [^<boost/python.hpp>]\n\n
# Call Py_Initialize() to start the interpreter and create the [^__main__] module.\n\n
# Call other Python C API routines to use the interpreter.\n\n
# Call Py_Finalize() to stop the interpreter and release its resources.
(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...]]]
[section Using the interpreter]
As you probably already know, objects in Python are reference-counted.
Naturally, the [^PyObject]s 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
[@http://www.python.org/doc/current/api/refcounts.html by hand]. This is
messy and especially hard to get right in the presence of C++ exceptions.
Fortunately Boost.Python provides the [@../../../v2/handle.html handle] and
[@../../../v2/object.html object] class templates to automate the process.
[h2 Reference-counting handles and objects]
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
[@../../../v2/handle.html#borrowed-spec 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:
object main_module((
handle<>(borrowed(PyImport_AddModule("__main__")))));
object main_namespace = main_module.attr("__dict__");
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.
[blurb __note__ [*Handle is a class ['template], so why haven't we been using any template parameters?]\n
\n
[^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>].
]
[h2 Running Python code]
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 [@http://www.python.org/doc/current/api/veryhigh.html 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:
[table 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:
object main_module((
handle<>(borrowed(PyImport_AddModule("__main__")))));
object main_namespace = main_module.attr("__dict__");
handle<> ignored((PyRun_String(
"hello = file('hello.txt', 'w')\n"
"hello.write('Hello world!')\n"
"hello.close()"
, Py_file_input
, main_namespace.ptr()
, main_namespace.ptr())
));
Because the Python/C API doesn't know anything about [^object]s, we used
the object's [^ptr] member function to retrieve the [^PyObject*].
This should create a file called 'hello.txt' in the current directory
containing a phrase that is well-known in programming circles.
[blurb
__note__ [*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 [^handle]s.
]
[h2 Beyond 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
above, and in the [@python/object.html previous section]: the aptly
named [^object] class and it's derivatives. We've already seen that they
can be constructed from a [^handle]. The following examples should further
illustrate this fact:
object main_module((
handle<>(borrowed(PyImport_AddModule("__main__")))));
object main_namespace = main_module.attr("__dict__");
handle<> ignored((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"
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -