From 6fee43fc6fa973a3c6781744466ded22f59fa465 Mon Sep 17 00:00:00 2001
From: Eric Niebler Copyright © 2002-2005 Joel de Guzman, David Abrahams Copyright © 2002-2005 Joel
+ de Guzman, 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 )
-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).
-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: + ++BOOST_PYTHON_MODULE(hello) +{ + def("greet", greet); +} ++#include <boost/python.hpp> +using namespace boost::python; -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 |
-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... 
-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) ;
-
+
-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: +-(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...
Now that we can embed the interpreter in + our programs, lets see how to put it to use...
-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. +-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_namespace = main_module.attr("__dict__"); ++object main_module(( + handle<>(borrowed(PyImport_AddModule("__main__"))))); -objectmain_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>. - |
-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: +| Py_eval_input | -for interpreting isolated expressions | +for + interpreting isolated expressions |
|---|---|---|
| Py_file_input | -for interpreting sequences of statements | +for + interpreting sequences of statements |
| Py_single_input | -for interpreting a single statement | +for + interpreting a single statement |
-When using Py_eval_input, the input string must contain a single expression -and its result is returned. When using Py_file_input, the string can -contain an abitrary number of statements and None is returned. -Py_single_input works in the same way as Py_file_input but only accepts a -single statement.
+ 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: + ++ , Py_file_input + , main_namespace.ptr() + , main_namespace.ptr()) +)); ++object main_module(( + handle<>(borrowed(PyImport_AddModule("__main__"))))); -objectmain_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()) -));
-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.
- |
-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: + ++int five_squared = extract<int>(main_namespace["result"]); ++object main_module(( + handle<>(borrowed(PyImport_AddModule("__main__"))))); -objectmain_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"]);
-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: + ++int five_squared = extract<int>(result); ++object result((handle<>( + PyRun_String("5 ** 2" + , Py_eval_input + , main_namespace.ptr() + , main_namespace.ptr())) +)); -intfive_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.
- |
-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: + ++ // execution will never get here: + int five_divided_by_zero = extract<int>(result); +} +catch(error_already_set) +{ + // handle the exception in some way +} ++try +{ + object result((handle<>(PyRun_String( + "5/0" + , Py_eval_input + , main_namespace.ptr() + , main_namespace.ptr())) + )); - // execution will never get here: -int five_divided_by_zero = extract<int>(result); -} -catch(error_already_set) -{ - // handle the exception in some way -}
-The error_already_set exception class doesn't carry any information in itself. -To find out more about the Python exception that occurred, you need to use the -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: + ++if (!result) + // Python exception occurred +else + // everything went okay, it's safe to use the result ++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 -
| - | Copyright © 2002-2005 Joel de Guzman, David Abrahams | +Copyright © 2002-2005 Joel + de Guzman, David Abrahams |
-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 Abrahams | +Copyright © 2002-2005 Joel + de Guzman, David Abrahams |
-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: + ++BOOST_PYTHON_MODULE(hello) +{ + class_<World>("World") + .def("greet", &World::greet) + .def("set", &World::set) + ; +} ++#include <boost/python.hpp> +using namespace boost::python; -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' +
-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. +-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 +
-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) +
-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: +-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>()); +
-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());. |
-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 methodsPython, + like many object oriented languages uses the term methods. + Methods correspond roughly to C++'s member functions |
-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: + ++ int default_f() { return this->Base::f(); } +}; ++struct BaseWrap : Base, wrapper<Base> +{ + int f() + { + if (override f = this->get_override("f")) + return f(); // *note* + return Base::f(); + } - intdefault_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());. |
-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 +
-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 { /*...*/ }; ++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); ++class FilePos { /*...*/ }; -FilePosoperator+(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". +-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>("Rational") + .def(float_(self)) // __float__ + .def(pow(self, other<Rational>)) // __pow__ + .def(abs(self)) // __abs__ + .def(str(self)) // __str__ + ; ++class Rational +{ public: operator double() const; }; -Rationalpow(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__ - ;
-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)). |
| - | Copyright © 2002-2005 Joel de Guzman, David Abrahams | +Copyright © 2002-2005 Joel + de Guzman, David Abrahams |
-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!
-
+
-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: +-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: +-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. +
- 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" |
-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: + ++ int f(int a, int b, int c) + { + return a + b + c; + }; +}; ++struct X +{ + bool f(int a) + { + return true; + } - boolf(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; - }; -};
-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) +
-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: + ++ // in module init + def("f", f); // all arguments + def("f", f2); // two arguments + def("f", f1); // one argument ++// 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 -
-When you want to wrap functions (or member functions) that either:
+ When you want to wrap functions (or member functions) that either: +-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()); +
-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. +-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). +-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(bool a, int b, char c) +{ + /*...*/ +} ++void foo() +{ + /*...*/ +} -voidfoo(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) -{ - /*...*/ -}
-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. +-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 Abrahams | +Copyright © 2002-2005 Joel + de Guzman, David Abrahams |
-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. - |
-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: +-if you are on Windows, and
+ if you are on Windows, and +-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: +-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. +
-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. +bjam is run using your operating system's command line interpreter.
-Start it up.
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. |
-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: +-if you are on Windows, and
+ if you are on Windows, and +-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 Abrahams | +Copyright © 2002-2005 Joel + de Guzman, David Abrahams |
-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:
Python Iterators:
-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: +-Here, start/finish may be one of:
+ Here, start/finish may be one of: +iterator
-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 Abrahams | +Copyright © 2002-2005 Joel + de Guzman, David Abrahams |
-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. +
-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 getfunc(): + return f; ++def f(x, y): + if (y == 'foo'): + x[3:7] = 'bar' + else: + x.items += y(3, x) + return x -defgetfunc(): - 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. +-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: +-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.
- |
-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 +
-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: + ++assert(vec345.attr("length") == 5.0); ++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);
-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__ ! +
-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. |
-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( ... ) - ; ++// Expose X::nested as X.nested +enum_<X::nested>("nested") + .value("red", red) + .value("blue", blue) + ; ++scope in_X = class_<X>("X") + .def( ... ) + .def( ... ) + ; -// Expose X::nested as X.nested -enum_<X::nested>("nested") - .value("red", red) - .value("blue", blue) - ;
| - | Copyright © 2002-2005 Joel de Guzman, David Abrahams | +Copyright © 2002-2005 Joel + de Guzman, David Abrahams |
-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. +-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 filters.cpp */ +BOOST_PYTHON_MODULE(filters) +{ + /* export everything in the sounds::filters namespace */ + ... +} ++/* 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 */ - ... -}
-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. |
-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(...) +
-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. 
-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 {...}; ++BOOST_PYTHON_MODULE(_geom) +{ + class_<point>("point")...; +} ++class 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: +
-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. + ++# 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!' ++# The one Boost.Python uses for all wrapped classes. +# You can use here any class exported by Boost instead of "point" +BoostPythonMetaclass = point.__class__ -classinjector(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!'
-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: + ++def point(x=0, y=0): + return _point(x, y) ++_point = point -defpoint(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. +-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> ++void export_triangle() +{ + class_<triangle>("triangle")...; +} ++/* file point.cpp */ +#include <point.h> +#include <boost/python.hpp> -voidexport_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")...; -}
-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. + ++BOOST_PYTHON_MODULE(_geom) +{ + export_point(); + export_triangle(); +} ++void export_point(); +void 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: + ++BOOST_PYTHON_MODULE(_geom) +{ + class_<point>("point")...; + class_<triangle>("triangle")...; +} ++#include <boost/python.hpp> +#include <point.h> +#include <triangle.h> -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. |
- 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 Abrahams | +Copyright © 2002-2005 Joel + de Guzman, David Abrahams |
|
+ |
+
+
+ Boost.Python+ +Header <boost/python/stl_iterator.hpp>+ |
+
<boost/python/stl_iterator.hpp> provides types
+ for creating C++
+ Iterators from
+ Python sequences.
stl_input_iteratorInstances 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 Parameter | + +Requirements | + +Semantics | + +Default | +
|---|---|---|---|
ValueType |
+
+ ValueType must be CopyConstructible. |
+
+ Dereferencing an instance of stl_input_iterator<ValueType>
+ will return an rvalue of type ValueType. |
+
+ None | +
+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
+ };
+}}
+
+
+ stl_input_iterator
+ constructors
+ +stl_input_iterator() ++ +
this is past-the-end.+stl_input_iterator(object const& ob) ++ +
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().
+ this is a dereferenceable or past-the-end.error_already_set if ob does not
+ provide an iterator.stl_input_iterator
+ modifiers
+ +stl_input_iterator& operator++() ++ +
this->it_.attr("next")() and stores the result in
+ this->ob_. If the sequence is exhausted, sets this->ob_
+ to object().
+ this is a dereferenceable or past-the-end.*this.+stl_input_iterator operator++(int) ++ +
stl_input_iterator tmp = *this; ++*this; return tmp;
+ this is a dereferenceable or past-the-end.stl_input_iterator
+ observers+ValueType operator*() const ++ +
extract<ValueType>(this->ob_);
+ 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) ++ +
(lhs.ob_ == object()) == (rhs.ob_ == object())
+ +friend bool operator!=(stl_input_iterator const& lhs, stl_input_iterator const& rhs) ++ +
!(lhs == rhs)
+
+#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