tutorial.qbk

来自「Boost provides free peer-reviewed portab」· QBK 代码 · 共 1,973 行 · 第 1/5 页

QBK
1,973
字号
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>] mayalso 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. Thiscan be achieved with the [^extract<T>] function. Consider the following:    double x = o.attr("length"); // compile errorIn the code above, we got a compiler error because Boost.Python[^object] can't be implicitly converted to [^double]s. Instead, whatwe 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 heldby 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 consideringthe dynamic nature of Python [^object]s. To be on the safe side, if the C++ typecan'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. WhilePython has no [^enum] type, we'll often want to expose our C++ enums toPython as an [^int]. Boost.Python's enum facility makes this easy whiletaking care of the proper conversions from Python's dynamic typing to C++'sstrong static typing (in C++, ints cannot be implicitly converted toenums). 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 thecurrent [^scope()], which is usually the current module. The snippet abovecreates a Python class derived from Python's [^int] type which isassociated with the C++ type passed as its first parameter.[note [*what is a scope?]The scope is a class that has an associated global Python object whichcontrols the Python namespace in which new extension classes and wrappedfunctions 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.redwhere my_module is the module where the enum is declared. You can alsocreate 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 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 fromPython. However, sometimes you may need to do the reverse: call Python codefrom the C++-side. This requires you to ['embed] the Python interpreterinto your C++ program.Currently, Boost.Python does not directly support everything you'll needwhen embedding. Therefore you'll need to use the[@http://www.python.org/doc/current/api/api.html Python/C API] to fill inthe 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 atall. So stay tuned... :-)[h2 Building embedded programs]To be able to embed python into your programs, you have to link toboth Boost.Python's as well as Python's own runtime library.Boost.Python's library comes in two variants. Both are locatedin Boost's [^/libs/python/build/bin-stage] subdirectory. On Windows, thevariants 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 library can be found in the [^/libs] subdirectory ofyour Python directory. On Windows it is called pythonXY.lib where X.Y isyour major Python version number.Additionally, Python's [^/include] subdirectory has to be added to yourinclude path.In a Jamfile, all the above boils down to:[preprojectroot c:\projects\embedded_program ; # location of the program# bring in the rules for pythonSEARCH 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. Embeddingthe Python interpreter into one of your C++ programs requires these 4steps:# '''#include''' [^<boost/python.hpp>]# Call Py_Initialize() to start the interpreter and create the [^__main__] module.# Call other Python C API routines to use the interpreter.[/ # Call Py_Finalize() to stop the interpreter and release its resources.][note [*Note that at this time you must not call Py_Finalize() to stop theinterpreter. This may be fixed in a future version of boost.python.]](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 fullyautomatic in Python, the Python\/C API requires you to do it[@http://www.python.org/doc/current/api/refcounts.html by hand]. This ismessy 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 Running Python code]Boost.python provides three related functions to run Python code from C++.    object eval(str expression, object globals = object(), object locals = object())    object exec(str code, object globals = object(), object locals = object())    object exec_file(str filename, object globals = object(), object locals = object())eval evaluates the given expression and returns the resulting value.exec executes the given code (typically a set of statements) returning the result,and exec_file executes the code contained in the given file.The [^globals] and [^locals] parameters are Python dictionariescontaining 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.Boost.python provides a function to import a module:    object import(str name)import imports a python module (potentially loading it into the running processfirst), and returns it.Let's import the [^__main__] module and run some Python code in its namespace:    object main_module = import("__main__");    object main_namespace = main_module.attr("__dict__");    object ignored = exec("hello = file('hello.txt', 'w')\n"                          "hello.write('Hello world!')\n"                          "hello.close()",                          main_namespace);This should create a file called 'hello.txt' in the current directorycontaining a phrase that is well-known in programming circles.[h2 Manipulating Python objects]Often we'd like to have a 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] classand its derivatives. We've already seen that they can be constructed froma [^handle]. The following examples should further illustrate this fact:    object main_module = import("__main__");    object main_namespace = main_module.attr("__dict__");    object ignored = exec("result = 5 ** 2", main_namespace);    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 fromthe dictionary. Another way to achieve the same result is to use eval instead,which returns the result directly:    object result = eval("5 ** 2");    int five_squared = extract<int>(result);[h2 Exception handling]If an exception occurs in the evaluation of the python expression,[@../../../v2/errors.html#error_already_set-spec error_already_set] is thrown:    try    {        object result = eval("5/0");        // execution will never get here:        int five_divided_by_zero = extract<int>(result);    }    catch(error_already_set const &)    {        // 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[@http://www.python.org/doc/api/exceptionHandling.html exception handling functions]of the Python/C API in your catch-statement. This can be as simple as calling[@http://www.python.org/doc/api/exceptionHandling.html#l2h-70 PyErr_Print()] toprint the exception's traceback to the console, or comparing the type of theexception with those of the [@http://www.python.org/doc/api/standardExceptions.htmlstandard exceptions]:    catch(error_already_set const &)    {        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 otherexception handling functions listed [@http://www.python.org/doc/api/exceptionHandling.html here].)[endsect][endsect] [/ Embedding][section Iterators]In C++, and STL in particular, we see iterators everywhere. Python also hasiterators, but these are two very different beasts.[*C++ iterators:]* C++ has 5 type categories (random-access, bidirectional, forward, input, output)* There are 2 Operation categories: reposition, access* A pair of iterators is needed to represent a (first/last) range.[*Python Iterators:]* 1 category (forward)* 1 operation category (next())* Raises StopIteration exception at endThe typical Python iteration protocol: [^[*for y in x...]] is as follows:[python]    iter = x.__iter__()         # get iterator    try:        while 1:        y = iter.next()         # get each item        ...                     # process y    except StopIteration: pass  # iterator exhaustedBoost.Python provides some mechanisms to make C++ iterators play alongnicely as Python iterators. What we need to do is to produceappropriate `__iter__` function from C++ iterators that is compatiblewith the Python iteration protocol. For example:[c++]    object get_iterator = iterator<vector<int> >();    object iter = get_iterator(v);    object first = iter.next();Or for use in class_<>:    .def("__iter__", iterator<vector<int> >())[*range]We can create a Python savvy iterator using the range function:* range(start, finish)* range<Policies,Target>(start, finish)Here, start/finish may be one of:* member data pointers* member function pointers* adaptable function object (use Target parameter)[*iterator]* iterator<T, Policies>()Given a container [^T], iterator is a shortcut that simply calls [^range]with &T::begin, &T::end.Let's put this into action... Here's an example from some hypotheticalbogon Particle accelerator code:[python]    f = Field()    for x in f.pions:        smash(x)    for y in f.bogons:        count(y)Now, our C++ Wrapper:[c++]    class_<F>("Field")        .property("pions", range(&F::p_begin, &F::p_end))        .property("bogons", range(&F::b_begin, &F::b_end));[*stl_input_iterator]

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?