添字アクセスのサポート

ヘッダ <boost/python/indexing/indexing_suite.hpp> 、ヘッダ <boost/python/indexing/vector_indexing_suite.hpp>

はじめに

indexing は、添字アクセス可能(indexable)な C++ コンテナを Python へ容易にエクスポートするための Boost.Python の機能である。添字アクセス可能なコンテナとは operator[] によりランダムアクセス可能なコンテナである(例:std::vector)。

std::vector のようなどこにでもある添字アクセス可能な C++ コンテナを Python へエクスポートするのに必要な機能は Boost.Python はすべて有しているが、その方法はあまり直感的ではない。Python のコンテナから C++ コンテナへの変換は容易ではない。Python のコンテナを Boost.Python を使用して C++ 内でエミュレート(Python リファレンスマニュアルの「コンテナ型をエミュレートする」を見よ)するのは簡単ではない。C++ コンテナを Python へ変換する以前に考慮すべきことが多数ある。これには __len____getitem____setitem____delitem____iter__ および __contains__ メソッドに対するラッパ関数の実装が含まれる。

目的は、

  1. 添字アクセス可能な C++ コンテナの振る舞いを、Python コンテナの振る舞いに一致させる。

  2. c[i] が変更可能であるといった、コンテナ要素のインデクス(__getitem__)に対する既定の参照セマンティクスを提供する。要件は以下のとおり(m は非 const(可変)メンバ関数(メソッド))。

    val = c[i]
    c[i].m()
    val == c[i]
    
  3. __getitem__ から安全な参照を返す:後でコンテナに追加、コンテナから削除しても懸垂参照が発生しない(Python をクラッシュさせない)。

  4. スライスの添字をサポート。

  5. 適切な場合は Python のコンテナ引数(例:listtuple)を受け付ける。

  6. 再定義可能なポリシークラスによる拡張性。

  7. ほとんどの STL および STL スタイルの添字アクセス可能なコンテナに対する定義済みサポートを提供する。

Boost.Python の indexing インターフェイス

indexing_suite[ヘッダ <boost/python/indexing/indexing_suite.hpp>]

indexing_suite クラスは、Python に調和させる C++ コンテナを管理するための基底クラスである。目的は C++ コンテナの外観と振る舞いを Python コンテナのそれに一致させることである。このクラスは自動的に(Python リファレンスの「コンテナ型をエミュレートする」の)Python の特殊メソッドをラップする。

__len__(self)

組み込み関数 len を実装するために呼び出される。オブジェクトの長さ(0 以上の整数)を返さなければならない。また __nonzero__ メソッドを定義せず __len__ メソッドが 0 を返すオブジェクトは、論理値の文脈で偽として扱われる。

__getitem__(self, key)

self[key] の評価を実装するために呼び出される。シーケンス型では、受け取るキーは整数およびスライスオブジェクトでなければならない。負の添字に対する特殊な解釈(クラスがシーケンス型をエミュレートする場合)は __getitem__ メソッドの仕事であることに注意していただきたい。key が不適な型な場合は TypeError を送出し、値が(負の値に対する特殊な解釈の後)シーケンスの添字の集合外である場合は IndexError を送出しなければならない。注意:シーケンスの終了を適切に検出するため、for ループは不正な添字に対して IndexError が送出することを想定している。

__setitem__(self, key, value)

self[key] への代入を実装するために呼び出される。注意すべき点は __getitem__ と同じである。このメソッドは、マップ型についてはオブジェクトがキーに対する値の変更をサポートするか新しいキーを追加可能な場合、シーケンス型については要素が置換可能な場合のみ実装すべきである。不適な key 値に対しては __getitem__ メソッドと同様の例外を送出しなければならない。

__delitem__(self, key)

self[key] の削除を実装するために呼び出される。注意すべき点は __getitem__ と同じである。このメソッドは、マップ型についてはオブジェクトがキーの削除をサポートする場合、シーケンス型については要素をシーケンスから削除可能な場合のみ実装すべきである。不適な key 値に対しては __getitem__ メソッドと同様の例外を送出しなければならない。

__iter__(self)

このメソッドは、コンテナに対してイテレータが要求されたときに呼び出される。このメソッドは、コンテナ内のすべてのオブジェクトを走査する新しいイテレータオブジェクトを返さなければならない。マップ型については、コンテナのキーを走査しなければならず、iterkeys メソッドとしても利用可能でなければならない。

イテレータオブジェクトもまたこのメソッドを実装する必要があり、自分自身を返さなければならない。イテレータオブジェクトの詳細については、Python ライブラリリファレンスの「イテレータ型」を見よ。

__contains__(self, item)

メンバ関係テスト操作を実装するために呼び出される。self 内に item があれば真を、そうでなければ偽を返さなければならない。マップ型オブジェクトについては、値やキー・値の組ではなくキーを対象とすべきである。

indexing_suite の派生クラス

indexing_suite はそのままで使用することを意図していない。indexing_suite の派生クラスにより 2 、3 のポリシー関数を提供しなければならない。しかしながら、標準的な添字アクセス可能な STL コンテナのための indexing_suite 派生クラス群が提供されている。ほとんどの場合、単に定義済みのクラス群を使用すればよい。場合によっては必要に応じて定義済みクラスを改良してもよい。

vector_indexing_suite[ヘッダ <boost/python/indexing/vector_indexing_suite.hpp>]

vector_indexing_suite クラスは、std::vector クラス(および std::vector スタイルのクラス(例:std::vector のインターフェイスを持つクラス))をラップするために設計された定義済みの indexing_suite 派生クラスである。indexing_suite が要求するポリシーをすべて提供する。

使用例
class X {...};
...

class_<std::vector<X> >("XVec")
    .def(vector_indexing_suite<std::vector<X> >())
;

XVec は完全な Python コンテナとなる(完全な例と Python のテストも見よ)。

map_indexing_suite[ヘッダ <boost/python/indexing/map_indexing_suite.hpp>]

map_indexing_suite クラスは、std::map クラス(および std::map スタイルのクラス(例:std::map のインターフェイスを持つクラス))をラップするために設計された定義済みの indexing_suite 派生クラスである。indexing_suite が要求するポリシーをすべて提供する。

使用例
class X {...};
...

class_<std::map<X> >("XMap")
    .def(map_indexing_suite<std::map<X> >())
;

既定では添字アクセスした要素はプロキシで返される。NoProxy テンプレート引数で true を与えるとこれは無効化できる。XMap は完全な Python コンテナとなる(完全な例Python のテストも見よ)。

indexing_suite クラス

template<class Container, class DerivedPolicies, bool NoProxy = false, bool NoSlice = false, class Data = typename Container::value_type, class Index = typename Container::size_type, class Key = typename Container::value_type>
class indexing_suite : unspecified
テンプレートパラメータ
  • Container --

    Python に対してラップするコンテナ型。

    要件

    クラス型

  • DerivedPolicies --

    ポリシーフックを提供する派生クラス群。以下の DerivedPolicies を見よ。

    要件

    indexing_suite の派生クラス

  • NoProxy --

    既定では添字アクセスした要素は Python の参照のセマンティクスを持ち、プロキシにより返される。これは NoProxy テンプレート引数に真を与えることで無効化できる。

    要件

    論理値

    既定

    false

  • NoSlice --

    スライスを許可しない。

    要件

    論理値

    既定

    false

  • Data --

    コンテナのデータ型。

    既定

    Container::value_type

  • Index --

    コンテナの添字型。

    既定

    Container::size_type

  • Key --

    コンテナのキー型。

    既定

    Container::value_type

template <
      class Container
    , class DerivedPolicies
    , bool NoProxy = false
    , bool NoSlice = false
    , class Data = typename Container::value_type
    , class Index = typename Container::size_type
    , class Key = typename Container::value_type
>
class indexing_suite
    : unspecified
{
public:

    indexing_suite(); // デフォルトコンストラクタ
}

DerivedPolicies

派生クラスは indexing_suite が必要なフックを提供する。

data_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, data_type const& v);

static void
set_slice(
    Container& container, index_type from,
    index_type to, data_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
);

これらのポリシーの大部分は自己説明的であるが、convert_indexadjust_index は少し説明が必要である。

convert_index は Python の添字をコンテナが処理可能な C++ の添字に変換する。例えば Python における負の添字は右から数え始める(例:C[-1]C 内の最も右の要素を差す)。convert_index は C++ コンテナのために必要な変換を処理しなければならない(例:-1C.size() - 1 である)。convert_index はまた、添字の型(Python の動的型)を C++ コンテナが想定する実際の型に変換できなければならない。

コンテナが拡張か縮小すると、要素への添字はデータの移動に追従して調整しなければならない。例えば 5 要素から成るベクタの 0 番目(a)から 3 つの要素を削除すると、添字 4 は添字 1 となる。

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

adjust_index の仕事は調整である。添字 current を与えると、この関数はコンテナにおける添字 from..to におけるデータを len 個の要素で置換したときの調整後の添字を返す。

vector_indexing_suite クラス

template<class Container, bool NoProxy = false, class DerivedPolicies = unspecified_default>
class vector_indexing_suite : unspecified_base
テンプレートパラメータ
  • Container --

    Python に対してラップするコンテナ型。

    要件

    クラス型

  • NoProxy --

    既定では添字アクセスした要素は Python の参照のセマンティクスを持ち、プロキシにより返される。これは NoProxy テンプレート引数に真を与えることで無効化できる。

    要件

    論理値

    既定

    false

  • DerivedPolicies --

    vector_indexing_suite はさらに定義済みのポリシーに派生している可能性がある。CRTP(James Coplien の「奇妙に再帰したテンプレートパターン」、C++ レポート、1995 年 2 月)を介した静的な多態により基底クラス indexing_suite が最派生クラスのポリシー関数を呼び出せる。

    要件

    indexing_suite の派生クラス

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

    typedef typename Container::value_type data_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;

    data_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, data_type const& v);

    static void
    set_slice(Container& container, index_type from,
        index_type to, data_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);
};

map_indexing_suite クラス

template<class Container, bool NoProxy = false, class DerivedPolicies = unspecified_default>
class map_indexing_suite : unspecified_base
テンプレートパラメータ
  • Container --

    Python に対してラップするコンテナ型。

    要件

    クラス型

  • NoProxy --

    既定では添字アクセスした要素は Python の参照のセマンティクスを持ち、プロキシにより返される。これは NoProxy テンプレート引数に真を与えることで無効化できる。

    要件

    論理値

    既定

    false

  • DerivedPolicies --

    map_indexing_suite はさらに定義済みのポリシーに派生している可能性がある。CRTP(James Coplien の「奇妙に再帰したテンプレートパターン」、C++ レポート、1995 年 2 月)を介した静的な多態により基底クラス indexing_suite が最派生クラスのポリシー関数を呼び出せる。

    要件

    indexing_suite の派生クラス

template <
    class Container,
    bool NoProxy = false,
    class DerivedPolicies = unspecified_default >
class map_indexing_suite : unspecified_base
{
public:

    typedef typename Container::value_type value_type;
    typedef typename Container::value_type::second_type data_type;
    typedef typename Container::key_type key_type;
    typedef typename Container::key_type index_type;
    typedef typename Container::size_type size_type;
    typedef typename Container::difference_type difference_type;

    static data_type&
    get_item(Container& container, index_type i);

    static void
    set_item(Container& container, index_type i, data_type const& v);

    static void
    delete_item(Container& container, index_type i);

    static size_t
    size(Container& container);

    static bool
    contains(Container& container, key_type const& key);

    static bool
    compare_index(Container& container, index_type a, index_type b);

    static index_type
    convert_index(Container& container, PyObject* i);
};