diff --git a/doc/cross_module_dependencies.html b/doc/cross_module_dependencies.html index 3d416bf5..0508cf6e 100644 --- a/doc/cross_module_dependencies.html +++ b/doc/cross_module_dependencies.html @@ -1,15 +1,15 @@ - - - Cross-extension-module dependencies - - + + +Cross-extension-module dependencies + +
c++boost.gif (8819 bytes) + width="277" height="86"> -

Cross-extension-module dependencies

@@ -27,11 +27,12 @@ provides for classes that are wrapped in the same extension module. To a large extent this complexity can be hidden from the author of a Boost.Python extension module, but not entirely. -

The recipe

+
+

The recipe

Suppose there is an extension module that exposes certain instances of -the C++ std::vector template library such that it can be used from -Python in the following manner: +the C++ std::vector template library such that it can be used +from Python in the following manner:
 import std_vector
@@ -40,79 +41,106 @@ v.push_back(5)
 v.size()
 
-Suppose the std_vector module is done well and reflects all C++ -functions that are useful at the Python level, for all C++ built-in -data types (std_vector.int, std_vector.long, etc.). +Suppose the std_vector module is done well and reflects all +C++ functions that are useful at the Python level, for all C++ built-in +data types (std_vector.int, std_vector.long, etc.).

Suppose further that there is statistic module with a C++ class that -has constructors or member functions that use or return a std::vector. -For example: +has constructors or member functions that use or return a +std::vector. For example:

 class xy {
-  private:
-    std::vector<double> m_x;
-    std::vector<double> m_y;
   public:
     xy(const std::vector<double>& x, const std::vector<double>& y) : m_x(x), m_y(y) {}
     const std::vector<double>& x() const { return m_x; }
     const std::vector<double>& y() const { return m_y; }
     double correlation();
+  private:
+    std::vector<double> m_x;
+    std::vector<double> m_y;
 }
 
-What is more natural then reusing the std_vector extension module to -expose these constructors or functions to Python? +What is more natural than reusing the std_vector extension +module to expose these constructors or functions to Python?

Unfortunately, what seems natural needs a little work in both the -std_vector and the statistics module. +std_vector and the statistics module.

-In the std_vector extension module, std::vector<double> needs to be -exposed to Python with the x_class_builder<> template instead of the -regular class_builder<>. For example: +In the std_vector extension module, +std::vector<double> is exposed to Python in the usual +way with the class_builder<> template. To also enable the +automatic conversion of std::vector<double> function +arguments or return values in other Boost.Python C++ modules, the +converters that convert a std::vector<double> C++ object +to a Python object and vice versa (see last section for details) have +to be exported. For example:

-  x_class_builder<std::vector<double> > v_double(std_vector_module, "double");
+  class_builder<std::vector<double> > v_double(std_vector_module, "double");
+  export_converters(v_double);
 
-In the extension module that wraps class xy we need to use -the import_class_builder<> template: - -
-  import_class_builder<std::vector<double> > v_double("std_vector", "double");
-
- -That is all. All the properties that are defined for std_vector.double -in the std_vector Boost.Python module will be available for the -returned objects of xy.x() and xy.y(). Similarly, the constructor for -xy will accept objects that were created by the std_vector module. - -

Non-copyable types

- -The x_class_builder<T> instantiates template functions that invoke the -copy constructor of T. For a T that is non-copyable this will result in -compile-time error messages. In such a case, another variety of the -class_builder<>, the xptr_class_builder<> must be used. +In the extension module that wraps class xy we can now import +these converters with the import_converters<> template. For example:
-xptr_class_builder<store> py_store(your_module, "store");
+  import_converters<std::vector<double> > v_double_converters("std_vector", "double");
 
-The corresponding import_class_builder<> does not need any special -attention: +That is all. All the attributes that are defined for +std_vector.double in the std_vector Boost.Python +module will be available for the returned objects of xy.x() +and xy.y(). Similarly, the constructor for xy will +accept objects that were created by the std_vectormodule. + +
+

Placement of import_converters<> template instantiations

+ +import_converts<> can be viewed as a drop-in replacement +for class_wrapper<>, and the recommendations for the +placement of class_wrapper<> template instantiations +also apply to to import_converts<>. In particular, it is +important that an instantiation of class_wrapper<> is +visible to any code which wraps a C++ function with a T, +T*, const T&, etc. parameter or return value. +Therefore you may want to group all class_wrapper<> and +import_converts<> instantiations at the top of your +module's init function, then def() the member functions later +to avoid problems with inter-class dependencies. + +
+

Non-copyable types

+ +export_converters() instantiates C++ template functions that +invoke the copy constructor of the wrapped type. For a type that is +non-copyable this will result in compile-time error messages. In such a +case, export_converters_noncopyable() can be used to export +the converters that do not involve the copy constructor of the wrapped +type. For example:
-import_class_builder<store> py_store("noncopyable_export", "store");
+class_builder<store> py_store(your_module, "store");
+export_converters_noncopyable(py_store);
 
-

Python module search path

+The corresponding import_converters() statement does not need +any special attention: -The std_vector and statistics modules can now be used in the following -way: +
+import_converters<store> py_store("noncopyable_export", "store");
+
+ +
+

Python module search path

+ +The std_vector and statistics modules can now be used +in the following way:
 import std_vector
@@ -124,14 +152,14 @@ xy.correlation()
 
In this example it is clear that Python has to be able to find both the -std_vector and the statistics extension module. In other words, both -extension modules need to be in the Python module search path -(sys.path). +std_vector and the statistics extension module. In +other words, both extension modules need to be in the Python module +search path (sys.path).

-The situation is not always that obvious. Suppose the statistics -module has a random function that returns a vector of random -numbers with a given length: +The situation is not always this obvious. Suppose the +statistics module has a random function that returns a vector +of random numbers with a given length:

 import statistics
@@ -141,15 +169,15 @@ xy = statistics.xy(x, y)
 xy.correlation()
 
-A naive user will not easily anticipate that the std_vector module is -used to pass the x and y vectors around. If the std_vector module is in -the Python module search path, this form of ignorance is of no harm. -On the contrary, we are glad that we do not have to bother the user -with details like this. +A naive user will not easily anticipate that the std_vector +module is used to pass the x and y vectors around. If +the std_vector module is in the Python module search path, +this form of ignorance is of no harm. On the contrary, we are glad +that we do not have to bother the user with details like this.

-If the std_vector module is not in the Python module search path, a -Python exception will be raised: +If the std_vector module is not in the Python module search +path, a Python exception will be raised:

 Traceback (innermost last):
@@ -161,16 +189,17 @@ ImportError: No module named std_vector
 As is the case with any system of a non-trivial complexity, it is
 important that the setup is consistent and complete.
 
-

Two-way module dependencies

+
+

Two-way module dependencies

Boost.Python supports two-way module dependencies. This is best illustrated by a simple example.

-Suppose there is a module ivect that implements vectors of integers, -and a similar module dvect that implements vectors of doubles. We want -to be able do convert an integer vector to a double vector and vice -versa. For example: +Suppose there is a module ivect that implements vectors of +integers, and a similar module dvect that implements vectors +of doubles. We want to be able do convert an integer vector to a double +vector and vice versa. For example:

 import ivect
@@ -178,9 +207,10 @@ iv = ivect.ivect((1,2,3,4,5))
 dv = iv.as_dvect()
 
-The last expression will implicitly import the dvect module in order to -enable the conversion of the C++ representation of dvect to a Python -object. The analogous is possible for a dvect: +The last expression will implicitly import the dvect module in +order to enable the conversion of the C++ representation of +dvect to a Python object. The analogous is possible for a +dvect:
 import dvect
@@ -188,44 +218,49 @@ dv = dvect.dvect((1,2,3,4,5))
 iv = dv.as_ivect()
 
-Now the ivect module is imported implicitly. +Now the ivect module is imported implicitly.

Note that the two-way dependencies are possible because the dependencies are resolved only when needed. This is, the initialization -of the ivect module does not rely on the dvect module, and vice versa. -Only if as_dvect() or as_ivect() is actually invoked will the -corresponding module be implicitly imported. This also means that, for -example, the dvect module does not have to be available at all if -as_dvect() is never used. +of the ivect module does not rely on the dvect +module, and vice versa. Only if as_dvect() or +as_ivect() is actually invoked will the corresponding module +be implicitly imported. This also means that, for example, the +dvect module does not have to be available at all if +as_dvect() is never used. -

Clarification of compile-time and link-time dependencies

+
+

Clarification of compile-time and link-time dependencies

Boost.Python's support for resolving cross-module dependencies at runtime does not imply that compile-time dependencies are eliminated. For example, the statistics extension module in the example above will -need to #include <vector>. This is immediately obvious from the -definition of class xy. +need to #include <vector>. This is immediately obvious +from the definition of class xy.

If a library is wrapped that consists of both header files and compiled -components (e.g. libdvect.a, dvect.lib, etc.), both the Boost.Python -extension module with the x_class_wrapper<> and the module with the -import_class_wrapper<> need to be linked against the object library. -Ideally one would build a shared library (e.g. libdvect.so, dvect.dll, -etc.). However, this introduces the issue of getting the search path -for the dynamic loading configured correctly. For small libraries it is -therefore often more convenient to ignore the fact that the object -files are loaded into memory more than once. +components (e.g. libdvect.a, dvect.lib, etc.), both +the Boost.Python extension module with the +export_converters() statement and the module with the +import_converters() statement need to be linked against +the object library. Ideally one would build a shared library (e.g. +libdvect.so, dvect.dll, etc.). However, this +introduces the issue of getting the search path for the dynamic loading +configured correctly. For small libraries it is therefore often more +convenient to ignore the fact that the object files are loaded into +memory more than once.

The main purpose of Boost.Python's support for resolving cross-module dependencies at runtime is to allow for a modular system layout. With this support it is straightforward to reflect C++ code organization at the Python level. Without the cross-module support, a multi-purpose -module like std_vector would be impractical because the entire wrapper -code would somehow have to be duplicated in all extension modules that -use it, making them harder to maintain and harder to build. +module like std_vector would be impractical because the entire +wrapper code would somehow have to be duplicated in all extension +modules that use it, making them harder to maintain and harder to +build.

Finally, there is an important psychological component. If a group of @@ -235,19 +270,27 @@ The situation is much more transparent if the work is represented by a module with a recognizable name. This is not just a question of strong egos, but also of getting credit and funding. -

Why not use the x_class_builder universally?

+
+

Why not use export_converters() universally?

There is some overhead associated with the Boost.Python cross-module -support. Depending on the platform, the code generated by -x_class_builder<> is roughly 10%-20% larger than that generated by -class_builder<>. For a large extension module with many wrapped -classes, this could mean a significant difference. Therefore the -general recommendation is to use x_class_wrapper<> only for classes -that are likely to be used as function arguments or return values in -other modules. +support. Depending on the platform, the size of the code generated by +export_converters() is roughly 10%-20% of that generated +by class_builder<>. For a large extension module with +many wrapped classes, this could mean a significant difference. +Therefore the general recommendation is to use +export_converters() only for classes that are likely to +be used as function arguments or return values in other modules.
-
-Author: Ralf W. Grosse-Kunstleve, March 2001 +© Copyright Ralf W. Grosse-Kunstleve 2001. Permission to copy, +use, modify, sell and distribute this document is granted provided this +copyright notice appears in all copies. This document is provided "as +is" without express or implied warranty, and with no claim as to its +suitability for any purpose. + +

+Updated: March 2001 +

- +