boost/concept_check.hpp
boost/concept_archetype.hpp
C++ におけるジェネリック・プログラミングは、抽象データ型(あるいは「コンセプト」)を表現するためにテンプレート・パラメータを使用することを特徴としている。 しかし、C++ 言語には、クラス・テンプレートや関数テンプレートに対して、与えられたテンプレート引数がモデル化(もしくは順応)すべきコンセプトを明確に規定するためのメカニズムがない。 よく利用されているのは、必要とされるコンセプトのヒントとなるようにテンプレート・パラメータを命名し、付属文書においてコンセプトの要求事項を記述する方式である。 残念ながら、こういった要求事項の記述は、往々にして曖昧であったり不正確であったり、まったく存在しないこともある。 あるテンプレートが想定している引数の性質を、そのテンプレートのユーザーが正確に理解できなければ、それは由々しき問題となる。 さらに、以下の問題が起こりうる:
Boost コンセプト・チェック・ライブラリーは、次のような手段を提供する:
このメカニズムは C++ 標準の機能のみを使用し、実行時オーバーヘッドを課さない。 メカニズムを導入するコストは、コンパイル時間の増大のみである。
クラス・テンプレートや関数テンプレートを記述するプログラマは、全員が通常のコード作成作業の一環としてコンセプト・チェックを含めるべきである。 コンセプト・チェックは、コンポーネントへのインターフェイスとして公開されている、すべてのテンプレート・パラメータに対して導入すべきである。 利用したいコンセプトが標準ライブラリーで用いられているものであれば、BCCL の該当するコンセプト・チェック用クラスを、そのまま使用すればよい。 そうでなければ、新たにコンセプト・チェック用クラスを記述することになるが、普通は数行程度に収まるはずだ。 新しいコンセプトを用いるならば、対応する原型クラスも作成すべきである。原型クラスとはコンセプトを表す最小限のスケルトン実装である。
この文書を、以下の通り構成する。
Jeremy Siek はこのライブラリを寄稿した。 Beman Dawes が公式レビューを管理した。
コンセプト "concept" とは、ジェネリック・アルゴリズムに対する引数として与えられる型が、アルゴリズム内部で正しく使用されるために満足しなければならない要求事項(有効な式、関連型、セマンティクス不変、計算量保証など)の組み合わせである。 C++ では、コンセプトは関数テンプレート(ジェネリック・アルゴリズム)のテンプレート・パラメータとして表現される。 しかしながら、C++ にはコンセプトを表現するための明示的なメカニズムがない。テンプレート・パラメータはただのプレースホルダでしかないのだ。 慣例として、こういったパラメータには、必要とされるコンセプトに対応する名前を与える。しかし、テンプレート・パラメータを実際の型で確定するときに、C++ コンパイラはコンセプトへの遵守を強要しない。
当然ながら、ジェネリック・アルゴリズムを、そのコンセプトの内、構文に関わる要求事項を満足していない型で呼び出した場合、コンパイル・エラーが生じる。 しかし、このエラーは、該当する型がコンセプトの要求事項すべてに適合していない事実を本質的に 反映したものではない。 それどころか、エラーはインスタンス化階層の深部で発生し、該当する型に対して式が有効でないか、想定された関連型が利用可能でないといったことが原因として挙げられることになるだろう。 こうして生じたエラーメッセージは、大抵において情報に乏しく、基本的に不可解である。
必要とされるものは、インスタンス化位置(かその近傍)で「コンセプトセーフ」を強要するためのメカニズムである。 Boost コンセプト・チェック・ライブラリーは、早い段階でコンセプトの遵守を強要し、遵守していない場合のエラー・メッセージをより有用にするために C++ 標準の機能を使用する。
注意すべき点は、この技術がコンセプトの要求事項のうち構文に関わる部分(有効な式および関連型)のみを扱うことである。 我々は、コンセプトの要求事項の一部であるセマンティクス不変あるいは計算量保証を扱わない。
テンプレート・ライブラリの不正な使用法と、その結果生じるエラーメッセージを例証するために単純なサンプルを示す。
下記のコードでは、標準テンプレート・ライブラリー (STL) のジェネリックな std::stable_sort()
アルゴリズム [3、4、5] をリンクリストに適用している。
bad_error_eg.cpp: 1 #include <list> 2 #include <algorithm> 3 4 int main(int, char*[]) { 5 std::list<int> v; 6 std::stable_sort(v.begin(), v.end()); 7 return 0; 8 }
この場合、std::stable_sort()
アルゴリズムは以下のようなプロトタイプを有する:
template <class RandomAccessIterator> void stable_sort(RandomAccessIterator first, RandomAccessIterator last);
Gnu C++ でこのコードをコンパイルすると、以下のコンパイラ・エラーを生成する。 他のコンパイラ出力は Appendix にリストしてある(訳注1)。
stl_algo.h: In function `void __merge_sort_loop<_List_iterator <int,int &,int *>, int *, int>(_List_iterator<int,int &,int *>, _List_iterator<int,int &,int *>, int *, int)': stl_algo.h:1448: instantiated from `__merge_sort_with_buffer <_List_iterator<int,int &,int *>, int *, int>( _List_iterator<int,int &,int *>, _List_iterator<int,int &,int *>, int *, int *)' stl_algo.h:1485: instantiated from `__stable_sort_adaptive< _List_iterator<int,int &,int *>, int *, int>(_List_iterator <int,int &,int *>, _List_iterator<int,int &,int *>, int *, int)' stl_algo.h:1524: instantiated from here stl_algo.h:1377: no match for `_List_iterator<int,int &,int *> & - _List_iterator<int,int &,int *> &'
この場合、根本的なエラーの原因は、std:list::iterator
が RandomAccessIterator コンセプトをモデル化していないことにある。
リストのイテレータは双方向でしかなく、(ベクタのイテレータのように) 完全なランダム・アクセスが可能なわけではない。
残念ながら、このエラーメッセージには、ユーザーにこの事実を示すものは何もない。
C++ プログラマがテンプレート・ライブラリに十分な経験を持っていれば、この手のエラーに惑うことは無いかもしれない。 しかし未熟な者にとっては、次のような理由で、このメッセージが理解し難いものとなっている。
bad_error_eg.cpp
の6行目は、Gnu C++ がインスタンス化スタックを4レベルも深くまで探索して表示するという事実にもかかわらず、エラーメッセージで示されていない。std::stable_sort()
および RandomAccessIterator に関する文書化された要求事項との間に、文面上の相関がない。次の例示は、より有用なメッセージとして斯くあるべきと我々が考えるものだ (また実際に Boost コンセプト・チェック・ライブラリが生成するものでもある)。
boost/concept_check.hpp: In method `void LessThanComparableConcept <_List_iterator<int,int &,int *> >::constraints()': boost/concept_check.hpp:334: instantiated from `RandomAccessIteratorConcept <_List_iterator<int,int &,int *> >::constraints()' bad_error_eg.cpp:6: instantiated from `stable_sort<_List_iterator <int,int &,int *> >(_List_iterator<int,int &,int *>, _List_iterator<int,int &,int *>)' boost/concept_check.hpp:209: no match for `_List_iterator<int,int &,int *> & < _List_iterator<int,int &,int *> &'
このメッセージは、標準的なエラー・メッセージがもつ欠点をいくつかの点で改善する。
bad_error_eg.cpp:6
)がエラーメッセージに明示されている。concept_check.hpp
および constraints()
が示されることで、ライブラリ実装の中にではなくユーザーのコードにエラーがあるという事実をユーザーに警告している。このコンセプト・チェック・システムの初期バージョンは、著者が SGI において、C++ コンパイラおよびライブラリ・グループの一員として勤務している間に開発された。 初期バージョンは今も SGI STL ディストリビューションの一部である。 Boost コンセプト・チェック・ライブラリは、エラーメッセージにおけるそれほど有用でない表現能力を犠牲にして、コンセプト・チェック用クラス定義を非常に単純化しており、その点で SGI STLのコンセプト・チェックとは異なっている。
インスタンス化を引き起こすために関数ポインタを使用するアイディアは、Alexander Stepanov に拠る。 テンプレートの事前チェックに式を使用するアイディアに関して、その起源を確認できなかった。しかし、それは D&E[2] に記載されている。 STL コンセプトに関する優れた文書化と構造化を行った Matt Austern に感謝をささげる。このコンセプト・チェックは彼の仕事を基礎にしている。 有益なコメントとレビューを賜った Boost のメンバにも感謝を。
Copyright © 2000 | Jeremy Siek, Univ.of Notre Dame (jsiek@lsc.nd.edu) |