From 6fee43fc6fa973a3c6781744466ded22f59fa465 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Mon, 31 Oct 2005 18:50:18 +0000 Subject: [PATCH] tests and docs for stl_input_iterator [SVN r31514] --- build/Jamfile | 1 + build/Jamfile.v2 | 1 + build/VisualStudio/boost_python.dsp | 12 + doc/tutorial/doc/html/index.html | 88 ++- doc/tutorial/doc/html/python/embedding.html | 484 +++++++----- doc/tutorial/doc/html/python/exception.html | 37 +- doc/tutorial/doc/html/python/exposing.html | 770 +++++++++++-------- doc/tutorial/doc/html/python/functions.html | 700 ++++++++++------- doc/tutorial/doc/html/python/hello.html | 275 ++++--- doc/tutorial/doc/html/python/iterators.html | 170 ++-- doc/tutorial/doc/html/python/object.html | 388 ++++++---- doc/tutorial/doc/html/python/techniques.html | 542 +++++++------ doc/tutorial/doc/tutorial.qbk | 32 + doc/v2/reference.html | 14 + doc/v2/stl_iterator.html | 269 +++++++ src/object/stl_iterator.cpp | 42 + test/Jamfile | 2 + test/Jamfile.v2 | 2 + test/stl_iterator.cpp | 31 + test/stl_iterator.py | 29 + 20 files changed, 2458 insertions(+), 1431 deletions(-) create mode 100755 doc/v2/stl_iterator.html create mode 100755 src/object/stl_iterator.cpp create mode 100755 test/stl_iterator.cpp create mode 100644 test/stl_iterator.py diff --git a/build/Jamfile b/build/Jamfile index 3d38585a..f4293c0d 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -51,6 +51,7 @@ if [ check-python-config ] converter/builtin_converters.cpp converter/arg_to_python_base.cpp object/iterator.cpp + object/stl_iterator.cpp object_protocol.cpp object_operators.cpp wrapper.cpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index c7a98407..ab40a949 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -50,6 +50,7 @@ lib boost_python converter/builtin_converters.cpp converter/arg_to_python_base.cpp object/iterator.cpp + object/stl_iterator.cpp object_protocol.cpp object_operators.cpp wrapper.cpp diff --git a/build/VisualStudio/boost_python.dsp b/build/VisualStudio/boost_python.dsp index 87e24065..b05e91bf 100644 --- a/build/VisualStudio/boost_python.dsp +++ b/build/VisualStudio/boost_python.dsp @@ -179,6 +179,10 @@ SOURCE=..\..\src\slice.cpp # End Source File # Begin Source File +SOURCE=..\..\src\object\stl_iterator.cpp +# End Source File +# Begin Source File + SOURCE=..\..\src\str.cpp # End Source File # Begin Source File @@ -602,6 +606,10 @@ SOURCE=..\..\..\..\boost\python\object\select_holder.hpp # End Source File # Begin Source File +SOURCE=..\..\..\..\boost\python\object\stl_iterator_core.hpp +# End Source File +# Begin Source File + SOURCE=..\..\..\..\boost\python\object\value_holder.hpp # End Source File # Begin Source File @@ -859,6 +867,10 @@ SOURCE=..\..\..\..\boost\python\slice_nil.hpp # End Source File # Begin Source File +SOURCE=..\..\..\..\boost\python\stl_iterator.hpp +# End Source File +# Begin Source File + SOURCE=..\..\..\..\boost\python\str.hpp # End Source File # Begin Source File diff --git a/doc/tutorial/doc/html/index.html b/doc/tutorial/doc/html/index.html index 9335a8ae..4929ad50 100644 --- a/doc/tutorial/doc/html/index.html +++ b/doc/tutorial/doc/html/index.html @@ -28,15 +28,13 @@

David Abrahams

-
+
-

- Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - - http://www.boost.org/LICENSE_1_0.txt - ) - +

+ Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt )

@@ -85,47 +83,59 @@

QuickStart

-The Boost Python Library is a framework for interfacing Python and -C++. It allows you to quickly and seamlessly expose C++ classes -functions and objects to Python, and vice-versa, using no special -tools -- just your C++ compiler. It is designed to wrap C++ interfaces -non-intrusively, so that you should not have to change the C++ code at -all in order to wrap it, making Boost.Python ideal for exposing -3rd-party libraries to Python. The library's use of advanced -metaprogramming techniques simplifies its syntax for users, so that -wrapping code takes on the look of a kind of declarative interface -definition language (IDL).

+ QuickStartThe Boost Python Library is a framework for interfacing Python and + C++. It allows you to quickly and seamlessly expose C++ classes functions and + objects to Python, and vice-versa, using no special tools -- just your C++ + compiler. It is designed to wrap C++ interfaces non-intrusively, so that you + should not have to change the C++ code at all in order to wrap it, making Boost.Python + ideal for exposing 3rd-party libraries to Python. The library's use of advanced + metaprogramming techniques simplifies its syntax for users, so that wrapping + code takes on the look of a kind of declarative interface definition language + (IDL). +

-Hello World

+ + Hello World +

-Following C/C++ tradition, let's start with the "hello, world". A C++ -Function:

-
char const* greet()
-{
-   return "hello, world";
-}
+ Following C/C++ tradition, let's start with the "hello, world". A + C++ Function: +

+
+char const* greet()
+{
+   return "hello, world";
+}
+

-can be exposed to Python by writing a Boost.Python wrapper:

-
#include <boost/python.hpp>
-using namespace boost::python;
+      can be exposed to Python by writing a Boost.Python wrapper:
+    

+
+#include <boost/python.hpp>
+using namespace boost::python;
 
-BOOST_PYTHON_MODULE(hello)
-{
-    def("greet", greet);
-}
+BOOST_PYTHON_MODULE(hello) +{ + def("greet", greet); +} +

-That's it. We're done. We can now build this as a shared library. The -resulting DLL is now visible to Python. Here's a sample Python session:

+ That's it. We're done. We can now build this as a shared library. The resulting + DLL is now visible to Python. Here's a sample Python session: +

-
>>> import hello
->>> print hello.greet()
-hello, world
+
+>>> import hello
+>>> print hello.greet()
+hello, world
+

-

Next stop... Building your Hello World module from start to finish...

+

Next stop... Building your Hello World module + from start to finish...

- +

Last revised: July 12, 2005 at 07:50:43 GMT

Last revised: October 31, 2005 at 18:46:06 GMT


diff --git a/doc/tutorial/doc/html/python/embedding.html b/doc/tutorial/doc/html/python/embedding.html index af8e9fed..fa6cce61 100644 --- a/doc/tutorial/doc/html/python/embedding.html +++ b/doc/tutorial/doc/html/python/embedding.html @@ -27,39 +27,47 @@ Embedding
Using the interpreter

-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.

+ EmbeddingBy 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...

+ 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...

-Building embedded programs

+ + 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.

+ 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 and Testing on how to do this.

+ 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 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.

+ 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.

+ 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
+      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) ;
@@ -73,90 +81,121 @@ exe embedded_program # name of the executable
   $(PYTHON_PROPERTIES)
     <library-path>$(PYTHON_LIB_PATH)
     <find-library>$(PYTHON_EMBEDDED_LIBRARY) ;
-
+

-Getting started

+ + 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:

+ 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: +

  1. -#include <boost/python.hpp>

    + #include <boost/python.hpp>

  2. -Call Py_Initialize() to start the interpreter and create the _main_ module.

    + Call Py_Initialize() + to start the interpreter and create the _main_ + module.

  3. -Call other Python C API routines to use the interpreter.

    + Call other Python C API routines to use the interpreter.

  4. -Call Py_Finalize() to stop the interpreter and release its resources. -
  5. + 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...

+ (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...

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 and -object class templates to automate the process.

+ Using the interpreterAs 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 + and object class templates to + automate the process. +

-Reference-counting handles and objects

+ + 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.

+ 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:

-
object main_module((
-    handle<>(borrowed(PyImport_AddModule("__main__")))));
+        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:
+      

+
+object main_module((
+    handle<>(borrowed(PyImport_AddModule("__main__")))));
 
-object main_namespace = main_module.attr("__dict__");
+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.

+ 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>. +
-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>. -

-Running Python code

+ + 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 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.

+ 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:

+ The start parameter is the start symbol from the Python + grammar to use for interpreting the code. The possible values are: +

-Start symbols +Start symbols

@@ -165,178 +204,215 @@ for interpreting the code. The possible values are:

- + - + - +
Py_eval_inputfor interpreting isolated expressionsfor + interpreting isolated expressions
Py_file_inputfor interpreting sequences of statementsfor + interpreting sequences of statements
Py_single_inputfor interpreting a single statementfor + 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.

+ 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.

+ 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__")))));
+        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__");
+object main_namespace = main_module.attr("__dict__");
 
-handle<> ignored((PyRun_String(
+handle<> ignored((PyRun_String(
 
-    "hello = file('hello.txt', 'w')\n"
-    "hello.write('Hello world!')\n"
-    "hello.close()"
+    "hello = file('hello.txt', 'w')\n"
+    "hello.write('Hello world!')\n"
+    "hello.close()"
 
-  , Py_file_input
-  , main_namespace.ptr()
-  , main_namespace.ptr())
-));
+ , Py_file_input + , main_namespace.ptr() + , main_namespace.ptr()) +)); +

-Because the Python/C API doesn't know anything about objects, we used -the object's ptr member function to retrieve the PyObject*.

+ Because the Python/C API doesn't know anything about objects, + 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.

+ 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. +
-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. -

-Beyond handles

+ + 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 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__")))));
+        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 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__");
+object main_namespace = main_module.attr("__dict__");
 
-handle<> ignored((PyRun_String(
+handle<> ignored((PyRun_String(
 
-    "result = 5 ** 2"
+    "result = 5 ** 2"
 
-    , Py_file_input
-    , main_namespace.ptr()
-    , main_namespace.ptr())
-));
+    , Py_file_input
+    , main_namespace.ptr()
+    , main_namespace.ptr())
+));
 
-int five_squared = extract<int>(main_namespace["result"]);
+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()))
-));
+        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);
+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.
-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. -

-Exception handling

+ + 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 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()))
-    ));
+        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
-}
+ // 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();
-    }
-}
+ 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.)

+ (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 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
-
+if (!result) + // Python exception occurred +else + // everything went okay, it's safe to use the result +
- +
Copyright © 2002-2005 Joel de Guzman, David AbrahamsCopyright © 2002-2005 Joel + de Guzman, David Abrahams

diff --git a/doc/tutorial/doc/html/python/exception.html b/doc/tutorial/doc/html/python/exception.html index 756e7555..cc6d632e 100644 --- a/doc/tutorial/doc/html/python/exception.html +++ b/doc/tutorial/doc/html/python/exception.html @@ -26,25 +26,32 @@

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'
+ Exception TranslationAll 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);
-     ...
+ 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);
+     ...
+
- +
Copyright © 2002-2005 Joel de Guzman, David AbrahamsCopyright © 2002-2005 Joel + de Guzman, David Abrahams

diff --git a/doc/tutorial/doc/html/python/exposing.html b/doc/tutorial/doc/html/python/exposing.html index 49310857..4959c175 100644 --- a/doc/tutorial/doc/html/python/exposing.html +++ b/doc/tutorial/doc/html/python/exposing.html @@ -35,284 +35,373 @@
Class Operators/Special Functions

-Now let's expose a C++ class to Python.

+ Exposing ClassesNow let's expose a C++ class to Python. +

-Consider a C++ class/struct that we want to expose to Python:

-
struct World
-{
-    void set(std::string msg) { this->msg = msg; }
-    std::string greet() { return msg; }
-    std::string msg;
-};
+ Consider a C++ class/struct that we want to expose to Python: +

+
+struct World
+{
+    void set(std::string msg) { this->msg = msg; }
+    std::string greet() { return msg; }
+    std::string msg;
+};
+

-We can expose this to Python by writing a corresponding Boost.Python -C++ Wrapper:

-
#include <boost/python.hpp>
-using namespace boost::python;
+      We can expose this to Python by writing a corresponding Boost.Python C++ Wrapper:
+    

+
+#include <boost/python.hpp>
+using namespace boost::python;
 
-BOOST_PYTHON_MODULE(hello)
-{
-    class_<World>("World")
-        .def("greet", &World::greet)
-        .def("set", &World::set)
-    ;
-}
+BOOST_PYTHON_MODULE(hello) +{ + class_<World>("World") + .def("greet", &World::greet) + .def("set", &World::set) + ; +} +

-Here, we wrote a C++ class wrapper that exposes the member functions -greet and set. Now, after building our module as a shared library, we -may use our class World in Python. Here's a sample Python session:

+ Here, we wrote a C++ class wrapper that exposes the member functions greet + and set. Now, after building our module as a shared library, + we may use our class World in Python. Here's a sample Python + session: +

-
>>> import hello
->>> planet = hello.World()
->>> planet.set('howdy')
->>> planet.greet()
-'howdy'
+
+>>> import hello
+>>> planet = hello.World()
+>>> planet.set('howdy')
+>>> planet.greet()
+'howdy'
+

Constructors

-Our previous example didn't have any explicit constructors. -Since World is declared as a plain struct, it has an implicit default -constructor. Boost.Python exposes the default constructor by default, -which is why we were able to write

-
>>> planet = hello.World()
+ ConstructorsOur previous example didn't have any explicit constructors. Since + World is declared as a plain struct, it has an implicit + default constructor. Boost.Python exposes the default constructor by default, + which is why we were able to write +

+
+>>> planet = hello.World()
+

-We may wish to wrap a class with a non-default constructor. Let us -build on our previous example:

+ We may wish to wrap a class with a non-default constructor. Let us build + on our previous example: +

-
struct World
-{
-    World(std::string msg): msg(msg) {} // added constructor
-    void set(std::string msg) { this->msg = msg; }
-    std::string greet() { return msg; }
-    std::string msg;
-};
+
+struct World
+{
+    World(std::string msg): msg(msg) {} // added constructor
+    void set(std::string msg) { this->msg = msg; }
+    std::string greet() { return msg; }
+    std::string msg;
+};
+

-This time World has no default constructor; our previous -wrapping code would fail to compile when the library tried to expose -it. We have to tell class_<World> about the constructor we want to -expose instead.

-
#include <boost/python.hpp>
-using namespace boost::python;
+        This time World has no default constructor; our previous
+        wrapping code would fail to compile when the library tried to expose it.
+        We have to tell class_<World> about the constructor
+        we want to expose instead.
+      

+
+#include <boost/python.hpp>
+using namespace boost::python;
 
-BOOST_PYTHON_MODULE(hello)
-{
-    class_<World>("World", init<std::string>())
-        .def("greet", &World::greet)
-        .def("set", &World::set)
-    ;
-}
-

init<std::string>() exposes the constructor taking in a -std::string (in Python, constructors are spelled -""_init_"").

+BOOST_PYTHON_MODULE(hello) +{ + class_<World>("World", init<std::string>()) + .def("greet", &World::greet) + .def("set", &World::set) + ; +} +
+

init<std::string>() exposes the constructor taking + in a std::string (in Python, constructors are spelled + ""_init_""). +

-We can expose additional constructors by passing more init<...>s to -the def() member function. Say for example we have another World -constructor taking in two doubles:

-
class_<World>("World", init<std::string>())
-    .def(init<double, double>())
-    .def("greet", &World::greet)
-    .def("set", &World::set)
-;
+ We can expose additional constructors by passing more init<...>s + to the def() member function. Say for example we have + another World constructor taking in two doubles: +

+
+class_<World>("World", init<std::string>())
+    .def(init<double, double>())
+    .def("greet", &World::greet)
+    .def("set", &World::set)
+;
+

-On the other hand, if we do not wish to expose any constructors at -all, we may use no_init instead:

-
class_<Abstract>("Abstract", no_init)
+ On the other hand, if we do not wish to expose any constructors at all, we + may use no_init instead: +

+
+class_<Abstract>("Abstract", no_init)
+

-This actually adds an _init_ method which always raises a -Python RuntimeError exception.

+ This actually adds an _init_ + method which always raises a Python RuntimeError exception. +

Class Data Members

-Data members may also be exposed to Python so that they can be -accessed as attributes of the corresponding Python class. Each data -member that we wish to be exposed may be regarded as read-only or -read-write. Consider this class Var:

-
struct Var
-{
-    Var(std::string name) : name(name), value() {}
-    std::string const name;
-    float value;
-};
+ Class Data MembersData members may also be exposed to Python so that they + can be accessed as attributes of the corresponding Python class. Each data + member that we wish to be exposed may be regarded as read-only + or read-write. Consider this class Var: +

+
+struct Var
+{
+    Var(std::string name) : name(name), value() {}
+    std::string const name;
+    float value;
+};
+

-Our C++ Var class and its data members can be exposed to Python:

-
class_<Var>("Var", init<std::string>())
-    .def_readonly("name", &Var::name)
-    .def_readwrite("value", &Var::value);
+ Our C++ Var class and its data members can be exposed + to Python: +

+
+class_<Var>("Var", init<std::string>())
+    .def_readonly("name", &Var::name)
+    .def_readwrite("value", &Var::value);
+

-Then, in Python, assuming we have placed our Var class inside the namespace -hello as we did before:

+ Then, in Python, assuming we have placed our Var class inside the namespace + hello as we did before: +

-
>>> x = hello.Var('pi')
->>> x.value = 3.14
->>> print x.name, 'is around', x.value
-pi is around 3.14
+
+>>> x = hello.Var('pi')
+>>> x.value = 3.14
+>>> print x.name, 'is around', x.value
+pi is around 3.14
+

-Note that name is exposed as read-only while value is exposed -as read-write.

-
>>> x.name = 'e' # can't change name
-Traceback (most recent call last):
-  File "<stdin>", line 1, in#
-AttributeError: can#t set attribute
+ Note that name is exposed as read-only + while value is exposed as read-write. +

+
+>>> x.name = 'e' # can't change name
+Traceback (most recent call last):
+  File "<stdin>", line 1, in #
+AttributeError: can#t set attribute
+

Class Properties

-In C++, classes with public data members are usually frowned -upon. Well designed classes that take advantage of encapsulation hide -the class' data members. The only way to access the class' data is -through access (getter/setter) functions. Access functions expose class -properties. Here's an example:

+ Class PropertiesIn C++, classes with public data members are usually frowned + upon. Well designed classes that take advantage of encapsulation hide the + class' data members. The only way to access the class' data is through access + (getter/setter) functions. Access functions expose class properties. Here's + an example: +

-
struct Num
-{
-    Num();
-    float get() const;
-    void set(float value);
-    ...
-};
+
+struct Num
+{
+    Num();
+    float get() const;
+    void set(float value);
+    ...
+};
+

-However, in Python attribute access is fine; it doesn't neccessarily break -encapsulation to let users handle attributes directly, because the -attributes can just be a different syntax for a method call. Wrapping our -Num class using Boost.Python:

-
class_<Num>("Num")
-    .add_property("rovalue", &Num::get)
-    .add_property("value", &Num::get, &Num::set);
+ However, in Python attribute access is fine; it doesn't neccessarily break + encapsulation to let users handle attributes directly, because the attributes + can just be a different syntax for a method call. Wrapping our Num + class using Boost.Python: +

+
+class_<Num>("Num")
+    .add_property("rovalue", &Num::get)
+    .add_property("value", &Num::get, &Num::set);
+

-And at last, in Python:

+ And at last, in Python: +

-
>>> x = Num()
->>> x.value = 3.14
->>> x.value, x.rovalue
-(3.14, 3.14)
->>> x.rovalue = 2.17 # error!
-
+
+>>> x = Num()
+>>> x.value = 3.14
+>>> x.value, x.rovalue
+(3.14, 3.14)
+>>> x.rovalue = 2.17 # error!
+

-Take note that the class property rovalue is exposed as read-only -since the rovalue setter member function is not passed in:

+ Take note that the class property rovalue is exposed as + read-only since the rovalue + setter member function is not passed in: +

-
.add_property("rovalue", &Num::get)
+
+.add_property("rovalue", &Num::get)
+

Inheritance

-In the previous examples, we dealt with classes that are not polymorphic. -This is not often the case. Much of the time, we will be wrapping -polymorphic classes and class hierarchies related by inheritance. We will -often have to write Boost.Python wrappers for classes that are derived from -abstract base classes.

+ InheritanceIn the previous examples, we dealt with classes that are not polymorphic. + This is not often the case. Much of the time, we will be wrapping polymorphic + classes and class hierarchies related by inheritance. We will often have + to write Boost.Python wrappers for classes that are derived from abstract + base classes. +

-Consider this trivial inheritance structure:

-
struct Base { virtual ~Base(); };
-struct Derived : Base {};
+ Consider this trivial inheritance structure: +

+
+struct Base { virtual ~Base(); };
+struct Derived : Base {};
+

-And a set of C++ functions operating on Base and Derived object -instances:

-
void b(Base*);
-void d(Derived*);
-Base* factory() { return new Derived; }
+ And a set of C++ functions operating on Base and Derived + object instances: +

+
+void b(Base*);
+void d(Derived*);
+Base* factory() { return new Derived; }
+

-We've seen how we can wrap the base class Base:

-
class_<Base>("Base")
-    /*...*/
-    ;
+ We've seen how we can wrap the base class Base: +

+
+class_<Base>("Base")
+    /*...*/
+    ;
+

-Now we can inform Boost.Python of the inheritance relationship between -Derived and its base class Base. Thus:

-
class_<Derived, bases<Base> >("Derived")
-    /*...*/
-    ;
+ Now we can inform Boost.Python of the inheritance relationship between Derived + and its base class Base. Thus: +

+
+class_<Derived, bases<Base> >("Derived")
+    /*...*/
+    ;
+

-Doing so, we get some things for free:

+ Doing so, we get some things for free: +

  1. -Derived automatically inherits all of Base's Python methods - (wrapped C++ member functions) -
  2. + Derived automatically inherits all of Base's Python methods (wrapped C++ + member functions) +
  3. -If Base is polymorphic, Derived objects which have been passed to - Python via a pointer or reference to Base can be passed where a pointer - or reference to Derived is expected. -
  4. +If Base is polymorphic, Derived + objects which have been passed to Python via a pointer or reference to + Base can be passed where a pointer or reference to + Derived is expected. +

-Now, we shall expose the C++ free functions b and d and factory:

-
def("b", b);
-def("d", d);
-def("factory", factory);
+ Now, we shall expose the C++ free functions b and d + and factory: +

+
+def("b", b);
+def("d", d);
+def("factory", factory);
+

-Note that free function factory is being used to generate new -instances of class Derived. In such cases, we use -return_value_policy<manage_new_object> to instruct Python to adopt -the pointer to Base and hold the instance in a new Python Base -object until the the Python object is destroyed. We shall see more of -Boost.Python call policies later.

-
// Tell Python to take ownership of factory's result
-def("factory", factory,
-    return_value_policy<manage_new_object>());
+ Note that free function factory is being used to generate + new instances of class Derived. In such cases, we use + return_value_policy<manage_new_object> to instruct + Python to adopt the pointer to Base and hold the instance + in a new Python Base object until the the Python object + is destroyed. We shall see more of Boost.Python call + policies later. +

+
+// Tell Python to take ownership of factory's result
+def("factory", factory,
+    return_value_policy<manage_new_object>());
+

Class Virtual Functions

-In this section, we shall learn how to make functions behave polymorphically -through virtual functions. Continuing our example, let us add a virtual function -to our Base class:

-
struct Base
-{
-    virtual ~Base() {}
-    virtual int f() = 0;
-};
+ Class Virtual FunctionsIn this section, we shall learn how to make functions + behave polymorphically through virtual functions. Continuing our example, + let us add a virtual function to our Base class: +

+
+struct Base
+{
+    virtual ~Base() {}
+    virtual int f() = 0;
+};
+

-One of the goals of Boost.Python is to be minimally intrusive on an existing C++ -design. In principle, it should be possible to expose the interface for a 3rd -party library without changing it. It is not ideal to add anything to our class -Base. Yet, when you have a virtual function that's going to be overridden in -Python and called polymorphically from C++, we'll need to add some -scaffoldings to make things work properly. What we'll do is write a class -wrapper that derives from Base that will unintrusively hook into the virtual -functions so that a Python override may be called:

-
struct BaseWrap : Base, wrapper<Base>
-{
-    int f()
-    {
-        return this->get_override("f")();
-    }
-};
+ One of the goals of Boost.Python is to be minimally intrusive on an existing + C++ design. In principle, it should be possible to expose the interface for + a 3rd party library without changing it. It is not ideal to add anything + to our class Base. Yet, when + you have a virtual function that's going to be overridden in Python and called + polymorphically from C++, we'll need to + add some scaffoldings to make things work properly. What we'll do is write + a class wrapper that derives from Base + that will unintrusively hook into the virtual functions so that a Python + override may be called: +

+
+struct BaseWrap : Base, wrapper<Base>
+{
+    int f()
+    {
+        return this->get_override("f")();
+    }
+};
+

-Notice too that in addition to inheriting from Base, we also multiply- -inherited wrapper<Base> (See Wrapper). The -wrapper template makes the job of wrapping classes that are meant to -overridden in Python, easier.

+ Notice too that in addition to inheriting from Base, + we also multiply- inherited wrapper<Base> (See Wrapper). + The wrapper template makes + the job of wrapping classes that are meant to overridden in Python, easier. +

+ MSVC6/7 Workaround

If you are using + Microsoft Visual C++ 6 or 7, you have to write f + as:

return call<int>(this->get_override("f").ptr());.
-MSVC6/7 Workaround

- -If you are using Microsoft Visual C++ 6 or 7, you have to write f as:

return call<int>(this->get_override("f").ptr());.

-BaseWrap's overridden virtual member function f in effect calls the -corresponding method of the Python object through get_override.

+ BaseWrap's overridden virtual member function f + in effect calls the corresponding method of the Python object through get_override. +

-Finally, exposing Base:

-
class_<BaseWrap, boost::noncopyable>("Base")
-    .def("f", pure_virtual(&Base::f))
-    ;
-

pure_virtual signals Boost.Python that the function f is a pure virtual -function.

+ Finally, exposing Base: +

+
+class_<BaseWrap, boost::noncopyable>("Base")
+    .def("f", pure_virtual(&Base::f))
+    ;
+
+

pure_virtual signals Boost.Python + that the function f is a + pure virtual function. +

-member function and methods

- Python, like -many object oriented languages uses the term methods. Methods -correspond roughly to C++'s member functions + member function and methods

Python, + like many object oriented languages uses the term methods. + Methods correspond roughly to C++'s member functions
@@ -320,153 +409,196 @@ correspond roughly to C++'s member functions

Virtual Functions with Default Implementations

-We've seen in the previous section how classes with pure virtual functions are -wrapped using Boost.Python's class wrapper -facilities. If we wish to wrap non-pure-virtual functions instead, the -mechanism is a bit different.

+ Virtual Functions with Default ImplementationsWe've seen in the previous + section how classes with pure virtual functions are wrapped using Boost.Python's + class wrapper facilities. If + we wish to wrap non-pure-virtual functions + instead, the mechanism is a bit different. +

-Recall that in the previous section, we -wrapped a class with a pure virtual function that we then implemented in C++, or -Python classes derived from it. Our base class:

-
struct Base
-{
-    virtual int f() = 0;
-};
+ Recall that in the previous + section, we wrapped a class with a pure virtual function that we then + implemented in C++, or Python classes derived from it. Our base class: +

+
+struct Base
+{
+    virtual int f() = 0;
+};
+

-had a pure virtual function f. If, however, its member function f was -not declared as pure virtual:

-
struct Base
-{
-    virtual ~Base() {}
-    virtual int f() { return 0; }
-};
+ had a pure virtual function f. If, however, its member + function f was not declared as pure virtual: +

+
+struct Base
+{
+    virtual ~Base() {}
+    virtual int f() { return 0; }
+};
+

-We wrap it this way:

-
struct BaseWrap : Base, wrapper<Base>
-{
-    int f()
-    {
-        if (override f = this->get_override("f"))
-            return f(); // *note*
-        return Base::f();
-    }
+        We wrap it this way:
+      

+
+struct BaseWrap : Base, wrapper<Base>
+{
+    int f()
+    {
+        if (override f = this->get_override("f"))
+            return f(); // *note*
+        return Base::f();
+    }
 
-    int default_f() { return this->Base::f(); }
-};
+ int default_f() { return this->Base::f(); } +}; +

-Notice how we implemented BaseWrap::f. Now, we have to check if there is an -override for f. If none, then we call Base::f().

+ Notice how we implemented BaseWrap::f. Now, + we have to check if there is an override for f. + If none, then we call Base::f(). +

+ MSVC6/7 Workaround

If you are using + Microsoft Visual C++ 6 or 7, you have to rewrite the line with the + *note* as:

return call<char const*>(f.ptr());.
-MSVC6/7 Workaround

- -If you are using Microsoft Visual C++ 6 or 7, you have to rewrite the line -with the *note* as:

return call<char const*>(f.ptr());.

-Finally, exposing:

-
class_<BaseWrap, boost::noncopyable>("Base")
-    .def("f", &Base::f, &BaseWrap::default_f)
-    ;
+ Finally, exposing: +

+
+class_<BaseWrap, boost::noncopyable>("Base")
+    .def("f", &Base::f, &BaseWrap::default_f)
+    ;
+

-Take note that we expose both &Base::f and &BaseWrap::default_f. -Boost.Python needs to keep track of 1) the dispatch function f and 2) the -forwarding function to its default implementation default_f. There's a -special def function for this purpose.

+ Take note that we expose both &Base::f and &BaseWrap::default_f. Boost.Python needs to keep track + of 1) the dispatch function f and 2) the forwarding function + to its default implementation default_f. There's a special + def function for this purpose. +

-In Python, the results would be as expected:

+ In Python, the results would be as expected: +

-
>>> base = Base()
->>> class Derived(Base):
-...     def f(self):
-...         return 42
-...
->>> derived = Derived()
+
+>>> base = Base()
+>>> class Derived(Base):
+...     def f(self):
+...         return 42
+...
+>>> derived = Derived()
+

-Calling base.f():

-
>>> base.f()
-0
+ Calling base.f(): +

+
+>>> base.f()
+0
+

-Calling derived.f():

-
>>> derived.f()
-42
+ Calling derived.f(): +

+
+>>> derived.f()
+42
+

Class Operators/Special Functions

-

-Python Operators

+

+ + Class Operators/Special FunctionsPython Operators +

-C is well known for the abundance of operators. C++ extends this to the -extremes by allowing operator overloading. Boost.Python takes advantage of -this and makes it easy to wrap C++ operator-powered classes.

+ C is well known for the abundance of operators. C++ extends this to the extremes + by allowing operator overloading. Boost.Python takes advantage of this and + makes it easy to wrap C++ operator-powered classes. +

-Consider a file position class FilePos and a set of operators that take -on FilePos instances:

+ Consider a file position class FilePos and a set of operators + that take on FilePos instances: +

-
class FilePos { /*...*/ };
+
+class FilePos { /*...*/ };
 
-FilePos     operator+(FilePos, int);
-FilePos     operator+(int, FilePos);
-int         operator-(FilePos, FilePos);
-FilePos     operator-(FilePos, int);
-FilePos&    operator+=(FilePos&, int);
-FilePos&    operator-=(FilePos&, int);
-bool        operator<(FilePos, FilePos);
+FilePos operator+(FilePos, int); +FilePos operator+(int, FilePos); +int operator-(FilePos, FilePos); +FilePos operator-(FilePos, int); +FilePos& operator+=(FilePos&, int); +FilePos& operator-=(FilePos&, int); +bool operator<(FilePos, FilePos); +

-The class and the various operators can be mapped to Python rather easily -and intuitively:

-
class_<FilePos>("FilePos")
-    .def(self + int())          // __add__
-    .def(int() + self)          // __radd__
-    .def(self - self)           // __sub__
-    .def(self - int())          // __sub__
-    .def(self += int())         // __iadd__
-    .def(self -= other<int>())
-    .def(self < self);          // __lt__
-
+ The class and the various operators can be mapped to Python rather easily + and intuitively: +

+
+class_<FilePos>("FilePos")
+    .def(self + int())          // __add__
+    .def(int() + self)          // __radd__
+    .def(self - self)           // __sub__
+    .def(self - int())          // __sub__
+    .def(self += int())         // __iadd__
+    .def(self -= other<int>())
+    .def(self < self);          // __lt__
+

-The code snippet above is very clear and needs almost no explanation at -all. It is virtually the same as the operators' signatures. Just take -note that self refers to FilePos object. Also, not every class T that -you might need to interact with in an operator expression is (cheaply) -default-constructible. You can use other<T>() in place of an actual -T instance when writing "self expressions".

+ The code snippet above is very clear and needs almost no explanation at all. + It is virtually the same as the operators' signatures. Just take note that + self refers to FilePos object. Also, not every class + T that you might need to interact with in an operator + expression is (cheaply) default-constructible. You can use other<T>() + in place of an actual T instance when writing "self + expressions". +

-Special Methods

+ + Special Methods +

-Python has a few more Special Methods. Boost.Python supports all of the -standard special method names supported by real Python class instances. A -similar set of intuitive interfaces can also be used to wrap C++ functions -that correspond to these Python special functions. Example:

-
class Rational
-{ public: operator double() const; };
+        Python has a few more Special Methods. Boost.Python
+        supports all of the standard special method names supported by real Python
+        class instances. A similar set of intuitive interfaces can also be used to
+        wrap C++ functions that correspond to these Python special functions.
+        Example:
+      

+
+class Rational
+{ public: operator double() const; };
 
-Rational pow(Rational, Rational);
-Rational abs(Rational);
-ostream& operator<<(ostream&,Rational);
+Rational pow(Rational, Rational);
+Rational abs(Rational);
+ostream& operator<<(ostream&,Rational);
 
-class_<Rational>("Rational")
-    .def(float_(self))                  // __float__
-    .def(pow(self, other<Rational>))    // __pow__
-    .def(abs(self))                     // __abs__
-    .def(str(self))                     // __str__
-    ;
+class_<Rational>("Rational") + .def(float_(self)) // __float__ + .def(pow(self, other<Rational>)) // __pow__ + .def(abs(self)) // __abs__ + .def(str(self)) // __str__ + ; +

-Need we say more?

+ Need we say more? +

+ + What is the business of operator<<? Well, the method str requires the operator<< to do its work (i.e. operator<< + is used by the method defined by def(str(self)).
- What is the business of operator<<? -Well, the method str requires the operator<< to do its work (i.e. -operator<< is used by the method defined by def(str(self)).
- +
Copyright © 2002-2005 Joel de Guzman, David AbrahamsCopyright © 2002-2005 Joel + de Guzman, David Abrahams

diff --git a/doc/tutorial/doc/html/python/functions.html b/doc/tutorial/doc/html/python/functions.html index 82eed97c..7070ccfe 100644 --- a/doc/tutorial/doc/html/python/functions.html +++ b/doc/tutorial/doc/html/python/functions.html @@ -32,17 +32,18 @@
Auto-Overloading

-In this chapter, we'll look at Boost.Python powered functions in closer -detail. We shall see some facilities to make exposing C++ functions to -Python safe from potential pifalls such as dangling pointers and -references. We shall also see facilities that will make it even easier for -us to expose C++ functions that take advantage of C++ features such as -overloading and default arguments.

+ FunctionsIn this chapter, we'll look at Boost.Python powered functions in closer + detail. We shall see some facilities to make exposing C++ functions to Python + safe from potential pifalls such as dangling pointers and references. We shall + also see facilities that will make it even easier for us to expose C++ functions + that take advantage of C++ features such as overloading and default arguments. +

Read on...

-But before you do, you might want to fire up Python 2.2 or later and type ->>> import this.

-
>>> import this
+      But before you do, you might want to fire up Python 2.2 or later and type
+      >>> import this.
+    

+
>>> import this
 The Zen of Python, by Tim Peters
 Beautiful is better than ugly.
 Explicit is better than implicit.
@@ -63,187 +64,231 @@ Although never is often better than right now.
 If the implementation is hard to explain, it's a bad idea.
 If the implementation is easy to explain, it may be a good idea.
 Namespaces are one honking great idea -- let's do more of those!
-
+

Call Policies

-In C++, we often deal with arguments and return types such as pointers -and references. Such primitive types are rather, ummmm, low level and -they really don't tell us much. At the very least, we don't know the -owner of the pointer or the referenced object. No wonder languages -such as Java and Python never deal with such low level entities. In -C++, it's usually considered a good practice to use smart pointers -which exactly describe ownership semantics. Still, even good C++ -interfaces use raw references and pointers sometimes, so Boost.Python -must deal with them. To do this, it may need your help. Consider the -following C++ function:

-
X& f(Y& y, Z* z);
+ Call PoliciesIn C++, we often deal with arguments and return types such as + pointers and references. Such primitive types are rather, ummmm, low level + and they really don't tell us much. At the very least, we don't know the + owner of the pointer or the referenced object. No wonder languages such as + Java and Python never deal with such low level entities. In C++, it's usually + considered a good practice to use smart pointers which exactly describe ownership + semantics. Still, even good C++ interfaces use raw references and pointers + sometimes, so Boost.Python must deal with them. To do this, it may need your + help. Consider the following C++ function: +

+
+X& f(Y& y, Z* z);
+

-How should the library wrap this function? A naive approach builds a -Python X object around result reference. This strategy might or might -not work out. Here's an example where it didn't

-
>>> x = f(y, z) # x refers to some C++ X
->>> del y
->>> x.some_method() # CRASH!
+ How should the library wrap this function? A naive approach builds a Python + X object around result reference. This strategy might or might not work out. + Here's an example where it didn't +

+
+>>> x = f(y, z) # x refers to some C++ X
+>>> del y
+>>> x.some_method() # CRASH!
+

-What's the problem?

+ What's the problem? +

-Well, what if f() was implemented as shown below:

-
X& f(Y& y, Z* z)
-{
-    y.z = z;
-    return y.x;
-}
+ Well, what if f() was implemented as shown below: +

+
+X& f(Y& y, Z* z)
+{
+    y.z = z;
+    return y.x;
+}
+

-The problem is that the lifetime of result X& is tied to the lifetime -of y, because the f() returns a reference to a member of the y -object. This idiom is is not uncommon and perfectly acceptable in the -context of C++. However, Python users should not be able to crash the -system just by using our C++ interface. In this case deleting y will -invalidate the reference to X. We have a dangling reference.

+ The problem is that the lifetime of result X& is tied to the lifetime + of y, because the f() returns a reference to a member of the y object. This + idiom is is not uncommon and perfectly acceptable in the context of C++. + However, Python users should not be able to crash the system just by using + our C++ interface. In this case deleting y will invalidate the reference + to X. We have a dangling reference. +

-Here's what's happening:

+ Here's what's happening: +

  1. -f is called passing in a reference to y and a pointer to z +f is called passing in a reference to y + and a pointer to z
  2. -A reference to y.x is returned -
  3. + A reference to y.x is returned +
  4. y is deleted. x is a dangling reference -
  5. +
  6. x.some_method() is called -
  7. +
  8. BOOM!

-We could copy result into a new object:

+ We could copy result into a new object: +

-
>>> f(y, z).set(42) # Result disappears
->>> y.x.get()       # No crash, but still bad
-3.14
+
+>>> f(y, z).set(42) # Result disappears
+>>> y.x.get()       # No crash, but still bad
+3.14
+

-This is not really our intent of our C++ interface. We've broken our -promise that the Python interface should reflect the C++ interface as -closely as possible.

+ This is not really our intent of our C++ interface. We've broken our promise + that the Python interface should reflect the C++ interface as closely as + possible. +

-Our problems do not end there. Suppose Y is implemented as follows:

+ Our problems do not end there. Suppose Y is implemented as follows: +

-
struct Y
-{
-    X x; Z* z;
-    int z_value() { return z->value(); }
-};
+
+struct Y
+{
+    X x; Z* z;
+    int z_value() { return z->value(); }
+};
+

-Notice that the data member z is held by class Y using a raw -pointer. Now we have a potential dangling pointer problem inside Y:

-
>>> x = f(y, z) # y refers to z
->>> del z       # Kill the z object
->>> y.z_value() # CRASH!
+ Notice that the data member z is held by class Y using + a raw pointer. Now we have a potential dangling pointer problem inside Y: +

+
+>>> x = f(y, z) # y refers to z
+>>> del z       # Kill the z object
+>>> y.z_value() # CRASH!
+

-For reference, here's the implementation of f again:

-
X& f(Y& y, Z* z)
-{
-    y.z = z;
-    return y.x;
-}
+ For reference, here's the implementation of f again: +

+
+X& f(Y& y, Z* z)
+{
+    y.z = z;
+    return y.x;
+}
+

-Here's what's happening:

+ Here's what's happening: +

  1. -f is called passing in a reference to y and a pointer to z +f is called passing in a reference to y + and a pointer to z
  2. -A pointer to z is held by y + A pointer to z is held by y
  3. -A reference to y.x is returned -
  4. + A reference to y.x is returned +
  5. z is deleted. y.z is a dangling pointer -
  6. +
  7. y.z_value() is called -
  8. +
  9. z->value() is called -
  10. +
  11. BOOM!

-Call Policies

+ + Call Policies +

-Call Policies may be used in situations such as the example detailed above. -In our example, return_internal_reference and with_custodian_and_ward -are our friends:

-
def("f", f,
-    return_internal_reference<1,
-        with_custodian_and_ward<1, 2> >());
+ Call Policies may be used in situations such as the example detailed above. + In our example, return_internal_reference and with_custodian_and_ward + are our friends: +

+
+def("f", f,
+    return_internal_reference<1,
+        with_custodian_and_ward<1, 2> >());
+

-What are the 1 and 2 parameters, you ask?

-
return_internal_reference<1
+ What are the 1 and 2 parameters, you + ask? +

+
+return_internal_reference<1
+

-Informs Boost.Python that the first argument, in our case Y& y, is the -owner of the returned reference: X&. The "1" simply specifies the -first argument. In short: "return an internal reference X& owned by the -1st argument Y& y".

-
with_custodian_and_ward<1, 2>
+ Informs Boost.Python that the first argument, in our case Y& + y, is the owner of the returned reference: X&. + The "1" simply specifies the first argument. + In short: "return an internal reference X& owned + by the 1st argument Y& y". +

+
+with_custodian_and_ward<1, 2>
+

-Informs Boost.Python that the lifetime of the argument indicated by ward -(i.e. the 2nd argument: Z* z) is dependent on the lifetime of the -argument indicated by custodian (i.e. the 1st argument: Y& y).

+ Informs Boost.Python that the lifetime of the argument indicated by ward + (i.e. the 2nd argument: Z* z) is dependent on the lifetime + of the argument indicated by custodian (i.e. the 1st argument: Y& + y). +

-It is also important to note that we have defined two policies above. Two -or more policies can be composed by chaining. Here's the general syntax:

-
policy1<args...,
-    policy2<args...,
-        policy3<args...> > >
+ It is also important to note that we have defined two policies above. Two + or more policies can be composed by chaining. Here's the general syntax: +

+
+policy1<args...,
+    policy2<args...,
+        policy3<args...> > >
+

-Here is the list of predefined call policies. A complete reference detailing -these can be found here.

+ Here is the list of predefined call policies. A complete reference detailing + these can be found here. +

  • -with_custodian_and_ward
    - Ties lifetimes of the arguments -
  • +with_custodian_and_ward
    Ties lifetimes + of the arguments +
  • with_custodian_and_ward_postcall
    - Ties lifetimes of the arguments and results -
  • + Ties lifetimes of the arguments and results +
  • -return_internal_reference
    - Ties lifetime of one argument to that of result -
  • +return_internal_reference
    Ties lifetime + of one argument to that of result +
  • return_value_policy<T> with T one of:
  • -reference_existing_object
    -naive (dangerous) approach -
  • +reference_existing_object
    naive + (dangerous) approach +
  • -copy_const_reference
    -Boost.Python v1 approach -
  • +copy_const_reference
    Boost.Python + v1 approach +
  • copy_non_const_reference
  • -manage_new_object
    - Adopt a pointer and hold the instance -
  • +manage_new_object
    Adopt a pointer + and hold the instance +
-Remember the Zen, Luke:

- -"Explicit is better than implicit"
- -"In the face of ambiguity, refuse the temptation to guess"
+ Remember the Zen, Luke:

"Explicit + is better than implicit"
"In the face of ambiguity, + refuse the temptation to guess"
@@ -251,226 +296,303 @@ Boost.Python v1 approach

Overloading

-The following illustrates a scheme for manually wrapping an overloaded -member functions. Of course, the same technique can be applied to wrapping -overloaded non-member functions.

+ OverloadingThe following illustrates a scheme for manually wrapping an overloaded + member functions. Of course, the same technique can be applied to wrapping + overloaded non-member functions. +

-We have here our C++ class:

-
struct X
-{
-    bool f(int a)
-    {
-        return true;
-    }
+        We have here our C++ class:
+      

+
+struct X
+{
+    bool f(int a)
+    {
+        return true;
+    }
 
-    bool f(int a, double b)
-    {
-        return true;
-    }
+    bool f(int a, double b)
+    {
+        return true;
+    }
 
-    bool f(int a, double b, char c)
-    {
-        return true;
-    }
+    bool f(int a, double b, char c)
+    {
+        return true;
+    }
 
-    int f(int a, int b, int c)
-    {
-        return a + b + c;
-    };
-};
+ int f(int a, int b, int c) + { + return a + b + c; + }; +}; +

-Class X has 4 overloaded functions. We shall start by introducing some -member function pointer variables:

-
bool    (X::*fx1)(int)              = &X::f;
-bool    (X::*fx2)(int, double)      = &X::f;
-bool    (X::*fx3)(int, double, char)= &X::f;
-int     (X::*fx4)(int, int, int)    = &X::f;
+ Class X has 4 overloaded functions. We shall start by introducing some member + function pointer variables: +

+
+bool    (X::*fx1)(int)              = &X::f;
+bool    (X::*fx2)(int, double)      = &X::f;
+bool    (X::*fx3)(int, double, char)= &X::f;
+int     (X::*fx4)(int, int, int)    = &X::f;
+

-With these in hand, we can proceed to define and wrap this for Python:

-
.def("f", fx1)
-.def("f", fx2)
-.def("f", fx3)
-.def("f", fx4)
+ With these in hand, we can proceed to define and wrap this for Python: +

+
+.def("f", fx1)
+.def("f", fx2)
+.def("f", fx3)
+.def("f", fx4)
+

Default Arguments

-Boost.Python wraps (member) function pointers. Unfortunately, C++ function -pointers carry no default argument info. Take a function f with default -arguments:

-
int f(int, double = 3.14, char const* = "hello");
+ Default ArgumentsBoost.Python wraps (member) function pointers. Unfortunately, + C++ function pointers carry no default argument info. Take a function f + with default arguments: +

+
+int f(int, double = 3.14, char const* = "hello");
+

-But the type of a pointer to the function f has no information -about its default arguments:

-
int(*g)(int,double,char const*) = f;    // defaults lost!
-
+ But the type of a pointer to the function f has no information + about its default arguments: +

+
+int(*g)(int,double,char const*) = f;    // defaults lost!
+

-When we pass this function pointer to the def function, there is no way -to retrieve the default arguments:

-
def("f", f);                            // defaults lost!
-
+ When we pass this function pointer to the def function, + there is no way to retrieve the default arguments: +

+
+def("f", f);                            // defaults lost!
+

-Because of this, when wrapping C++ code, we had to resort to manual -wrapping as outlined in the previous section, or -writing thin wrappers:

-
// write "thin wrappers"
-int f1(int x) { f(x); }
-int f2(int x, double y) { f(x,y); }
+        Because of this, when wrapping C++ code, we had to resort to manual wrapping
+        as outlined in the previous section,
+        or writing thin wrappers:
+      

+
+// write "thin wrappers"
+int f1(int x) { f(x); }
+int f2(int x, double y) { f(x,y); }
 
-/*...*/
+/*...*/
 
-    // in module init
-    def("f", f);  // all arguments
-    def("f", f2); // two arguments
-    def("f", f1); // one argument
-
+ // in module init + def("f", f); // all arguments + def("f", f2); // two arguments + def("f", f1); // one argument +

-When you want to wrap functions (or member functions) that either:

+ When you want to wrap functions (or member functions) that either: +

  • -have default arguments, or -
  • + have default arguments, or +
  • -are overloaded with a common sequence of initial arguments -
  • + are overloaded with a common sequence of initial arguments +

-BOOST_PYTHON_FUNCTION_OVERLOADS

+ + BOOST_PYTHON_FUNCTION_OVERLOADS +

-Boost.Python now has a way to make it easier. For instance, given a function:

-
int foo(int a, char b = 1, unsigned c = 2, double d = 3)
-{
-    /*...*/
-}
+ Boost.Python now has a way to make it easier. For instance, given a function: +

+
+int foo(int a, char b = 1, unsigned c = 2, double d = 3)
+{
+    /*...*/
+}
+

-The macro invocation:

-
BOOST_PYTHON_FUNCTION_OVERLOADS(foo_overloads, foo, 1, 4)
+ The macro invocation: +

+
+BOOST_PYTHON_FUNCTION_OVERLOADS(foo_overloads, foo, 1, 4)
+

-will automatically create the thin wrappers for us. This macro will create -a class foo_overloads that can be passed on to def(...). The third -and fourth macro argument are the minimum arguments and maximum arguments, -respectively. In our foo function the minimum number of arguments is 1 -and the maximum number of arguments is 4. The def(...) function will -automatically add all the foo variants for us:

-
def("foo", foo, foo_overloads());
+ will automatically create the thin wrappers for us. This macro will create + a class foo_overloads that can be passed on to def(...). + The third and fourth macro argument are the minimum arguments and maximum + arguments, respectively. In our foo function the minimum + number of arguments is 1 and the maximum number of arguments is 4. The def(...) + function will automatically add all the foo variants for us: +

+
+def("foo", foo, foo_overloads());
+

-BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS

+ + BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS +

-Objects here, objects there, objects here there everywhere. More frequently -than anything else, we need to expose member functions of our classes to -Python. Then again, we have the same inconveniences as before when default -arguments or overloads with a common sequence of initial arguments come -into play. Another macro is provided to make this a breeze.

+ Objects here, objects there, objects here there everywhere. More frequently + than anything else, we need to expose member functions of our classes to + Python. Then again, we have the same inconveniences as before when default + arguments or overloads with a common sequence of initial arguments come into + play. Another macro is provided to make this a breeze. +

-Like BOOST_PYTHON_FUNCTION_OVERLOADS, -BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS may be used to automatically create -the thin wrappers for wrapping member functions. Let's have an example:

-
struct george
-{
-    void
-    wack_em(int a, int b = 0, char c = 'x')
-    {
-        /*...*/
-    }
-};
+ Like BOOST_PYTHON_FUNCTION_OVERLOADS, BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS + may be used to automatically create the thin wrappers for wrapping member + functions. Let's have an example: +

+
+struct george
+{
+    void
+    wack_em(int a, int b = 0, char c = 'x')
+    {
+        /*...*/
+    }
+};
+

-The macro invocation:

-
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(george_overloads, wack_em, 1, 3)
+ The macro invocation: +

+
+BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(george_overloads, wack_em, 1, 3)
+

-will generate a set of thin wrappers for george's wack_em member function -accepting a minimum of 1 and a maximum of 3 arguments (i.e. the third and -fourth macro argument). The thin wrappers are all enclosed in a class named -george_overloads that can then be used as an argument to def(...):

-
.def("wack_em", &george::wack_em, george_overloads());
+ will generate a set of thin wrappers for george's wack_em + member function accepting a minimum of 1 and a maximum of 3 arguments (i.e. + the third and fourth macro argument). The thin wrappers are all enclosed + in a class named george_overloads that can then be used + as an argument to def(...): +

+
+.def("wack_em", &george::wack_em, george_overloads());
+

-See the overloads reference -for details.

+ See the overloads + reference for details. +

-init and optional

+ + init and optional +

-A similar facility is provided for class constructors, again, with -default arguments or a sequence of overloads. Remember init<...>? For example, -given a class X with a constructor:

-
struct X
-{
-    X(int a, char b = 'D', std::string c = "constructor", double d = 0.0);
-    /*...*/
-}
+ A similar facility is provided for class constructors, again, with default + arguments or a sequence of overloads. Remember init<...>? + For example, given a class X with a constructor: +

+
+struct X
+{
+    X(int a, char b = 'D', std::string c = "constructor", double d = 0.0);
+    /*...*/
+}
+

-You can easily add this constructor to Boost.Python in one shot:

-
.def(init<int, optional<char, std::string, double> >())
+ You can easily add this constructor to Boost.Python in one shot: +

+
+.def(init<int, optional<char, std::string, double> >())
+

-Notice the use of init<...> and optional<...> to signify the default -(optional arguments).

+ Notice the use of init<...> and optional<...> + to signify the default (optional arguments). +

Auto-Overloading

-It was mentioned in passing in the previous section that -BOOST_PYTHON_FUNCTION_OVERLOADS and BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS -can also be used for overloaded functions and member functions with a -common sequence of initial arguments. Here is an example:

-
void foo()
-{
-   /*...*/
-}
+        Auto-OverloadingIt was mentioned in passing in the previous section that
+        BOOST_PYTHON_FUNCTION_OVERLOADS and BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS
+        can also be used for overloaded functions and member functions with a common
+        sequence of initial arguments. Here is an example:
+      

+
+void foo()
+{
+   /*...*/
+}
 
-void foo(bool a)
-{
-   /*...*/
-}
+void foo(bool a)
+{
+   /*...*/
+}
 
-void foo(bool a, int b)
-{
-   /*...*/
-}
+void foo(bool a, int b)
+{
+   /*...*/
+}
 
-void foo(bool a, int b, char c)
-{
-   /*...*/
-}
+void foo(bool a, int b, char c) +{ + /*...*/ +} +

-Like in the previous section, we can generate thin wrappers for these -overloaded functions in one-shot:

-
BOOST_PYTHON_FUNCTION_OVERLOADS(foo_overloads, foo, 0, 3)
+ Like in the previous section, we can generate thin wrappers for these overloaded + functions in one-shot: +

+
+BOOST_PYTHON_FUNCTION_OVERLOADS(foo_overloads, foo, 0, 3)
+

-Then...

-
.def("foo", foo, foo_overloads());
+ Then... +

+
+.def("foo", foo, foo_overloads());
+

-Notice though that we have a situation now where we have a minimum of zero -(0) arguments and a maximum of 3 arguments.

+ Notice though that we have a situation now where we have a minimum of zero + (0) arguments and a maximum of 3 arguments. +

-Manual Wrapping

+ + Manual Wrapping +

-It is important to emphasize however that the overloaded functions must -have a common sequence of initial arguments. Otherwise, our scheme above -will not work. If this is not the case, we have to wrap our functions -manually.

+ It is important to emphasize however that the overloaded + functions must have a common sequence of initial arguments. Otherwise, + our scheme above will not work. If this is not the case, we have to wrap + our functions manually. +

-Actually, we can mix and match manual wrapping of overloaded functions and -automatic wrapping through BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS and -its sister, BOOST_PYTHON_FUNCTION_OVERLOADS. Following up on our example -presented in the section on overloading, since the -first 4 overload functins have a common sequence of initial arguments, we -can use BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS to automatically wrap the -first three of the defs and manually wrap just the last. Here's -how we'll do this:

-
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(xf_overloads, f, 1, 4)
+ Actually, we can mix and match manual wrapping of overloaded functions and + automatic wrapping through BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS + and its sister, BOOST_PYTHON_FUNCTION_OVERLOADS. Following + up on our example presented in the section on + overloading, since the first 4 overload functins have a common sequence + of initial arguments, we can use BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS + to automatically wrap the first three of the defs and + manually wrap just the last. Here's how we'll do this: +

+
+BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(xf_overloads, f, 1, 4)
+

-Create a member function pointers as above for both X::f overloads:

-
bool    (X::*fx1)(int, double, char)    = &X::f;
-int     (X::*fx2)(int, int, int)        = &X::f;
+ Create a member function pointers as above for both X::f overloads: +

+
+bool    (X::*fx1)(int, double, char)    = &X::f;
+int     (X::*fx2)(int, int, int)        = &X::f;
+

-Then...

-
.def("f", fx1, xf_overloads());
-.def("f", fx2)
+ Then... +

+
+.def("f", fx1, xf_overloads());
+.def("f", fx2)
+
- +
Copyright © 2002-2005 Joel de Guzman, David AbrahamsCopyright © 2002-2005 Joel + de Guzman, David Abrahams

diff --git a/doc/tutorial/doc/html/python/hello.html b/doc/tutorial/doc/html/python/hello.html index 8413e3b6..6d648bf9 100644 --- a/doc/tutorial/doc/html/python/hello.html +++ b/doc/tutorial/doc/html/python/hello.html @@ -25,79 +25,88 @@

Building Hello World

-

-From Start To Finish

+

+ + Building Hello WorldFrom Start To Finish +

-Now the first thing you'd want to do is to build the Hello World module and -try it for yourself in Python. In this section, we shall outline the steps -necessary to achieve that. We shall use the build tool that comes bundled -with every boost distribution: bjam.

+ Now the first thing you'd want to do is to build the Hello World module and + try it for yourself in Python. In this section, we shall outline the steps + necessary to achieve that. We shall use the build tool that comes bundled with + every boost distribution: bjam. +

+ Building without bjam

Besides bjam, + there are of course other ways to get your module built. What's written + here should not be taken as "the one and only way". There are + of course other build tools apart from bjam.

Take note however that the preferred build tool for Boost.Python + is bjam. There are so many ways to set up the build incorrectly. Experience + shows that 90% of the "I can't build Boost.Python" problems + come from people who had to use a different tool.
-Building without bjam

- - Besides bjam, there are of course other ways to get your module built. - What's written here should not be taken as "the one and only way". - There are of course other build tools apart from bjam.

- - Take note however that the preferred build tool for Boost.Python is bjam. - There are so many ways to set up the build incorrectly. Experience shows - that 90% of the "I can't build Boost.Python" problems come from people - who had to use a different tool. -

-We shall skip over the details. Our objective will be to simply create the -hello world module and run it in Python. For a complete reference to -building Boost.Python, check out: building.html. -After this brief bjam tutorial, we should have built two DLLs:

+ We shall skip over the details. Our objective will be to simply create the + hello world module and run it in Python. For a complete reference to building + Boost.Python, check out: building.html. + After this brief bjam tutorial, we should have built two + DLLs: +

  • -boost_python.dll -
  • + boost_python.dll +
  • -hello.pyd -
  • + hello.pyd +

-if you are on Windows, and

+ if you are on Windows, and +

  • -libboost_python.so -
  • + libboost_python.so +
  • -hello.so -
  • + hello.so +

-if you are on Unix.

+ if you are on Unix. +

-The tutorial example can be found in the directory: -libs/python/example/tutorial. There, you can find:

+ The tutorial example can be found in the directory: libs/python/example/tutorial. + There, you can find: +

  • -hello.cpp -
  • + hello.cpp +
  • -Jamfile -
  • + Jamfile +

-The hello.cpp file is our C++ hello world example. The Jamfile is a -minimalist bjam script that builds the DLLs for us.

+ The hello.cpp file is our C++ hello world example. The + Jamfile is a minimalist bjam script + that builds the DLLs for us. +

-Before anything else, you should have the bjam executable in your boost -directory or somewhere in your path such that bjam can be executed in -the command line. Pre-built Boost.Jam executables are available for most -platforms. The complete list of Bjam executables can be found -here.

+ Before anything else, you should have the bjam executable in your boost directory + or somewhere in your path such that bjam can be executed + in the command line. Pre-built Boost.Jam executables are available for most + platforms. The complete list of Bjam executables can be found here. +

-Let's Jam!

+ + Let's Jam! +

-Here is our minimalist Jamfile:

-
# This is the top of our own project tree
+      Here is our minimalist Jamfile:
+    

+
# This is the top of our own project tree
 project-root ;
 
 import python ;
@@ -107,85 +116,106 @@ extension hello                     # Declare a Python extension called hello
     # requirements and dependencies for Boost.Python extensions
     <template>@boost/libs/python/build/extension
     ;
-
+

-First, we need to specify our location. You may place your project anywhere. -project-root allows you to do that.

-
project-root ;
-
+ First, we need to specify our location. You may place your project anywhere. + project-root allows you to do that. +

+
project-root ;
+

-By doing so, you'll need a Jamrules file. Simply copy the one in the -example/tutorial directory and tweak -the path-global BOOST_ROOT to where your boost root directory is. The file -has detailed instructions you can follow.

+ By doing so, you'll need a Jamrules file. Simply copy the one in the example/tutorial directory + and tweak the path-global BOOST_ROOT to where your boost + root directory is. The file has detailed + instructions you can follow. +

-Then we will import the definitions needed by Python modules:

-
import python ;
-
+ Then we will import the definitions needed by Python modules: +

+
import python ;
+

-Finally we declare our hello extension:

-
extension hello                     # Declare a Python extension called hello
+      Finally we declare our hello extension:
+    

+
extension hello                     # Declare a Python extension called hello
 :   hello.cpp                       # source
 
     # requirements and dependencies for Boost.Python extensions
     <template>@boost/libs/python/build/extension
     ;
-
+

-The last part tells BJam that we are depending on the Boost Python Library.

+ The last part tells BJam that we are depending on the Boost Python Library. +

-Running bjam

-

bjam is run using your operating system's command line interpreter.

-

Start it up.

+ + Running bjam + +

bjam is run using your operating system's command line + interpreter. +

+

+ Start it up. +

-Make sure that the environment is set so that we can invoke the C++ -compiler. With MSVC, that would mean running the Vcvars32.bat batch -file. For instance:

-
C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\vsvars32.bat
-
+ Make sure that the environment is set so that we can invoke the C++ compiler. + With MSVC, that would mean running the Vcvars32.bat batch + file. For instance: +

+
C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\vsvars32.bat
+

-Some environment variables will have to be setup for proper building of our -Python modules. Example:

-
set PYTHON_ROOT=c:/dev/tools/python
+      Some environment variables will have to be setup for proper building of our
+      Python modules. Example:
+    

+
set PYTHON_ROOT=c:/dev/tools/python
 set PYTHON_VERSION=2.2
-
+

-The above assumes that the Python installation is in c:/dev/tools/python -and that we are using Python version 2.2. You'll have to tweak these -appropriately.

+ The above assumes that the Python installation is in c:/dev/tools/python + and that we are using Python version 2.2. You'll have to tweak these appropriately. +

+ + Be sure not to include a third number, e.g. not + "2.2.1", even if that's the version you have.
- Be sure not to include a third number, e.g. not "2.2.1", -even if that's the version you have.

-Take note that you may also do that through the Jamrules file we put in -our project as detailed above. The file -has detailed instructions you -can follow.

+ Take note that you may also do that through the Jamrules file we put in our + project as detailed above. The file has detailed + instructions you can follow. +

-Now we are ready... Be sure to cd to libs/python/example/tutorial -where the tutorial "hello.cpp" and the "Jamfile" is situated.

+ Now we are ready... Be sure to cd to libs/python/example/tutorial + where the tutorial "hello.cpp" and the "Jamfile" + is situated. +

-Finally:

-
bjam -sTOOLS=vc-7_1
+ Finally: +

+
+bjam -sTOOLS=vc-7_1
+

-We are again assuming that we are using Microsoft Visual C++ version 7.1. If -not, then you will have to specify the appropriate tool. See -Building Boost Libraries for -further details.

+ We are again assuming that we are using Microsoft Visual C++ version 7.1. If + not, then you will have to specify the appropriate tool. See Building + Boost Libraries for further details. +

-It should be building now:

-
cd C:\dev\boost\libs\python\example\tutorial
+      It should be building now:
+    

+
cd C:\dev\boost\libs\python\example\tutorial
 bjam -sTOOLS=msvc
 ...patience...
 ...found 1703 targets...
 ...updating 40 targets...
-
+

-And so on... Finally:

-
Creating library bin\boost\libs\python\build\boost_python.dll\vc-7_1\debug\th
+      And so on... Finally:
+    

+
Creating library bin\boost\libs\python\build\boost_python.dll\vc-7_1\debug\th
 reading-multi\boost_python.lib and object bin\boost\libs\python\build\boost_pyth
 on.dll\vc-7_1\debug\threading-multi\boost_python.exp
 vc-C++ bin\tutorial\hello.pyd\vc-7_1\debug\threading-multi\hello.obj
@@ -195,45 +225,54 @@ al\hello.pyd\vc-7_1\debug\threading-multi\hello.lib
    Creating library bin\tutorial\hello.pyd\vc-7_1\debug\threading-multi\hello.li
 b and object bin\tutorial\hello.pyd\vc-7_1\debug\threading-multi\hello.exp
 ...updated 31 targets...
-
+

-If all is well, you should now have:

+ If all is well, you should now have: +

  • -boost_python.dll -
  • + boost_python.dll +
  • -hello.pyd -
  • + hello.pyd +

-if you are on Windows, and

+ if you are on Windows, and +

  • -libboost_python.so -
  • + libboost_python.so +
  • -hello.so -
  • + hello.so +

-if you are on Unix.

-

boost_python.dll and hello.pyd can be found somewhere in your project's -bin directory. After a successful build, you can just link in these DLLs with -the Python interpreter. In Windows for example, you can simply put these libraries -inside the directory where the Python executable is.

+ if you are on Unix. +

+

boost_python.dll and hello.pyd can be + found somewhere in your project's bin directory. After a + successful build, you can just link in these DLLs with the Python interpreter. + In Windows for example, you can simply put these libraries inside the directory + where the Python executable is. +

-You may now fire up Python and run our hello module:

+ You may now fire up Python and run our hello module: +

-
>>> import hello
->>> print hello.greet()
-hello, world
+
+>>> import hello
+>>> print hello.greet()
+hello, world
+

There you go... Have fun!

- +
Copyright © 2002-2005 Joel de Guzman, David AbrahamsCopyright © 2002-2005 Joel + de Guzman, David Abrahams

diff --git a/doc/tutorial/doc/html/python/iterators.html b/doc/tutorial/doc/html/python/iterators.html index 08a1df62..022620e1 100644 --- a/doc/tutorial/doc/html/python/iterators.html +++ b/doc/tutorial/doc/html/python/iterators.html @@ -26,104 +26,156 @@

Iterators

-In C++, and STL in particular, we see iterators everywhere. Python also has -iterators, but these are two very different beasts.

+ IteratorsIn 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) -
  • + C++ has 5 type categories (random-access, bidirectional, forward, input, + output) +
  • -There are 2 Operation categories: reposition, access -
  • + There are 2 Operation categories: reposition, access +
  • -A pair of iterators is needed to represent a (first/last) range. -
  • + A pair of iterators is needed to represent a (first/last) range. +

Python Iterators:

  • -1 category (forward) -
  • + 1 category (forward) +
  • -1 operation category (next()) -
  • + 1 operation category (next()) +
  • -Raises StopIteration exception at end -
  • + Raises StopIteration exception at end +

-The typical Python iteration protocol: for y in x... is as follows:

+ 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
-
+
+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:

+ 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();
+
+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> >())
+ Or for use in class_<>: +

+
+.def("__iter__", iterator<vector<int> >())
+

range

-We can create a Python savvy iterator using the range function:

+ We can create a Python savvy iterator using the range function: +

  • -range(start, finish) -
  • + range(start, finish) +
  • -range<Policies,Target>(start, finish) -
  • + range<Policies,Target>(start, finish) +

-Here, start/finish may be one of:

+ Here, start/finish may be one of: +

  • -member data pointers -
  • + member data pointers +
  • -member function pointers -
  • + member function pointers +
  • -adaptable function object (use Target parameter) -
  • + adaptable function object (use Target parameter) +

iterator

  • -iterator<T, Policies>() -
+ iterator<T, Policies>() +

-Given a container T, iterator is a shortcut that simply calls range -with &T::begin, &T::end.

+ 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:

+ 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)
+
+f = Field()
+for x in f.pions:
+    smash(x)
+for y in f.bogons:
+    count(y)
+

-Now, our C++ Wrapper:

+ 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));
+
+class_<F>("Field")
+    .property("pions", range(&F::p_begin, &F::p_end))
+    .property("bogons", range(&F::b_begin, &F::b_end));
+
+

stl_input_iterator

+

+ So far, we have seen how to expose C++ iterators and ranges to Python. Sometimes + we wish to go the other way, though: we'd like to pass a Python sequence to + an STL algorithm or use it to initialize an STL container. We need to make + a Python iterator look like an STL iterator. For that, we use stl_input_iterator<>. + Consider how we might implement a function that exposes std::list<int>::assign() to Python: +

+

+
+template<typename T>
+void list_assign(std::list<T>& l, object o) {
+    // Turn a Python sequence into an STL input range
+    stl_input_iterator<T> begin(o), end;
+    l.assign(begin, end);
+}
+
+// Part of the wrapper for list<int>
+class_<std::list<int> >("list_int")
+    .def("assign", &list_assign<int>)
+    // ...
+    ;
+
+

+ Now in Python, we can assign any integer sequence to list_int + objects: +

+

+
+x = list_int();
+x.assign([1,2,3,4,5])
+
- +
Copyright © 2002-2005 Joel de Guzman, David AbrahamsCopyright © 2002-2005 Joel + de Guzman, David Abrahams

diff --git a/doc/tutorial/doc/html/python/object.html b/doc/tutorial/doc/html/python/object.html index 377380e2..e17cf4a4 100644 --- a/doc/tutorial/doc/html/python/object.html +++ b/doc/tutorial/doc/html/python/object.html @@ -32,234 +32,304 @@
Enums

-Python is dynamically typed, unlike C++ which is statically typed. Python -variables may hold an integer, a float, list, dict, tuple, str, long etc., -among other things. In the viewpoint of Boost.Python and C++, these -Pythonic variables are just instances of class object. We shall see in -this chapter how to deal with Python objects.

+ Object InterfacePython is dynamically typed, unlike C++ which is statically + typed. Python variables may hold an integer, a float, list, dict, tuple, str, + long etc., among other things. In the viewpoint of Boost.Python and C++, these + Pythonic variables are just instances of class object. We + shall see in this chapter how to deal with Python objects. +

-As mentioned, one of the goals of Boost.Python is to provide a -bidirectional mapping between C++ and Python while maintaining the Python -feel. Boost.Python C++ objects are as close as possible to Python. This -should minimize the learning curve significantly.

+ As mentioned, one of the goals of Boost.Python is to provide a bidirectional + mapping between C++ and Python while maintaining the Python feel. Boost.Python + C++ objects are as close as possible to Python. This should + minimize the learning curve significantly. +

Basic Interface

-Class object wraps PyObject*. All the intricacies of dealing with -PyObjects such as managing reference counting are handled by the -object class. C++ object interoperability is seamless. Boost.Python C++ -objects can in fact be explicitly constructed from any C++ object.

+ Basic InterfaceClass object wraps PyObject*. + All the intricacies of dealing with PyObjects such as + managing reference counting are handled by the object + class. C++ object interoperability is seamless. Boost.Python C++ objects + can in fact be explicitly constructed from any C++ object. +

-To illustrate, this Python code snippet:

+ To illustrate, this Python code snippet: +

-
def f(x, y):
-     if (y == 'foo'):
-         x[3:7] = 'bar'
-     else:
-         x.items += y(3, x)
-     return x
+
+def f(x, y):
+     if (y == 'foo'):
+         x[3:7] = 'bar'
+     else:
+         x.items += y(3, x)
+     return x
 
-def getfunc():
-   return f;
+def getfunc(): + return f; +

-Can be rewritten in C++ using Boost.Python facilities this way:

+ Can be rewritten in C++ using Boost.Python facilities this way: +

-
object f(object x, object y) {
-     if (y == "foo")
-         x.slice(3,7) = "bar";
-     else
-         x.attr("items") += y(3, x);
-     return x;
-}
-object getfunc() {
-    return object(f);
-}
+
+object f(object x, object y) {
+     if (y == "foo")
+         x.slice(3,7) = "bar";
+     else
+         x.attr("items") += y(3, x);
+     return x;
+}
+object getfunc() {
+    return object(f);
+}
+

-Apart from cosmetic differences due to the fact that we are writing the -code in C++, the look and feel should be immediately apparent to the Python -coder.

+ Apart from cosmetic differences due to the fact that we are writing the code + in C++, the look and feel should be immediately apparent to the Python coder. +

Derived Object types

-Boost.Python comes with a set of derived object types corresponding to -that of Python's:

+ Derived Object typesBoost.Python comes with a set of derived object + types corresponding to that of Python's: +

  • -list -
  • + list +
  • -dict -
  • + dict +
  • -tuple -
  • + tuple +
  • -str -
  • + str +
  • -long_ -
  • + long_ +
  • -enum -
  • + enum +

-These derived object types act like real Python types. For instance:

-
str(1) ==> "1"
+ 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);
+ 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 objects 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 C++, when Boost.Python objects 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();
+ 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);
+ 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++.

+ 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++. +

+ Beware the common pitfall of forgetting that + the constructors of most of Python's mutable types make copies, just + as in Python.
-Beware the common pitfall of forgetting that the constructors - of most of Python's mutable types make copies, just as in Python. -

-Python: -

-
>>> d = dict(x.__dict__)     # copies x.__dict__
->>> d['whatever']            # modifies the copy
-
+ Python: +

+
+>>> d = dict(x.__dict__)     # copies x.__dict__
+>>> d['whatever'] = 3        # modifies the copy
+

-C++: -

-
dict d(x.attr("__dict__"));  // copies x.__dict__
-d['whatever'] = 3;           // modifies the copy
-
+ C++: +

+
+dict d(x.attr("__dict__"));  // copies x.__dict__
+d['whatever'] = 3;           // modifies the copy
+

-class_<T> as objects

+ + 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.

+ 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);
+        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);
+assert(vec345.attr("length") == 5.0); +

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
-
+ Extracting C++ objectsAt 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 doubles. 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());
+ In the code above, we got a compiler error because Boost.Python object + can't be implicitly converted to doubles. 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.

+ 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 objects. 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(); ...
-

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__ !
+ 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 + objects. 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(); ...
+
+

+ 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__ !
+

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 };
+ EnumsBoost.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)
-    ;
+ 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.

+ 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. +

+ what is a scope?

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 here.
-what is a scope?

- 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 here.

-You can access those values in Python as

+ You can access those values in Python as +

-
>>> my_module.choice.red
-my_module.choice.red
+
+>>> 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:

+ where my_module is the module where the enum is declared. You can also create + a new scope around a class: +

-
scope in_X = class_<X>("X")
-                .def( ... )
-                .def( ... )
-            ;
+
+scope in_X = class_<X>("X")
+                .def( ... )
+                .def( ... )
+            ;
 
-// Expose X::nested as X.nested
-enum_<X::nested>("nested")
-    .value("red", red)
-    .value("blue", blue)
-    ;
+// Expose X::nested as X.nested +enum_<X::nested>("nested") + .value("red", red) + .value("blue", blue) + ; +
- +
Copyright © 2002-2005 Joel de Guzman, David AbrahamsCopyright © 2002-2005 Joel + de Guzman, David Abrahams

diff --git a/doc/tutorial/doc/html/python/techniques.html b/doc/tutorial/doc/html/python/techniques.html index 806adf32..87f7798c 100644 --- a/doc/tutorial/doc/html/python/techniques.html +++ b/doc/tutorial/doc/html/python/techniques.html @@ -30,109 +30,138 @@
Reducing Compiling Time

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

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

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 -Python Tutorial.

+ Creating PackagesA 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 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.

+ 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 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
+ 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 */
-    ...
-}
+        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 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 */
-    ...
-}
+/* 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.

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

+ + 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.
- 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:

-
sounds/
+        Now, we create this directory structure for our Python package:
+      

+
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.

+ 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 -PYTHONPATH -and fire up the interpreter:

+ Now our package is ready. All the user has to do is put sounds + into his 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)
+
+>>> import sounds.io
+>>> import sounds.filters
+>>> sound = sounds.io.open('file.mp3')
+>>> new_sound = sounds.filters.echo(sound, 1.0)
+

-Nice heh?

+ 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.

+ 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:

+ 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 */
-}
+
+/* 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:

-
sounds/
+        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:
+      

+
sounds/
     __init__.py
     core/
         __init__.py
@@ -143,220 +172,275 @@ Now, we change our package hierarchy like so:

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:

+ 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(...)
+
+>>> 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 soundscore__init__.py:

-
from _core import *
+ 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 soundscore__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(...)
+ 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
+ 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 soundsfilters__init__.py:

-
from echo_noise import echo_noise
+ Next, we add this line to soundsfilters__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(...)
+ 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(...)
+

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__ = C_str
->>>
->>> c = C()
->>> print c
-A C instance!
->>> C_str(c)
-A C instance!
+ Extending Wrapped Objects in PythonThanks 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__ = C_str
+>>>
+>>> c = C()
+>>> print c
+A C instance!
+>>> C_str(c)
+A C instance!
+

-Yes, Python rox.

+ Yes, Python rox.

-We can do the same with classes that were wrapped with Boost.Python. Suppose -we have a class point in C++:

+ We can do the same with classes that were wrapped with Boost.Python. Suppose + we have a class point in C++: +

-
class point {...};
+
+class point {...};
 
-BOOST_PYTHON_MODULE(_geom)
-{
-    class_<point>("point")...;
-}
+BOOST_PYTHON_MODULE(_geom) +{ + class_<point>("point")...; +} +

-If we are using the technique from the previous session, -Creating Packages, we can code directly -into geom/__init__.py:

+ If we are using the technique from the previous session, Creating + Packages, we can code directly into geom/__init__.py: +

-
from _geom import *
+
+from _geom import *
 
-# a regular function
-def point_str(self):
-    return str((self.x, self.y))
+# a regular function
+def point_str(self):
+    return str((self.x, self.y))
 
-# now we turn it into a member function
-point.__str__ = point_str
-

All point instances created from C++ will also have this member function! -This technique has several advantages:

+# now we turn it into a member function +point.__str__ = point_str +
+

All point instances created from C++ will + also have this member function! This technique has several advantages: +

  • -Cut down compile times to zero for these additional functions -
  • + Cut down compile times to zero for these additional functions +
  • -Reduce the memory footprint to virtually zero -
  • + Reduce the memory footprint to virtually zero +
  • -Minimize the need to recompile -
  • + Minimize the need to recompile +
  • -Rapid prototyping (you can move the code to C++ if required without changing the interface) -
  • + Rapid prototyping (you can move the code to C++ if required without changing + the interface) +

-You can even add a little syntactic sugar with the use of metaclasses. Let's -create a special metaclass that "injects" methods in other classes.

-
# The one Boost.Python uses for all wrapped classes.
-# You can use here any class exported by Boost instead of "point"
-BoostPythonMetaclass = point.__class__
+        You can even add a little syntactic sugar with the use of metaclasses. Let's
+        create a special metaclass that "injects" methods in other classes.
+      

+
+# The one Boost.Python uses for all wrapped classes.
+# You can use here any class exported by Boost instead of "point"
+BoostPythonMetaclass = point.__class__
 
-class injector(object):
-    class __metaclass__(BoostPythonMetaclass):
-        def __init__(self, name, bases, dict):
-            for b in bases:
-                if type(b) not in (self, type):
-                    for k,v in dict.items():
-                        setattr(b,k,v)
-            return type.__init__(self, name, bases, dict)
+class injector(object):
+    class __metaclass__(BoostPythonMetaclass):
+        def __init__(self, name, bases, dict):
+            for b in bases:
+                if type(b) not in (self, type):
+                    for k,v in dict.items():
+                        setattr(b,k,v)
+            return type.__init__(self, name, bases, dict)
 
-# inject some methods in the point foo
-class more_point(injector, point):
-    def __repr__(self):
-        return 'Point(x=%s, y=%s)' % (self.x, self.y)
-    def foo(self):
-        print 'foo!'
+# inject some methods in the point foo +class more_point(injector, point): + def __repr__(self): + return 'Point(x=%s, y=%s)' % (self.x, self.y) + def foo(self): + print 'foo!' +

-Now let's see how it got:

-
>>> print point()
-Point(x=10, y=10)
->>> point().foo()
-foo!
+ Now let's see how it got: +

+
+>>> print point()
+Point(x=10, y=10)
+>>> point().foo()
+foo!
+

-Another useful idea is to replace constructors with factory functions:

-
_point = point
+        Another useful idea is to replace constructors with factory functions:
+      

+
+_point = point
 
-def point(x=0, y=0):
-    return _point(x, y)
+def point(x=0, y=0): + return _point(x, y) +

-In this simple case there is not much gained, but for constructurs with -many overloads and/or arguments this is often a great simplification, again -with virtually zero memory footprint and zero compile-time overhead for -the keyword support.

+ In this simple case there is not much gained, but for constructurs with many + overloads and/or arguments this is often a great simplification, again with + virtually zero memory footprint and zero compile-time overhead for the keyword + support. +

Reducing Compiling Time

-If you have ever exported a lot of classes, you know that it takes quite a good -time to compile the Boost.Python wrappers. Plus the memory consumption can -easily become too high. If this is causing you problems, you can split the -class_ definitions in multiple files:

+ Reducing Compiling TimeIf you have ever exported a lot of classes, you know + that it takes quite a good time to compile the Boost.Python wrappers. Plus + the memory consumption can easily become too high. If this is causing you + problems, you can split the class_ definitions in multiple files: +

-
/* file point.cpp */
-#include <point.h>
-#include <boost/python.hpp>
+
+/* file point.cpp */
+#include <point.h>
+#include <boost/python.hpp>
 
-void export_point()
-{
-    class_<point>("point")...;
-}
+void export_point()
+{
+    class_<point>("point")...;
+}
 
-/* file triangle.cpp */
-#include <triangle.h>
-#include <boost/python.hpp>
+/* file triangle.cpp */
+#include <triangle.h>
+#include <boost/python.hpp>
 
-void export_triangle()
-{
-    class_<triangle>("triangle")...;
-}
+void export_triangle() +{ + class_<triangle>("triangle")...; +} +

-Now you create a file main.cpp, which contains the BOOST_PYTHON_MODULE -macro, and call the various export functions inside it.

-
void export_point();
-void export_triangle();
+        Now you create a file main.cpp, which contains the BOOST_PYTHON_MODULE
+        macro, and call the various export functions inside it.
+      

+
+void export_point();
+void export_triangle();
 
-BOOST_PYTHON_MODULE(_geom)
-{
-    export_point();
-    export_triangle();
-}
+BOOST_PYTHON_MODULE(_geom) +{ + export_point(); + export_triangle(); +} +

-Compiling and linking together all this files produces the same result as the -usual approach:

-
#include <boost/python.hpp>
-#include <point.h>
-#include <triangle.h>
+        Compiling and linking together all this files produces the same result as
+        the usual approach:
+      

+
+#include <boost/python.hpp>
+#include <point.h>
+#include <triangle.h>
 
-BOOST_PYTHON_MODULE(_geom)
-{
-    class_<point>("point")...;
-    class_<triangle>("triangle")...;
-}
+BOOST_PYTHON_MODULE(_geom) +{ + class_<point>("point")...; + class_<triangle>("triangle")...; +} +

-but the memory is kept under control.

+ but the memory is kept under control. +

-This method is recommended too if you are developing the C++ library and -exporting it to Python at the same time: changes in a class will only demand -the compilation of a single cpp, instead of the entire wrapper code.

+ This method is recommended too if you are developing the C++ library and + exporting it to Python at the same time: changes in a class will only demand + the compilation of a single cpp, instead of the entire wrapper code. +

+ + If you're exporting your classes with Pyste, + take a look at the --multiple option, that generates + the wrappers in various files as demonstrated here.
- If you're exporting your classes with Pyste, -take a look at the --multiple option, that generates the wrappers in -various files as demonstrated here.
+ + This method is useful too if you are getting the error message "fatal + error C1204:Compiler limit:internal structure overflow" + when compiling a large source file, as explained in the FAQ.
- This method is useful too if you are getting the error message -"fatal error C1204:Compiler limit:internal structure overflow" when compiling -a large source file, as explained in the FAQ.
- +
Copyright © 2002-2005 Joel de Guzman, David AbrahamsCopyright © 2002-2005 Joel + de Guzman, David Abrahams

diff --git a/doc/tutorial/doc/tutorial.qbk b/doc/tutorial/doc/tutorial.qbk index ec862279..cce8dfc7 100644 --- a/doc/tutorial/doc/tutorial.qbk +++ b/doc/tutorial/doc/tutorial.qbk @@ -1754,6 +1754,38 @@ Now, our C++ Wrapper: .property("pions", range(&F::p_begin, &F::p_end)) .property("bogons", range(&F::b_begin, &F::b_end)); +[*stl_input_iterator] + +So far, we have seen how to expose C++ iterators and ranges to Python. +Sometimes we wish to go the other way, though: we'd like to pass a +Python sequence to an STL algorithm or use it to initialize an STL +container. We need to make a Python iterator look like an STL iterator. +For that, we use `stl_input_iterator<>`. Consider how we might +implement a function that exposes `std::list::assign()` to +Python: + +[c++] + + template + void list_assign(std::list& l, object o) { + // Turn a Python sequence into an STL input range + stl_input_iterator begin(o), end; + l.assign(begin, end); + } + + // Part of the wrapper for list + class_ >("list_int") + .def("assign", &list_assign) + // ... + ; + +Now in Python, we can assign any integer sequence to `list_int` objects: + +[python] + + x = list_int(); + x.assign([1,2,3,4,5]) + [endsect] [section:exception Exception Translation] diff --git a/doc/v2/reference.html b/doc/v2/reference.html index 69bb518d..5ccf5329 100644 --- a/doc/v2/reference.html +++ b/doc/v2/reference.html @@ -293,6 +293,20 @@ +
stl_iterator.hpp
+ +
+
+
Classes
+ +
+
+
stl_input_iterator
+
+
+
+
+
wrapper.hpp
diff --git a/doc/v2/stl_iterator.html b/doc/v2/stl_iterator.html new file mode 100755 index 00000000..6457803f --- /dev/null +++ b/doc/v2/stl_iterator.html @@ -0,0 +1,269 @@ + + + + + + Boost.Python - <boost/python/stl_iterator.hpp> + + + + + + + + + + + + +
+

C++ Boost

+
+

Boost.Python

+ +

Header <boost/python/stl_iterator.hpp>

+
+
+ +

Contents

+
+
Introduction
+ +
Classes
+ +
+
+
Class template + stl_input_iterator
+ +
+
+
Class + stl_input_iterator synopsis
+ +
Class template + stl_input_iterator constructors
+ +
Class template + stl_input_iterator modifiers
+ +
Class template + stl_input_iterator observers
+
+
+
+
+ +
Examples
+
+
+ +

Introduction

+ +

<boost/python/stl_iterator.hpp> provides types + for creating C++ + Iterators from + Python sequences.

+ +

Classes

+ +

Class Template + stl_input_iterator

+ +

Instances of stl_input_iterator<T> hold a Python + iterator and adapt it for use with STL algorithms. + stl_input_iterator<T> satisfies the requirements for + an Input Iterator. +

+ + + + + + + + + + + + + + + + + + + + + +
Template ParameterRequirementsSemanticsDefault
ValueTypeValueType must be CopyConstructible.Dereferencing an instance of stl_input_iterator<ValueType> + will return an rvalue of type ValueType.None
+ +

Class Template stl_input_iterator + synopsis

+ +
+namespace boost { namespace python
+{
+  template <class ValueType>
+  struct stl_input_iterator
+  {
+      stl_input_iterator();
+      stl_input_iterator(object const& ob);
+
+      stl_input_iterator& operator++();
+      stl_input_iterator operator++(int);
+      
+      ValueType operator*() const;
+
+      friend bool operator==(stl_input_iterator const& lhs, stl_input_iterator const& rhs);
+      friend bool operator!=(stl_input_iterator const& lhs, stl_input_iterator const& rhs);
+  private:
+      object it_; // For exposition only
+      object ob_; // For exposition only
+  };
+}}
+
+ +

+ Class Template stl_input_iterator + constructors +

+ +
+stl_input_iterator()
+
+ +
+
Effects: + Creates a past-the-end input iterator, useful for signifying the end of a sequence. +
+
Postconditions: this is past-the-end.
+
Throws: Nothing.
+
+ +
+stl_input_iterator(object const& ob)
+
+ +
+
Effects: + Calls ob.attr("__iter__")() and stores the resulting Python iterator + object in this->it_. Then, calls this->it_.attr("next")() and + stores the result in this->ob_. If the sequence is exhausted, sets + this->ob_ to object(). +
+ +
Postconditions: this is a dereferenceable or past-the-end.
+ +
Throws: error_already_set if ob does not + provide an iterator.
+
+ +

+ Class Template stl_input_iterator + modifiers +

+ +
+stl_input_iterator& operator++()
+
+ +
+
Effects: + Calls this->it_.attr("next")() and stores the result in + this->ob_. If the sequence is exhausted, sets this->ob_ + to object(). +
+ +
Postconditions: this is a dereferenceable or past-the-end.
+ +
Returns: *this.
+
+ +
+stl_input_iterator operator++(int)
+
+ +
+
Effects: + stl_input_iterator tmp = *this; ++*this; return tmp; +
+ +
Postconditions: this is a dereferenceable or past-the-end.
+
+ +

Class Templatestl_input_iterator + observers

+ +
+ValueType operator*() const
+
+ +
+
Effects: + Returns the current element in the sequence. +
+
Returns: + extract<ValueType>(this->ob_); +
+
Throws: error_already_set if the current element in the sequence + is not of type ValueType.
+
+ +
+friend bool operator==(stl_input_iterator const& lhs, stl_input_iterator const& rhs)
+
+ +
+
Effects: + Returns true if both iterators are dereferenceable or if both iterators are past-the-end, + false otherwise. +
+
Returns: + (lhs.ob_ == object()) == (rhs.ob_ == object()) +
+
+ +
+friend bool operator!=(stl_input_iterator const& lhs, stl_input_iterator const& rhs)
+
+ +
+
Effects: + Returns false if both iterators are dereferenceable or if both iterators are past-the-end, + true otherwise. +
+
Returns: + !(lhs == rhs) +
+
+ +

Examples

+
+#include <boost/python/object.hpp>
+#include <boost/python/stl_iterator.hpp>
+
+#include <list>
+
+using namespace boost::python;
+std::list<int> sequence_to_int_list(object const& ob)
+{
+    stl_input_iterator<int> begin(ob), end;
+    return std::list<int>(begin, end);
+}
+
+ +
+

Revised + 30 +October, 2005 + +

+ +

© Copyright Eric Niebler 2005.

+ + \ No newline at end of file diff --git a/src/object/stl_iterator.cpp b/src/object/stl_iterator.cpp new file mode 100755 index 00000000..e2b66cea --- /dev/null +++ b/src/object/stl_iterator.cpp @@ -0,0 +1,42 @@ +// Copyright Eric Niebler 2005. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +namespace boost { namespace python { namespace objects +{ + +stl_input_iterator_impl::stl_input_iterator_impl() + : it_() + , ob_() +{ +} + +stl_input_iterator_impl::stl_input_iterator_impl(boost::python::object const &ob) + : it_(ob.attr("__iter__")()) + , ob_() +{ + this->increment(); +} + +void stl_input_iterator_impl::increment() +{ + this->ob_ = boost::python::handle<>( + boost::python::allow_null(PyIter_Next(this->it_.ptr()))); +} + +bool stl_input_iterator_impl::equal(stl_input_iterator_impl const &that) const +{ + return !this->ob_ == !that.ob_; +} + +boost::python::handle<> const &stl_input_iterator_impl::current() const +{ + return this->ob_; +} + +}}} // namespace boost::python::objects diff --git a/test/Jamfile b/test/Jamfile index a3cb8f93..aeec62b0 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -152,6 +152,8 @@ bpl-test crossmod_exception [ bpl-test iterator : iterator.py iterator.cpp input_iterator.cpp ] +[ bpl-test stl_iterator : stl_iterator.py stl_iterator.cpp ] + [ bpl-test extract ] [ bpl-test opaque ] diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index bd9ccfff..ac5c62ba 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -101,6 +101,8 @@ bpl-test crossmod_exception # [ bpl-test iterator : iterator.py iterator.cpp input_iterator.cpp ] +[ bpl-test stl_iterator : stl_iterator.py stl_iterator.cpp ] + [ bpl-test extract ] [ bpl-test opaque ] diff --git a/test/stl_iterator.cpp b/test/stl_iterator.cpp new file mode 100755 index 00000000..9cef9eba --- /dev/null +++ b/test/stl_iterator.cpp @@ -0,0 +1,31 @@ +// Copyright Eric Niebler 2005. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +#include +#include +#include +#include +#include + +using namespace boost::python; + +typedef std::list list_int; + +void assign(list_int& x, object const& y) +{ + stl_input_iterator begin(y), end; + x.assign(begin, end); +} + +BOOST_PYTHON_MODULE(stl_iterator_ext) +{ + using boost::python::iterator; // gcc 2.96 bug workaround + + class_("list_int") + .def("assign", assign) + .def("__iter__", iterator()) + ; +} + +#include "module_tail.cpp" diff --git a/test/stl_iterator.py b/test/stl_iterator.py new file mode 100644 index 00000000..64edd8d1 --- /dev/null +++ b/test/stl_iterator.py @@ -0,0 +1,29 @@ +# Copyright Eric Niebler 2005. Distributed under the Boost +# Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +''' +>>> from stl_iterator_ext import * +>>> x = list_int() +>>> x.assign([1,2,3,4,5]) +>>> for y in x: +... print y +1 +2 +3 +4 +5 +''' +def run(args = None): + import sys + import doctest + + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + +if __name__ == '__main__': + print "running..." + import sys + status = run()[0] + if (status == 0): print "Done." + sys.exit(status)