mirror of
https://github.com/boostorg/python.git
synced 2026-01-26 06:42:27 +00:00
Initial commit of indexing suite documentation
[SVN r19314]
This commit is contained in:
278
doc/v2/indexing.html
Normal file
278
doc/v2/indexing.html
Normal file
@@ -0,0 +1,278 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"
|
||||
"http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<link rel="stylesheet" type="text/css" href="../boost.css">
|
||||
<title>Boost.Python Indexing Support</title>
|
||||
|
||||
<div> <img src="../../../../c++boost.gif"
|
||||
alt="c++boost.gif (8819 bytes)"
|
||||
align="center"
|
||||
width="277" height="86">
|
||||
<hr>
|
||||
<h1>Boost.Python Indexing Support</h1>
|
||||
<p>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).
|
||||
<p>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, <a href="http://www.python.org/doc/current/ref/sequence-types.html">Emulating
|
||||
container types</a>) 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 <strong>__len__</strong>, <strong>__getitem__</strong>,
|
||||
<strong>__setitem__</strong>, <strong>__delitem__, </strong><strong>__iter__
|
||||
</strong>and<strong> __contains</strong>.
|
||||
<p>The goals:</div>
|
||||
<ul>
|
||||
<li>
|
||||
<div>Make indexable C++ containers behave exactly as one would expect a Python
|
||||
container to behave.</div>
|
||||
</li>
|
||||
<li>Provide default reference semantics for container element indexing (__getitem__)
|
||||
such that c[i] can be mutable. Require: <tt>c[i].m() == c[i] </tt> where m
|
||||
is a non-const (mutating) member function (method).</li>
|
||||
<li>Return safe references from __getitem__ such that subsequent adds and deletes
|
||||
to and from the container will not result in dangling references (will not
|
||||
crash Python).</li>
|
||||
<li>Support slice indexes.</li>
|
||||
<li>Allow interoperability with Python containers (e.g lists, tuples).</li>
|
||||
<li>Allow for extensibility through redefinable policy classes.</li>
|
||||
<li>Provide predefined support for the most common STL and STL like indexable
|
||||
containers.</li>
|
||||
</ul>
|
||||
<div>
|
||||
<hr>
|
||||
<h2>The Boost.Python Indexing Interface</h2>
|
||||
<h3>indexing_suite</h3>
|
||||
<p>The <tt>indexing_suite</tt> 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:</p>
|
||||
</div>
|
||||
<dl>
|
||||
<dt>
|
||||
<div><strong>__len__(self)</strong></div>
|
||||
</dt>
|
||||
<dd>
|
||||
<div>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.</div>
|
||||
</dd>
|
||||
<dt> </dt>
|
||||
<dd>
|
||||
<div></div>
|
||||
</dd>
|
||||
<dt><strong>__getitem__(self, key)</strong></dt>
|
||||
<dd>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.</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt>
|
||||
<div><strong>__setitem__(self, key, value)</strong></div>
|
||||
</dt>
|
||||
<dd>
|
||||
<div> 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.<br>
|
||||
</div>
|
||||
</dd>
|
||||
<dt><strong>__delitem__(self, key)</strong></dt>
|
||||
<dd>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.</dd>
|
||||
<dt>
|
||||
<div><br>
|
||||
<strong>__iter__(self)</strong></div>
|
||||
</dt>
|
||||
<dd>
|
||||
<div>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().</div>
|
||||
</dd>
|
||||
<dt> </dt>
|
||||
<dd>
|
||||
<div></div>
|
||||
</dd>
|
||||
<dt>
|
||||
<div><strong>__contains__(self, item)</strong></div>
|
||||
</dt>
|
||||
<dd>
|
||||
<div>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. </div>
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>indexing_suite sub-classes</h3>
|
||||
<p>The <tt>indexing_suite</tt> is not meant to be used as is. A couple of policy
|
||||
functions must be supplied by subclasses of <tt>indexing_suite</tt>. However,
|
||||
a set of <tt>indexing_suite</tt> 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.</p>
|
||||
<h3>vector_indexing_suite</h3>
|
||||
<p>The <tt>vector_indexing_suite</tt> class is a predefined <tt>indexing_suite</tt>
|
||||
derived class for wrapping <tt>std::vector</tt> (and <tt>std::vector</tt> like)
|
||||
classes (currently, this is the only predefined suite available). It provides
|
||||
all the policies required by the <tt>indexing_suite</tt>.</p>
|
||||
<p> Example usage:</p>
|
||||
<pre> class X {...};
|
||||
...
|
||||
|
||||
class_<std::vector<X> >("XVec")
|
||||
.def(vector_indexing_suite<std::vector<X> >())
|
||||
;
|
||||
</pre>
|
||||
<p>That's it! <tt>XVec</tt> is now a full-fledged Python container (see the <a href="../../test/vector_indexing_suite.cpp">example
|
||||
in full</a>, along with its <a href="../../test/vector_indexing_suite.py">python
|
||||
test</a>).</p>
|
||||
<hr>
|
||||
<h2>indexing_suite class</h2>
|
||||
<pre> template <<br> class Container<br> , class DerivedPolicies<br> , bool NoProxy = false<br> , class Element = typename Container::value_type<br> , class Key = typename Container::value_type<br> , class Index = typename Container::size_type<br> ><br> class indexing_suite <br> : public def_arg<<br> indexing_suite<<br> Container<br> , DerivedPolicies<br> , NoProxy<br> , Element<br> , Key<br> , Index<br> > ><br> {<br> public:</pre>
|
||||
<pre> template <class Class><br> void visit(Class& cl) const;<br> };</pre>
|
||||
<dl>
|
||||
<dt> <strong><tt>Container</tt></strong></dt>
|
||||
<dd>The contianer type to be wrapped to Python.</dd>
|
||||
<dt> </dt>
|
||||
<dt><strong><tt>DerivedPolicies</tt></strong></dt>
|
||||
<dd>Derived classes provide the hooks needed by the <tt>indexing_suite:</tt></dd>
|
||||
</dl>
|
||||
<pre> static element_type& <br> get_item(Container& container, index_type i);
|
||||
|
||||
static object <br> get_slice(Container& container, index_type from, index_type to);
|
||||
|
||||
static void <br> set_item(Container& container, index_type i, element_type const& v);
|
||||
|
||||
static void <br> set_slice(<br> Container& container, index_type from, <br> index_type to, element_type const& v<br> );
|
||||
|
||||
template <class Iter><br> static void <br> set_slice(Container& container, index_type from, <br> index_type to, Iter first, Iter last<br> );
|
||||
|
||||
static void <br> delete_item(Container& container, index_type i);
|
||||
|
||||
static void <br> delete_slice(Container& container, index_type from, index_type to);
|
||||
|
||||
static size_t<br> size(Container& container);
|
||||
|
||||
template <class T><br> static bool<br> contains(Container& container, T const& val);
|
||||
|
||||
static index_type<br> convert_index(Container& container, PyObject* i);
|
||||
|
||||
static index_type<br> adjust_index(index_type current, index_type from, <br> index_type to, size_type len<br> );
|
||||
</pre>
|
||||
<blockquote>
|
||||
<p>Most of these policies are self explanatory. <tt><strong>convert_index</strong></tt>
|
||||
and <tt><strong>adjust_index</strong></tt>, however, deserves some explanation.</p>
|
||||
<p><strong><tt>convert_index</tt></strong> 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. <tt>C[-1]</tt> indexes the rightmost
|
||||
element in <tt>C</tt>). <strong><tt>convert_index</tt></strong> should handle
|
||||
the necessary conversion for the C++ container (e.g. convert <tt>-1</tt> to
|
||||
<tt>C.size()-1</tt>). <tt><strong>convert_index</strong></tt> should also
|
||||
be able to convert the type of the index (A dynamic Python type) to the actual
|
||||
type that the C++ container expects.</p>
|
||||
<p>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:</p>
|
||||
<pre>
|
||||
[a][b][c][d][e] ---> [d][e]
|
||||
^ ^
|
||||
4 1</pre>
|
||||
<p> <strong><tt>adjust_index</tt></strong> takes care of the adjustment. Given
|
||||
a current index, the function should return the adjusted index when data in
|
||||
the container at index <tt>from</tt>..<tt>to</tt> is replaced by <tt>len</tt>
|
||||
elements. </p>
|
||||
</blockquote>
|
||||
<dl>
|
||||
<dt><strong><tt>NoProxy</tt></strong></dt>
|
||||
<dd>By default indexed elements have Python reference semantics and are returned
|
||||
by proxy. This can be disabled by supplying <strong>true</strong> in the <tt>NoProxy</tt>
|
||||
template parameter.</dd>
|
||||
<dt><tt><strong><br>
|
||||
Element</strong></tt></dt>
|
||||
<dd><tt><strong></strong></tt>The container's element type. Defaults to <tt>Container::value_type</tt></dd>
|
||||
<dt><tt><strong></strong></tt><tt><strong><br>
|
||||
Key</strong></tt></dt>
|
||||
<dd><tt><strong></strong></tt>The container's key type. Defaults to <tt>Container::value_type</tt></dd>
|
||||
<dt><tt><strong></strong></tt><tt><strong><br>
|
||||
Index</strong></tt></dt>
|
||||
<dd><tt><strong></strong></tt>The container's index type. Defaults to <tt>Container::size_type</tt></dd>
|
||||
</dl>
|
||||
<table width="75%" border="1" align="center">
|
||||
<tr>
|
||||
<td><h2><strong><tt>def_arg</tt></strong></h2>
|
||||
<p>The Boost Python <tt><strong>class_</strong></tt> interface provides
|
||||
a generic visitation interface to avoid cluttering the class interface
|
||||
through the <tt><strong>def_arg</strong></tt> mechanism. indexing_suite
|
||||
derives from the <tt>def_arg</tt> base class and provides the requisite
|
||||
<strong> <tt>visit</tt></strong> member function taking in a const reference
|
||||
to the class. </p></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br>
|
||||
<div>
|
||||
<hr>
|
||||
<h2>vector_indexing_suite class</h2>
|
||||
<pre>
|
||||
template <<br> class Container, <br> bool NoProxy = false,<br> class DerivedPolicies = unspecified_default<br> class vector_indexing_suite <br> : public indexing_suite<Container, DerivedPolicies, NoProxy><br> {<br> public:<br> <br> typedef typename Container::value_type element_type;<br> typedef typename Container::value_type key_type;<br> typedef typename Container::size_type index_type;<br> typedef typename Container::size_type size_type;<br> typedef typename Container::difference_type difference_type;<br> <br> static element_type& <br> get_item(Container& container, index_type i);
|
||||
|
||||
static object
|
||||
get_slice(Container& container, index_type from, index_type to);
|
||||
|
||||
static void <br> 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><br> static void <br> set_slice(Container& container, index_type from, <br> 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);<br>
|
||||
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);
|
||||
};</pre>
|
||||
<dl>
|
||||
<dt><strong><tt>Container</tt></strong></dt>
|
||||
<dd>The contianer type to be wrapped to Python.</dd>
|
||||
<dt> </dt>
|
||||
<dt><strong><tt>NoProxy</tt></strong></dt>
|
||||
<dd>By default indexed elements have Python reference semantics and are returned
|
||||
by proxy. This can be disabled by supplying <strong>true</strong> in the
|
||||
<tt>NoProxy</tt> template parameter.</dd>
|
||||
<dt><br>
|
||||
<strong><tt>DerivedPolicies</tt></strong></dt>
|
||||
<dd>The <tt>vector_indexing_suite</tt> 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 <tt>indexing_suite</tt> class to call policy function
|
||||
of the most derived class.</dd>
|
||||
</dl>
|
||||
<hr>
|
||||
© 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.</div>
|
||||
Reference in New Issue
Block a user