2008-08-23

コンセプトを使った実用的なコードに挑戦

コンセプトの厳格さと面倒くささを確かめるために、実用的なコードをスクラッチから書いてみた。一時間ほどかかった。ConceptGCCのエラーメッセージは不親切すぎる。

auto concept ValueTypeConcept< typename T >
{
    T::T() ;
    T::T( T const & ) ;
    T::~T() ;
    
    T & operator +=( T &, T const & ) ;
}

auto concept IteratorConcept< typename T >
{    
    T::T() ;
    T::T( T const & ) ;
    T::~T() ;
    
    typename value_type = T::value_type ;
    
    T & operator ++ (T & ) ;
    value_type operator * ( T & ) ;
    bool operator == ( T const &, T const & ) ;
    bool operator != ( T const &, T const & ) ;
}



template < typename Iterator >
    requires IteratorConcept< Iterator >
       && ValueTypeConcept< Iterator::value_type >
typename Iterator::value_type
    accumlate( Iterator first, Iterator last )
{
    if ( first == last )
        return typename Iterator::value_type() ;
        
    typename Iterator::value_type ret( *first ) ;
    
    ++first ;
    while( first != last )
    {
            ret += *first ;
            ++first ;
    }
    
    return ret ;
}

なぜこの程度のコードを書くのに時間がかかったかと言うと、コンセプトの記述を、一箇所間違えてしまったからだ。

auto concept IteratorConcept< typename T >
{    
    typename value_type = T::value_type ;
    
    value_type operator ++ (T & ) ;
}

こんなミスぐらいすぐに発見できると思うかもしれないが、まさかこんな間違いをするはずが無いという思い込みから、まったく発見できなかった。また、ConceptGCCのエラーメッセージもひどい。STLのint型の要素のvectorのイテレータを渡したら、

test.cpp: In function 'int main()':
test.cpp:72: error: no matching function for call to 'accumlate(__gnu_cxx::__normal_iterator<int*,std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >)'

これだけしかエラーを出力しない。まず、最低限どのコンセプトを満たしていないかぐらい教えてくれよ。それから、あるコンセプトのどの要求を満たしていないのか、ということも教えて欲しい。もしConceptGCCが、

「あんさんあんさん、このコード間違ってまんがな。IteratorConceptっちゅーコンセプトのな、value_type operator ++ (T & ) ; ちゅー要求を、std::vector<int>::iteratorは満たしてへんねやで。もひとつ云うておくとやな、たしかにoperator ++はありまんねん。せやけど戻り値をvalue_typeに変換可能なものはおまへん。そこんとこ気ぃつけて、まひとつよろしく頼んます」

と言ってくれたならば、どんなに楽なことか。このエラーメッセージには、現在のConceptGCCにできないことが三つある。まず一つ目は、どのコンセプトにマッチしていないのか知らせることだ。ConceptGCCは、ちょっと複雑なコードになってくると、とたんにこれができなくなる。二つ目は、typedefされた型名を用いるということだ。libstdc++の内部的な実装の型名を使われても、わけが分からない。MSVCを見習って欲しいものだ。またその際には、テンプレートパラメータのデフォルト引数は省略してくれれば、なおよい。三つ目は、戻り値や引数の型が違う場合に、警告してくれることだ。

No comments: