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 + -
显示快捷键?