From e41abb6e9257c522f3ecc6198d0d9df073419cb9 Mon Sep 17 00:00:00 2001 From: Joel de Guzman Date: Sat, 26 Jul 2003 05:48:59 +0000 Subject: [PATCH] Initial commit of indexing suite documentation [SVN r19314] --- doc/v2/indexing.html | 278 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 doc/v2/indexing.html diff --git a/doc/v2/indexing.html b/doc/v2/indexing.html new file mode 100644 index 00000000..444044c8 --- /dev/null +++ b/doc/v2/indexing.html @@ -0,0 +1,278 @@ + + +Boost.Python Indexing Support + +
c++boost.gif (8819 bytes) +
+

Boost.Python Indexing Support

+

Indexing is a Python module for easy exportation of indexable C++ containers + to Python. Indexable containers are containers that allow random access through + the operator[] (e.g. std::vector). +

While Boost Python has all the facilities to expose indexable C++ containers + such as the ubiquitous std::vector to Python, the procedure is not as straightforward + as we'd like it to be. When in Python, do as the Pythonians do. Yet, Python + containers do not map easily to C++ containers. Emulating Python containers + in C++ (see Python Reference Manual, Emulating + container types) using Boost Python is non trivial. There are a lot of + issues to consider before we can map a C++ container to Python which involves + implementing wrapper functions for the methods __len__, __getitem__, + __setitem__, __delitem__, __iter__ + and __contains. +

The goals:

+ +
+
+

The Boost.Python Indexing Interface

+

indexing_suite

+

The indexing_suite class is the base protocol class for the management + of C++ containers intended to be integrated to Python. The objective is make + a C++ container look and feel and behave exactly as we'd expect a Python container. + The class automatically wraps these special Python methods:

+
+
+
+
__len__(self)
+
+
+
Called to implement the built-in function len() Should return the length + of the object, an integer >= 0. Also, an object that doesn't define a + __nonzero__() method and whose __len__() method returns zero is considered + to be false in a Boolean context.
+
+
 
+
+
+
+
__getitem__(self, key)
+
Called to implement evaluation of self[key]. For sequence types, the accepted + keys should be integers and slice objects. Note that the special interpretation + of negative indexes (if the class wishes to emulate a sequence type) is up + to the __getitem__() method. If key is of an inappropriate type, TypeError + may be raised; if of a value outside the set of indexes for the sequence (after + any special interpretation of negative values), IndexError should be raised. + Note: for loops expect that an IndexError will be raised for illegal indexes + to allow proper detection of the end of the sequence.
+
+
+
+
__setitem__(self, key, value)
+
+
+
Called to implement assignment to self[key]. Same note as for __getitem__(). + This should only be implemented for mappings if the objects support changes + to the values for keys, or if new keys can be added, or for sequences if + elements can be replaced. The same exceptions should be raised for improper + key values as for the __getitem__() method.
+
+
+
__delitem__(self, key)
+
Called to implement deletion of self[key]. Same note as for __getitem__(). + This should only be implemented for mappings if the objects support removal + of keys, or for sequences if elements can be removed from the sequence. The + same exceptions should be raised for improper key values as for the __getitem__() + method.
+
+

+ __iter__(self)
+
+
+
This method is called when an iterator is required for a container. This + method should return a new iterator object that can iterate over all the + objects in the container. For mappings, it should iterate over the keys + of the container, and should also be made available as the method iterkeys().
+
+
 
+
+
+
+
+
__contains__(self, item)
+
+
+
Called to implement membership test operators. Should return true if + item is in self, false otherwise. For mapping objects, this should consider + the keys of the mapping rather than the values or the key-item pairs.
+
+
+

indexing_suite sub-classes

+

The indexing_suite is not meant to be used as is. A couple of policy + functions must be supplied by subclasses of indexing_suite. However, + a set of indexing_suite subclasses for the standard indexable STL containers + will be provided, In most cases, we can simply use the available predefined + suites. In some cases, if needed, we can refine them to suit our needs.

+

vector_indexing_suite

+

The vector_indexing_suite class is a predefined indexing_suite + derived class for wrapping std::vector (and std::vector like) + classes (currently, this is the only predefined suite available). It provides + all the policies required by the indexing_suite.

+

Example usage:

+
    class X {...};
+    ...
+
+    class_<std::vector<X> >("XVec")
+        .def(vector_indexing_suite<std::vector<X> >())
+    ;
+
+

That's it! XVec is now a full-fledged Python container (see the example + in full, along with its python + test).

+
+

indexing_suite class

+
    template <
class Container
, class DerivedPolicies
, bool NoProxy = false
, class Element = typename Container::value_type
, class Key = typename Container::value_type
, class Index = typename Container::size_type
>
class indexing_suite
: public def_arg<
indexing_suite<
Container
, DerivedPolicies
, NoProxy
, Element
, Key
, Index
> >
{
public:
+
        template <class Class>
void visit(Class& cl) const;
};
+
+
Container
+
The contianer type to be wrapped to Python.
+
 
+
DerivedPolicies
+
Derived classes provide the hooks needed by the indexing_suite:
+
+
        static element_type& 
get_item(Container& container, index_type i); + + static object
get_slice(Container& container, index_type from, index_type to); + + static void
set_item(Container& container, index_type i, element_type const& v); + + static void
set_slice(
Container& container, index_type from,
index_type to, element_type const& v
); + + template <class Iter>
static void
set_slice(Container& container, index_type from,
index_type to, Iter first, Iter last
); + + static void
delete_item(Container& container, index_type i); + + static void
delete_slice(Container& container, index_type from, index_type to); + + static size_t
size(Container& container); + + template <class T>
static bool
contains(Container& container, T const& val); + + static index_type
convert_index(Container& container, PyObject* i); + + static index_type
adjust_index(index_type current, index_type from,
index_type to, size_type len
); +
+
+

Most of these policies are self explanatory. convert_index + and adjust_index, however, deserves some explanation.

+

convert_index converts an Python index into a C++ + index that the container can handle. For instance, negative indexes in Python, + by convention, indexes from the right (e.g. C[-1] indexes the rightmost + element in C). convert_index should handle + the necessary conversion for the C++ container (e.g. convert -1 to + C.size()-1). convert_index should also + be able to convert the type of the index (A dynamic Python type) to the actual + type that the C++ container expects.

+

When a container expands or contracts, held indexes to its elements must + be adjusted to follow the movement of data. For instance, if we erase 3 elements, + starting from index 0 from a 5 element vector, what used to be at index 4 + will now be at index 1:

+
+    [a][b][c][d][e] ---> [d][e]
+                 ^           ^
+                 4           1
+

adjust_index takes care of the adjustment. Given + a current index, the function should return the adjusted index when data in + the container at index from..to is replaced by len + elements.

+
+
+
NoProxy
+
By default indexed elements have Python reference semantics and are returned + by proxy. This can be disabled by supplying true in the NoProxy + template parameter.
+

+ Element
+
The container's element type. Defaults to Container::value_type
+

+ Key
+
The container's key type. Defaults to Container::value_type
+

+ Index
+
The container's index type. Defaults to Container::size_type
+
+ + + + +

def_arg

+

The Boost Python class_ interface provides + a generic visitation interface to avoid cluttering the class interface + through the def_arg mechanism. indexing_suite + derives from the def_arg base class and provides the requisite + visit member function taking in a const reference + to the class.

+
+
+
+

vector_indexing_suite class

+
+    template <
class Container,
bool NoProxy = false,
class DerivedPolicies = unspecified_default
class vector_indexing_suite
: public indexing_suite<Container, DerivedPolicies, NoProxy>
{
public:

typedef typename Container::value_type element_type;
typedef typename Container::value_type key_type;
typedef typename Container::size_type index_type;
typedef typename Container::size_type size_type;
typedef typename Container::difference_type difference_type;

static element_type&
get_item(Container& container, index_type i); + + static object + get_slice(Container& container, index_type from, index_type to); + + static void
set_item(Container& container, index_type i, element_type const& v); + + static void + set_slice(Container& container, index_type from, + index_type to, element_type const& v); + + template <class Iter>
static void
set_slice(Container& container, index_type from,
index_type to, Iter first, Iter last); + + static void + delete_item(Container& container, index_type i); + + static void + delete_slice(Container& container, index_type from, index_type to);
+ static size_t + size(Container& container); + + static bool + contains(Container& container, key_type const& key); + + static index_type + convert_index(Container& container, PyObject* i); + + static index_type + adjust_index(index_type current, index_type from, + index_type to, size_type len); + };
+
+
Container
+
The contianer type to be wrapped to Python.
+
 
+
NoProxy
+
By default indexed elements have Python reference semantics and are returned + by proxy. This can be disabled by supplying true in the + NoProxy template parameter.
+

+ DerivedPolicies
+
The vector_indexing_suite may still be derived to further tweak + any of the predefined policies. Static polymorphism through CRTP (James + Coplien. "Curiously Recurring Template Pattern". C++ Report, Feb. + 1995) enables the base indexing_suite class to call policy function + of the most derived class.
+
+
+ © Copyright Joel de Guzman 2003. 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.