⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 quickstart.txt

📁 boost库提供标准的C++ API 配合dev c++使用,功能更加强大
💻 TXT
📖 第 1 页 / 共 5 页
字号:
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__ [*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
in the [@object_interface.html 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__") ));
    dict main_namespace(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__ [*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.

[h2 Exception handling]

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 [@../../v2/errors.html#error_already_set-spec 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 [@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()] to print the exception's traceback to the console, or comparing the type of the exception with those of the [@http://www.python.org/doc/api/standardExceptions.html 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 [@http://www.python.org/doc/api/exceptionHandling.html here].)

If you'd rather not have [^handle] throw a C++ exception when it is constructed, you can use the [@../../v2/handle.html#allow_null-spec 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

[page Iterators]

In C++, and STL in particular, we see iterators everywhere. Python also has
iterators, 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 end

The typical Python iteration protocol: [^[*for y in x...]] is as follows:

    iter = x.__iter__()         # get iterator
    try:
        while 1:
        y = iter.next()         # get each item
        ...                     # process y
    except StopIteration: pass  # iterator exhausted

Boost.Python provides some mechanisms to make C++ iterators play along
nicely as Python iterators. What we need to do is to produce
appropriate __iter__ function from C++ iterators that is compatible
with the Python iteration protocol. For example:

    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 hypothetical
bogon Particle accelerator code:

    f = Field()
    for x in f.pions:
        smash(x)
    for y in f.bogons:
        count(y)

Now, our C++ Wrapper:

    class_<F>("Field")
        .property("pions", range(&F::p_begin, &F::p_end))
        .property("bogons", range(&F::b_begin, &F::b_end));

[page Exception Translation]

All C++ exceptions must be caught at the boundary with Python code. This
boundary is the point where C++ meets Python. Boost.Python provides a
default exception handler that translates selected standard exceptions,
then gives up:

    raise RuntimeError, 'unidentifiable C++ Exception'

Users may provide custom translation. Here's an example:

    struct PodBayDoorException;
    void translator(PodBayDoorException const& x) {
        PyErr_SetString(PyExc_UserWarning, "I'm sorry Dave...");
    }
    BOOST_PYTHON_MODULE(kubrick) {
         register_exception_translator<
              PodBayDoorException>(translator);
         ...

[page General Techniques]

Here are presented some useful techniques that you can use while wrapping code with Boost.Python.

[page:1 Creating Packages]

A Python package is a collection of modules that provide to the user a certain
functionality. If you're not familiar on how to create packages, a good
introduction to them is provided in the
[@http://www.python.org/doc/current/tut/node8.html Python Tutorial].

But we are wrapping C++ code, using Boost.Python. How can we provide a nice
package interface to our users? To better explain some concepts, let's work
with an example.

We have a C++ library that works with sounds: reading and writing various
formats, applying filters to the sound data, etc. It is named (conveniently)
[^sounds].  Our library already has a neat C++ namespace hierarchy, like so:

    sounds::core
    sounds::io
    sounds::filters

We would like to present this same hierarchy to the Python user, allowing him
to write code like this:

    import sounds.filters
    sounds.filters.echo(...) # echo is a C++ function

The first step is to write the wrapping code. We have to export each module
separately with Boost.Python, like this:

    /* file core.cpp */
    BOOST_PYTHON_MODULE(core)
    {
        /* export everything in the sounds::core namespace */
        ...
    }

    /* file io.cpp */
    BOOST_PYTHON_MODULE(io)
    {
        /* export everything in the sounds::io namespace */
        ...
    }

    /* file filters.cpp */
    BOOST_PYTHON_MODULE(filters)
    {
        /* export everything in the sounds::filters namespace */
        ...
    }

Compiling these files will generate the following Python extensions:
[^core.pyd], [^io.pyd] and [^filters.pyd].

[blurb __note__ The extension [^.pyd] is used for python extension modules, which
are just shared libraries.  Using the default for your system, like [^.so] for
Unix and [^.dll] for Windows, works just as well.]

Now, we create this directory structure for our Python package:

[pre
    sounds/
        __init__.py
        core.pyd
        filters.pyd
        io.pyd
]

The file [^__init__.py] is what tells Python that the directory [^sounds/] is
actually a Python package. It can be a empty file, but can also perform some
magic, that will be shown later.

Now our package is ready. All the user has to do is put [^sounds] into his
[@http://www.python.org/doc/current/tut/node8.html#SECTION008110000000000000000 PYTHONPATH] and fire up the interpreter:

    >>> import sounds.io
    >>> import sounds.filters
    >>> sound = sounds.io.open('file.mp3')
    >>> new_sound = sounds.filters.echo(sound, 1.0)

Nice heh?

This is the simplest way to create hierarchies of packages, but it is not very
flexible. What if we want to add a ['pure] Python function to the filters
package, for instance, one that applies 3 filters in a sound object at once?
Sure, you can do this in C++ and export it, but why not do so in Python? You
don't have to recompile the extension modules, plus it will be easier to write
it.

If we want this flexibility, we will have to complicate our package hierarchy a
little. First, we will have to change the name of the extension modules:

    /* file core.cpp */
    BOOST_PYTHON_MODULE(_core)
    {
        ...
        /* export everything in the sounds::core namespace */
    }

Note that we added an underscore to the module name. The filename will have to
be changed to [^_core.pyd] as well, and we do the same to the other extension modules.
Now, we change our package hierarchy like so:

[pre
    sounds/
        __init__.py
        core/
            __init__.py
            _core.pyd
        filters/
            __init__.py
            _filters.pyd
        io/
            __init__.py
            _io.pyd
]

Note that we created a directory for each extension module, and added a
__init__.py to each one. But if we leave it that way, the user will have to
access the functions in the core module with this syntax:

    >>> import sounds.core._core
    >>> sounds.core._core.foo(...)

which is not what we want. But here enters the [^__init__.py] magic: everything
that is brought to the [^__init__.py] namespace can be accessed directly by the
user.  So, all we have to do is bring the entire namespace from [^_core.pyd]
to [^core/__init__.py]. So add this line of code to [^sounds/core/__init__.py]:

    from _core import *

We do the same for the other packages. Now the user accesses the functions and
classes in the extension modules like before:

    >>> import sounds.filters
    >>> sounds.filters.echo(...)

with the additional benefit that we can easily add pure Python functions to
any module, in a way that the user can't tell the difference between a C++
function and a Python function. Let's add a ['pure] Python function,
[^echo_noise], to the [^filters] package. This function applies both the
[^echo] and [^noise] filters in sequence in the given [^sound] object. We
create a file named [^sounds/filters/echo_noise.py] and code our function:

    import _filters
    def echo_noise(sound):
        s = _filters.echo(sound)
        s = _filters.noise(sound)
        return s

Next, we add this line to [^sounds/filters/__init__.py]:

    from echo_noise import echo_noise

And that's it. The user now accesses this function like any other function
from the [^filters] package:

    >>> import sounds.filters
    >>> sounds.filters.echo_noise(...)

[page:1 Extending Wrapped Objects in Python]

Thanks to Python's flexibility, you can easily add new methods to a class,
even after it was already created:

    >>> class C(object): pass
    >>>
    >>> # a regular function
    >>> def C_str(self): return 'A C instance!'
    >>>
    >>> # now we turn it in a member function
    >>> C.__str__ =

⌨️ 快捷键说明

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